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> <b>Service Status</b>
<table> <table>
<tr> <tr>
@@ -60,29 +20,21 @@ csrf_verify();
<br /> <br />
<?php if (!empty($_POST)): <?php if (!empty($_POST)):
// Each $_POST read uses the `?? ''` null-coalesce so that: if (preg_match('/[^A-Z]/',$_POST["Link"])) { unset ($_POST["Link"]);}
// - a missing key (Link / RefName / Letter / Module never sent) and if ($_POST["Link"] == "LINK") {
// - a key unset by the preg_match sanitiser below (e.g. Link if (preg_match('/[^A-Z0-9]/',$_POST["RefName"])) { unset ($_POST["RefName"]);}
// contained non-uppercase chars and was unset) if (preg_match('/[^A-Z]/',$_POST["Letter"])) { unset ($_POST["Letter"]);}
// both resolve to '' instead of triggering PHP 8's "Undefined array if (preg_match('/[^A-Z0-9 ]/',$_POST["Module"])) { unset ($_POST["Module"]);}
// key" E_WARNING. Behaviour is unchanged: '' fails every "X" == "Y" }
// comparison the way an undefined key did under PHP 7's E_NOTICE. if ($_POST["Link"] == "UNLINK") {
// `(... ?? '')` is parenthesised because == binds tighter than ??. 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";} if (empty($_POST["RefName"]) || empty($_POST["Letter"]) || empty($_POST["Module"])) { echo "Somthing wrong with your input, try again";}
else { else {
$targetRef = $_POST["RefName"]." ".$_POST["Letter"]; $targetRef = $_POST["RefName"]." ".$_POST["Letter"];
$module = $_POST["Module"]; $module = $_POST["Module"];
if (strlen($module) != 8) { //Fix the length of the module information if (strlen($module) != 8) { //Fix the length of the module information
$moduleFixedCs= strlen($module) - 1; //Length of the string, -1 $moduleFixedCs= strlen($module) - 1; //Length of the string, -1
@@ -91,25 +43,22 @@ else {
$module = $moduleFixedCallPad.$moduleFixedBand; //Re add the band information $module = $moduleFixedCallPad.$moduleFixedBand; //Re add the band information
}; };
$unlinkCommand = "sudo remotecontrold \"".$module."\" unlink"; $unlinkCommand = "sudo remotecontrold \"".$module."\" unlink";
$linkCommand = "sudo remotecontrold \"".$module."\" link never \"".$targetRef."\""; $linkCommand = "sudo remotecontrold \"".$module."\" link never \"".$targetRef."\"";
if (($_POST["Link"] ?? '') == "LINK") { if ($_POST["Link"] == "LINK") {
echo "<b>Reflector Connector</b>\n"; 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 "<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 echo exec($linkCommand);
// display defence-in-depth (the input fields above were echo "</tr></td>\n</table>\n";
// already preg_match-whitelist-validated). }
echo htmlspecialchars((string)exec($linkCommand), ENT_QUOTES, 'UTF-8'); if ($_POST["Link"] == "UNLINK") {
echo "</tr></td>\n</table>\n"; 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>";
if (($_POST["Link"] ?? '') == "UNLINK") { echo exec($unlinkCommand);
echo "<b>Reflector Connector</b>\n"; echo "</tr></td>\n</table>\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";
}
}
unset($_POST); unset($_POST);
echo '<script type="text/javascript">setTimeout(function() { window.location=window.location;},2000);</script>'; 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: ?> else: ?>
<b>Reflector Connector</b> <b>Reflector Connector</b>
<form action="//<?php echo htmlentities($_SERVER['HTTP_HOST']).htmlentities($_SERVER['PHP_SELF']); ?>" method="post"> <form action="//<?php echo htmlentities($_SERVER['HTTP_HOST']).htmlentities($_SERVER['PHP_SELF']); ?>" method="post">
<?php csrf_field(); ?>
<table> <table>
<tr> <tr>
<th id="lblModule" width="150"><a class=tooltip href="#">Radio Module<span><b>Radio Module</b></span></th> <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"); $dextraFile = fopen("/usr/local/etc/DExtra_Hosts.txt", "r");
while (!feof($dcsFile)) { while (!feof($dcsFile)) {
$dcsLine = fgets($dcsFile); $dcsLine = fgets($dcsFile);
if (strpos($dcsLine, 'DCS') !== FALSE && strpos($dcsLine, '#') === FALSE) if (strpos($dcsLine, 'DCS') !== FALSE && strpos($dcsLine, '#') === FALSE)
echo " <option>".substr($dcsLine, 0, 6)."</option>\n"; echo " <option>".substr($dcsLine, 0, 6)."</option>\n";
} }
fclose($dcsFile); fclose($dcsFile);
echo " <option selected>REF001</option>\n"; echo " <option selected>REF001</option>\n";
while (!feof($dplusFile)) { while (!feof($dplusFile)) {
$dplusLine = fgets($dplusFile); $dplusLine = fgets($dplusFile);
if (strpos($dplusLine, 'REF') !== FALSE && strpos($dplusLine, '#') === FALSE && strpos($dplusLine, 'REF001') === FALSE) if (strpos($dplusLine, 'REF') !== FALSE && strpos($dplusLine, '#') === FALSE && strpos($dplusLine, 'REF001') === FALSE)
echo " <option>".substr($dplusLine, 0, 6)."</option>\n"; echo " <option>".substr($dplusLine, 0, 6)."</option>\n";
} }
fclose($dplusFile); fclose($dplusFile);
while (!feof($dextraFile)) { while (!feof($dextraFile)) {
$dextraLine = fgets($dextraFile); $dextraLine = fgets($dextraFile);
if (strpos($dextraLine, 'XRF') !== FALSE && strpos($dextraLine, '#') === FALSE) if (strpos($dextraLine, 'XRF') !== FALSE && strpos($dextraLine, '#') === FALSE)
echo " <option>".substr($dextraLine, 0, 6)."</option>\n"; echo " <option>".substr($dextraLine, 0, 6)."</option>\n";
} }
fclose($dextraFile); fclose($dextraFile);
?> ?>
</select> </select>
<select aria-label="Module" name="Letter"> <select aria-label="Module" name="Letter">
<option>A</option> <option>A</option>
<option>B</option> <option>B</option>
<option selected>C</option> <option selected>C</option>
<option>D</option> <option>D</option>
<option>E</option> <option>E</option>
<option>F</option> <option>F</option>
<option>G</option> <option>G</option>
<option>H</option> <option>H</option>
<option>I</option> <option>I</option>
<option>J</option> <option>J</option>
<option>K</option> <option>K</option>
<option>L</option> <option>L</option>
<option>M</option> <option>M</option>
<option>N</option> <option>N</option>
<option>O</option> <option>O</option>
<option>P</option> <option>P</option>
<option>Q</option> <option>Q</option>
<option>R</option> <option>R</option>
<option>S</option> <option>S</option>
<option>T</option> <option>T</option>
<option>U</option> <option>U</option>
<option>V</option> <option>V</option>
<option>W</option> <option>W</option>
<option>X</option> <option>X</option>
<option>Y</option> <option>Y</option>
<option>Z</option> <option>Z</option>
</select> </select>
</td> </td>
<td role="radiogroup" aria-labelledby="lblLinkUnlink"> <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> <input id="rbUnlink" type="radio" name="Link" value="UNLINK"><label for="rbUnlink">UnLink</label>
</td> </td>
<td> <td>
@@ -218,20 +166,21 @@ fclose($dextraFile);
<?php <?php
exec ("pgrep pistar-keeper", $pids); exec ("pgrep pistar-keeper", $pids);
if (!empty($pids)) if (!empty($pids))
{ {
echo "<br />\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 "<b>PiStar-Keeper Logbook</b><input type=button onClick=\"location.href='/admin/pistar-keeper-download.php'\" value=\"Download Logbook\">\n";
echo "<table>\n"; echo "<table>\n";
echo " <tr>\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 " <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 " </tr>\n";
exec ("tail -n 5 /var/pistar-keeper/pistar-keeper.log", $lines); exec ("tail -n 5 /var/pistar-keeper/pistar-keeper.log", $lines);
$counter = 0; $counter = 0;
foreach ($lines as $line) { foreach ($lines as $line) {
echo "<tr><td align=\"left\">".$lines[$counter]."</td></tr>\n"; echo "<tr><td align=\"left\">".$lines[$counter]."</td></tr>\n";
$counter++; $counter++;
} }
echo "</table>\n"; echo "</table>\n";
} }
?>
-266
View File
@@ -1,266 +0,0 @@
<?php
/**
* Forced password-change destination — Layer 2 of the default-password
* protection (see config/banner_warnings.inc for the redirect logic).
*
* Reached only when the operator is connecting from a remote subnet
* AND still using the factory default basic-auth password. Renders a
* single-purpose form: change the password, nothing else.
*
* The htpasswd / chpasswd update flow is a verbatim mirror of the
* password-change section in admin/configure.php (~line 440 onward).
* Keep the two in sync — any future fix to one belongs in the other.
*
* On success: redirect to /admin/. The Layer 2 trigger will no longer
* fire (password is no longer 'raspberry'), so the operator returns
* to normal dashboard navigation.
*
* Direct-URL access by an operator who is NOT using the default
* password just redirects to /admin/ — the page is only useful when
* the trigger condition holds.
*/
require_once($_SERVER['DOCUMENT_ROOT'] . '/config/security_headers.php');
require_once($_SERVER['DOCUMENT_ROOT'] . '/config/csrf.php');
require_once($_SERVER['DOCUMENT_ROOT'] . '/config/banner_warnings.inc');
setSecurityHeaders();
// CSRF: rejects forged POSTs cleanly with 403 before any chpasswd /
// htpasswd side-effect. Bootstraps the session on GET so the token
// cookie ships with the form render.
csrf_verify();
// If the operator's password is NOT the default, they don't need
// this page. Send them to the main admin dashboard.
//
// Done BEFORE any HTML output so the redirect is clean.
if (!_pistar_default_password_in_use()) {
header('Location: /admin/', true, 302);
exit;
}
require_once('../config/language.php');
$pistarReleaseConfig = '/etc/pistar-release';
$configPistarRelease = parse_ini_file($pistarReleaseConfig, true);
require_once('../config/version.php');
// Mirror of admin/configure.php password change (~line 440). Keep
// these two paths byte-equivalent so a fix to one always lands in
// the other. proc_open() + stdin pipes — password never on argv,
// never reaches a shell. chpasswd FIRST so PAM rejection blocks
// the htpasswd write and keeps web/shell auth in sync.
//
// Two-field verification: the form posts both `adminPassword` and
// `adminPasswordConfirm`. Client-side functions.js compares them on
// keystroke and only enables the submit button when they match;
// the server-side check below is defence in depth — never trust
// the client to enforce verification.
$passwordChanged = false;
$passwordRejected = false;
$rejectReason = '';
if (!empty($_POST['adminPassword'])) {
$rawPassword = stripslashes(trim($_POST['adminPassword']));
$rawPasswordConfirm = isset($_POST['adminPasswordConfirm'])
? stripslashes(trim($_POST['adminPasswordConfirm']))
: '';
$hasIllegalChar = preg_match('/[\x00\r\n]/', $rawPassword);
$tooLong = strlen($rawPassword) > 256;
$isStillDefault = hash_equals('raspberry', $rawPassword);
// hash_equals on user-supplied vs user-supplied isn't a security
// boundary (no secret on either side), but it's the right idiom
// for password compares — and guards against any future caller
// mistaking the result for an authentication signal.
$confirmMatches = hash_equals($rawPassword, $rawPasswordConfirm);
if ($rawPassword === '' || $hasIllegalChar || $tooLong) {
$passwordRejected = true;
$rejectReason = 'invalid';
error_log('Pi-Star change_password_required.php: adminPassword rejected '
. '(empty, contains NUL/CR/LF, or > 256 bytes)');
} elseif ($isStillDefault) {
$passwordRejected = true;
$rejectReason = 'default';
error_log('Pi-Star change_password_required.php: adminPassword rejected (still the default)');
} elseif (!$confirmMatches) {
$passwordRejected = true;
$rejectReason = 'mismatch';
error_log('Pi-Star change_password_required.php: adminPassword rejected '
. '(confirmation does not match)');
} else {
// Pi-Star runs with `/` mounted read-only by default. chpasswd
// and htpasswd both write to files on the rootfs (/etc/shadow
// and /var/www/.htpasswd respectively); without a remount-rw
// they fail with PAM "Authentication token manipulation error"
// and the symptom looks like every password is rejected. The
// matching configure.php POST handler does this at line ~348.
// Re-seal the rootfs at the bottom of this block on every
// exit path (success, PAM rejection, htpasswd failure).
system('sudo mount -o remount,rw /');
$descriptors = array(
0 => array('pipe', 'r'),
1 => array('pipe', 'w'),
2 => array('pipe', 'w'),
);
// 1. Linux user via chpasswd. Hardcoded command; payload on stdin only.
$cpExit = 1;
$cpStderr = '';
$proc = proc_open('sudo -n /usr/sbin/chpasswd', $descriptors, $pipes);
if (is_resource($proc)) {
fwrite($pipes[0], 'pi-star:' . $rawPassword . "\n");
fclose($pipes[0]);
stream_get_contents($pipes[1]); fclose($pipes[1]);
$cpStderr = stream_get_contents($pipes[2]); fclose($pipes[2]);
$cpExit = proc_close($proc);
} else {
error_log('Pi-Star change_password_required.php: failed to spawn chpasswd');
}
if ($cpExit !== 0) {
// PAM rejection (libpwquality, history, length, etc.).
$passwordRejected = true;
$rejectReason = 'pam';
error_log('Pi-Star change_password_required.php: chpasswd exit='
. $cpExit . '; stderr=' . trim($cpStderr));
} else {
// 2. Apache basic-auth file. /var/www/.htpasswd is owned by
// www-data, so no sudo needed.
$htExit = 1;
$htStderr = '';
$proc = proc_open('htpasswd -i /var/www/.htpasswd pi-star',
$descriptors, $pipes);
if (is_resource($proc)) {
fwrite($pipes[0], $rawPassword);
fclose($pipes[0]);
stream_get_contents($pipes[1]); fclose($pipes[1]);
$htStderr = stream_get_contents($pipes[2]); fclose($pipes[2]);
$htExit = proc_close($proc);
if ($htExit !== 0) {
error_log('Pi-Star change_password_required.php: htpasswd exit='
. $htExit . ' (Linux password updated); stderr='
. trim($htStderr));
}
} else {
error_log('Pi-Star change_password_required.php: failed to spawn htpasswd');
}
// Tighten /var/www/.htpasswd from htpasswd's default 644
// to 600. Owner stays www-data so nginx (running as
// www-data) keeps reading for basic-auth checks; other
// local users no longer see the bcrypt hash.
//
// Surface chmod failure into the error log. The realistic
// failure modes are (a) htpasswd spawn failed and the
// file is in some unexpected state, or (b) a future
// refactor moves this call outside the rootfs-rw window
// (it sits inside one today — see commit aa999d49).
// Either way, the file silently staying mode 644 would
// be worth knowing about.
if (@chmod('/var/www/.htpasswd', 0600) === false) {
error_log('Pi-Star change_password_required.php: chmod 0600 /var/www/.htpasswd failed (file may stay mode 644 until next save)');
}
// chpasswd succeeded; treat as success even if htpasswd
// failed (operator can retry — same behaviour as
// configure.php). The browser will re-prompt for the new
// credentials on the next request.
$passwordChanged = true;
}
// Re-seal the rootfs on every exit path out of the writable
// block. sync first so the shadow / htpasswd writes land on
// disk before the remount makes the FS read-only again.
system('sudo sync && sudo sync && sudo sync && sudo mount -o remount,ro /');
}
}
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="Author" content="Andrew Taylor (MW0MWZ)" />
<meta name="Description" content="Pi-Star — Default password change required" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon" />
<link rel="stylesheet" type="text/css" href="../css/pistar-css.php" />
<script type="text/javascript" src="/functions.js"></script>
<title>Pi-Star — Change Default Password</title>
</head>
<body>
<?php pistar_warnings_render(); ?>
<div class="container">
<div class="header">
<h1>Pi-Star — Default Password Change Required</h1>
</div>
<div class="contentwide">
<?php if ($passwordChanged) { ?>
<table>
<tr><th>Working...</th></tr>
<tr><td>Password changed. Your browser will re-prompt for the new credentials shortly.</td></tr>
</table>
<script type="text/javascript">setTimeout(function() { window.location='/admin/'; }, 5000);</script>
<?php } else { ?>
<p>You are connecting from outside the local network and your dashboard is still using the
factory default password. Set a new password to continue.</p>
<?php if ($passwordRejected) {
// Distinct message per rejection class so the operator knows
// exactly what to fix without having to guess.
switch ($rejectReason) {
case 'mismatch':
$msg = '<b>Passwords do not match.</b> Please enter the same value in both fields.';
break;
case 'default':
$msg = '<b>Password rejected.</b> The new password must be different from the factory default.';
break;
case 'pam':
$msg = '<b>Password rejected by the system.</b> It must satisfy the system password-quality rules (length, complexity, history).';
break;
case 'invalid':
default:
$msg = '<b>Password rejected.</b> It must not contain NUL / CR / LF bytes and must be at most 256 bytes long.';
break;
}
?>
<p style="color: #f01010;"><?php echo $msg; ?></p>
<?php } ?>
<form id="adminPassForm" action="/admin/change_password_required.php" method="post" autocomplete="off">
<?php echo csrf_field_html(); ?>
<table>
<tr>
<td align="right" width="40%"><label for="pass1">New password:</label></td>
<td align="left">
<input type="password" name="adminPassword" id="pass1"
size="32" autocomplete="new-password"
onkeyup="checkPass(); return false;" required="required" />
</td>
</tr>
<tr>
<td align="right"><label for="pass2">Confirm password:</label></td>
<td align="left">
<input type="password" name="adminPasswordConfirm" id="pass2"
size="32" autocomplete="new-password"
onkeyup="checkPass(); return false;" required="required" />
</td>
</tr>
<tr>
<td colspan="2" align="center">
<!-- id="submitpwd" matches the existing checkPass() in
functions.js which enables this button only when both
fields match. Server still re-verifies, so the JS gate
is UX, not security. -->
<input type="submit" id="submitpwd" value="Change Password" disabled="disabled" />
</td>
</tr>
</table>
</form>
<?php } ?>
</div>
<div class="footer">
Pi-Star / Pi-Star Dashboard, &copy; Andy Taylor (MW0MWZ) 2014-<?php echo date('Y'); ?>.<br />
</div>
</div>
</body>
</html>
-1
View File
@@ -1 +0,0 @@
../config/
+15
View File
@@ -0,0 +1,15 @@
<?php
if(empty($_SERVER['HTTP_USER_AGENT'])) {
print " <link rel=\"stylesheet\" type=\"text/css\" media=\"screen and (min-width: 830px)\" href=\"css/pistar-css.php?version=0.95\" />\n";
print " <link rel=\"stylesheet\" type=\"text/css\" media=\"screen and (max-width: 829px)\" href=\"css/pistar-css-mini.php?version=0.95\" />\n";
} else {
$useragent=$_SERVER['HTTP_USER_AGENT'];
if (preg_match('/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i',$useragent)||preg_match('/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i',substr($useragent,0,4))) {
print " <link rel=\"stylesheet\" type=\"text/css\" media=\"screen and (min-device-width: 380px)\" href=\"css/pistar-css.php?version=0.95\" />\n";
print " <link rel=\"stylesheet\" type=\"text/css\" media=\"screen and (max-device-width: 379px)\" href=\"css/pistar-css-mini.php?version=0.95\" />\n";
} else {
print " <link rel=\"stylesheet\" type=\"text/css\" media=\"screen and (min-width: 830px)\" href=\"css/pistar-css.php?version=0.95\" />\n";
print " <link rel=\"stylesheet\" type=\"text/css\" media=\"screen and (max-width: 829px)\" href=\"css/pistar-css-mini.php?version=0.95\" />\n";
}
}
?>
+27
View File
@@ -0,0 +1,27 @@
<?php
# This is an auto-generated config-file!
# Be careful, when manual editing this!
date_default_timezone_set('UTC');
define("MMDVMLOGPATH", "/var/log/pi-star");
define("MMDVMLOGPREFIX", "MMDVM");
define("MMDVMINIPATH", "/etc");
define("MMDVMINIFILENAME", "mmdvmhost");
define("MMDVMHOSTPATH", "/usr/local/bin");
define("DMRIDDATPATH", "/usr/local/etc");
define("YSFGATEWAYLOGPATH", "/var/log/pi-star");
define("YSFGATEWAYLOGPREFIX", "YSFGateway");
define("YSFGATEWAYINIPATH", "/etc");
define("YSFGATEWAYINIFILENAME", "ysfgateway");
define("P25GATEWAYLOGPATH", "/var/log/pi-star");
define("P25GATEWAYLOGPREFIX", "P25Gateway");
define("P25GATEWAYINIPATH", "/etc");
define("P25GATEWAYINIFILENAME", "p25gateway");
define("LINKLOGPATH", "/var/log/pi-star");
define("IRCDDBGATEWAY", "ircddbgatewayd");
define("REFRESHAFTER", "30");
define("TEMPERATUREHIGHLEVEL", "");
define("REBOOTMMDVM", "");
define("REBOOTSYS", "");
define("HALTSYS", "");
?>
View File
+24
View File
@@ -0,0 +1,24 @@
# ircddbgateway_languages.inc
#
# Written for Pi-Star Digital Voice by Andy Taylor (MW0MWZ)
# Orrigional request by Dennis Bonesky
#
# Updated 28-May-2017
#
###########################################################
#language;ircddbgateway;timeserver
###########################################################
English_(UK);0;0
Deutsch;1;4
Dansk;2;0
Francais;3;6
Italiano;4;0
Polski;5;0
English_(US);6;2
Espanol;7;9
Svenska;8;8
Nederlands_(NL);9;7
Nederlands_(BE);10;7
Norsk;11;10
Portugues;12;11
###########################################################
+15
View File
@@ -0,0 +1,15 @@
<?php
$logPath='/var/log/pi-star';
$callsign='M1ABC';
$registerURL = '';
$starLogPath = $logPath . '/STARnet.log';
$linkLogPath = $logPath . '/Links.log';
$hdrLogPath = $logPath . '/Headers.log';
$ddmode_log = $logPath . '/DDMode.log';
$configPath='/etc';
$gatewayConfigPath = '/etc/ircddbgateway';
$defaultConfPath = '/etc/default';
$sharedFilesPath = '/usr/local/etc';
$sysConfigPath = '/etc/sysconfig';
?>
+5
View File
@@ -0,0 +1,5 @@
<?php
// Set the language
$pistarLanguage='chinese_cn';
include_once $_SERVER['DOCUMENT_ROOT']."/lang/$pistarLanguage.php";
?>
+3
View File
@@ -0,0 +1,3 @@
<?php
$version = '20260705';
?>
+172 -522
View File
@@ -1,143 +1,4 @@
<?php <?php
/**
* Configuration backup & restore.
*
* Two POST actions:
* - download: zips the operator's config files into
* /tmp/config_backup.zip and sends it as a download. Files
* are listed once in {@see backup_files()} and shared with
* the restore handler so the two paths cannot drift.
* - restore: accepts a ZIP upload, validates it, extracts a
* known-safe subset into /tmp/config_restore/, stops every
* DV service, atomically installs each file at its mapped
* target, then restarts services.
*
* Security model (post-#16 remediation):
* - Upload is finfo_file()-typechecked, size-capped, then
* moved to a fixed temp filename — the operator-supplied
* name never reaches a path concat.
* - ZIP entries are filtered against backup_files() before
* extraction. Anything not on the allowlist (including any
* entry whose name contains `/`, `\`, or starts with `.`)
* is silently skipped — no zip-slip vector remains.
* - The blind `sudo mv -v -f /tmp/config_restore/* /etc/`
* pattern is replaced by per-file `sudo install -m … -o
* root -g root` driven by the same allowlist. No glob ever
* reaches the shell after extraction.
* - The two post-restore "re-apply from restored config"
* paths (timezone via shell-interpolated config.php grep,
* and remotePassword via shell-interpolated sed-i) are
* replaced by data-side propagation: timezone validated
* against DateTimeZone::listIdentifiers() and passed via
* escapeshellarg(), remotePassword routed through
* config_writer's privileged-flat helper (no shell sees
* the value).
*
* Backup contents (and therefore the restore allowlist) are
* defined ONCE in {@see backup_files()}. Files NOT in that list
* are deliberately NOT backed up — notably /etc/hostapd/hostapd.conf
* and /root/.Remote Control, which carry secrets that should
* never leave the device in a portable form.
*/
require_once($_SERVER['DOCUMENT_ROOT'] . '/config/security_headers.php');
require_once($_SERVER['DOCUMENT_ROOT'] . '/config/banner_warnings.inc');
setSecurityHeaders();
require_once($_SERVER['DOCUMENT_ROOT'].'/config/csrf.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/config_writer.php');
/**
* Canonical map of files in a Pi-Star backup ZIP.
*
* Single source of truth: the backup loop iterates this map to
* decide what to package, and the restore loop iterates this map
* to decide what to install (and where). ZIP entries with names
* outside the keys here are silently ignored on restore.
*
* key = basename inside the ZIP (the backup is a zip -j
* "junk paths" archive — every entry sits at the
* archive root with no directory component).
* value = absolute target path on the device.
*
* Files NOT in this map are deliberately excluded from backup:
* - /etc/hostapd/hostapd.conf (AP wpa_passphrase secret)
* - /root/.Remote Control (linker password secret)
* - /etc/sudoers* / /etc/passwd (system auth)
*
* @return array<string, array{0:string, 1:int, 2:string, 3:string}>
* basename => [path, mode, owner, group]
*/
function backup_files()
{
// Each entry is [path, mode, owner, group]. The mode/owner/group
// tuple is what the restore loop passes to `sudo install -m -o
// -g`, and MUST match the corresponding /etc/sudoers.d/pistar-
// dashboard PISTAR_DASH_INSTALL line for that destination — or
// sudo will reject the install and the file will silently fail
// to restore. Pre-2026-05-01 the restore loop used a hardcoded
// `install -m 644 -o root -g root` for every entry, which
// worked under the old NOPASSWD: ALL sudoers but breaks under
// the path-scoped allowlist for: the .key files (require 600
// www-data:www-data so the dashboard's per-page sudo-less
// parse_ini_file() reads still work — see fulledit_bmapikey.php
// for the full rationale), wpa_supplicant.conf (600 root:root),
// and the three system files (dhcpcd.conf, hosts, hostname)
// which have no install entry in the deployed sudoers at all
// and rely on a re-spun image to gain them.
return array(
// Network / WiFi
'dhcpcd.conf' => array('/etc/dhcpcd.conf', 644, 'root', 'root'),
'wpa_supplicant.conf' => array('/etc/wpa_supplicant/wpa_supplicant.conf', 600, 'root', 'root'),
// Gateway daemon configs (flat key=value INI-ish)
'ircddbgateway' => array('/etc/ircddbgateway', 644, 'root', 'root'),
'mmdvmhost' => array('/etc/mmdvmhost', 644, 'root', 'root'),
'dstarrepeater' => array('/etc/dstarrepeater', 644, 'root', 'root'),
'dapnetgateway' => array('/etc/dapnetgateway', 644, 'root', 'root'),
'p25gateway' => array('/etc/p25gateway', 644, 'root', 'root'),
'm17gateway' => array('/etc/m17gateway', 644, 'root', 'root'),
'ysfgateway' => array('/etc/ysfgateway', 644, 'root', 'root'),
'ysf2dmr' => array('/etc/ysf2dmr', 644, 'root', 'root'),
'dgidgateway' => array('/etc/dgidgateway', 644, 'root', 'root'),
'nxdngateway' => array('/etc/nxdngateway', 644, 'root', 'root'),
'dmrgateway' => array('/etc/dmrgateway', 644, 'root', 'root'),
'mobilegps' => array('/etc/mobilegps', 644, 'root', 'root'),
'starnetserver' => array('/etc/starnetserver', 644, 'root', 'root'),
'timeserver' => array('/etc/timeserver', 644, 'root', 'root'),
// Mode markers (operator has at most one of these)
'dstar-radio.mmdvmhost' => array('/etc/dstar-radio.mmdvmhost', 644, 'root', 'root'),
'dstar-radio.dstarrepeater' => array('/etc/dstar-radio.dstarrepeater', 644, 'root', 'root'),
// Pi-Star service / dashboard config
'pistar-remote' => array('/etc/pistar-remote', 644, 'root', 'root'),
'hosts' => array('/etc/hosts', 644, 'root', 'root'),
'hostname' => array('/etc/hostname', 644, 'root', 'root'),
// .key files: 600 www-data so unprivileged dashboard reads
// (banner_warnings.inc, bm_links.php, bm_manager.php — all
// parse_ini_file() without sudo) still work post-restore.
'bmapi.key' => array('/etc/bmapi.key', 600, 'www-data', 'www-data'),
'dapnetapi.key' => array('/etc/dapnetapi.key', 600, 'www-data', 'www-data'),
'pistar-css.ini' => array('/etc/pistar-css.ini', 644, 'root', 'root'),
'RSSI.dat' => array('/usr/local/etc/RSSI.dat', 644, 'root', 'root'),
'ircddblocal.php' => array('/var/www/dashboard/config/ircddblocal.php', 644, 'root', 'root'),
'config.php' => array('/var/www/dashboard/config/config.php', 644, 'root', 'root'),
);
}
// 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 rejects forged POSTs cleanly with 403
// before the download / restore handlers run.
//
// CSRF protection here makes C1's zip-slip / restore-pipeline RCE
// harder to exploit (attacker can no longer trigger restore via a
// cross-site click); the underlying bugs in the restore handler
// remain — they are tracked separately as the C1/C2 work-on-hold.
csrf_verify();
// Layer 2 of the default-password protection — see config/banner_warnings.inc.
// MUST run BEFORE any output so header('Location: ...') works.
pistar_warnings_enforce_redirect();
// Load the language support // Load the language support
require_once('config/language.php'); require_once('config/language.php');
// Load the Pi-Star Release file // Load the Pi-Star Release file
@@ -150,10 +11,7 @@ require_once('config/version.php');
if ($_SERVER["PHP_SELF"] == "/admin/config_backup.php") { if ($_SERVER["PHP_SELF"] == "/admin/config_backup.php") {
// Sanity Check Passed. // Sanity Check Passed.
header('Cache-Control: no-cache'); header('Cache-Control: no-cache');
// session_start() is no longer called here — csrf_verify() at session_start();
// the top of the file already started the session via
// csrf_session_start(). A second session_start() would emit a
// "session is already active" NOTICE on PHP 8.x.
?> ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
@@ -164,79 +22,75 @@ if ($_SERVER["PHP_SELF"] == "/admin/config_backup.php") {
<meta name="language" content="English" /> <meta name="language" content="English" />
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<meta name="Author" content="Andrew Taylor (MW0MWZ)" /> <meta name="Author" content="Andrew Taylor (MW0MWZ)" />
<meta name="Description" content="Pi-Star Power" /> <meta name="Description" content="CDN Power" />
<meta name="KeyWords" content="Pi-Star" /> <meta name="KeyWords" content="CDN" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="pragma" content="no-cache" /> <meta http-equiv="pragma" content="no-cache" />
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon" /> <link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon" />
<meta http-equiv="Expires" content="0" /> <meta http-equiv="Expires" content="0" />
<title>Pi-Star - <?php echo $lang['digital_voice']." ".$lang['dashboard']." - ".$lang['backup_restore'];?></title> <title>CDN - <?php echo $lang['digital_voice']." ".$lang['dashboard']." - ".$lang['backup_restore'];?></title>
<link rel="stylesheet" type="text/css" href="css/pistar-css.php" /> <link rel="stylesheet" type="text/css" href="css/pistar-css.php" />
</head> </head>
<body> <body>
<?php pistar_warnings_render(); ?>
<div class="container"> <div class="container">
<div class="header"> <div class="header">
<div style="font-size: 8px; text-align: right; padding-right: 8px;">Pi-Star:<?php echo $configPistarRelease['Pi-Star']['Version']?> / <?php echo $lang['dashboard'].": ".$version; ?></div> <div style="font-size: 8px; text-align: right; padding-right: 8px;">CDN:<?php echo $configPistarRelease['Pi-Star']['Version']?> / <?php echo $lang['dashboard'].": ".$version; ?></div>
<h1>Pi-Star <?php echo $lang['digital_voice']." - ".$lang['backup_restore'];?></h1> <h1>CDN <?php echo $lang['digital_voice']." - ".$lang['backup_restore'];?></h1>
<p style="padding-right: 5px; text-align: right; color: #ffffff;"> <p style="padding-right: 5px; text-align: right; color: #ffffff;">
<a href="/" style="color: #ffffff;"><?php echo $lang['dashboard'];?></a> | <a href="/" style="color: #ffffff;"><?php echo $lang['dashboard'];?></a> |
<a href="/admin/" style="color: #ffffff;"><?php echo $lang['admin'];?></a> | <a href="/admin/" style="color: #ffffff;"><?php echo $lang['admin'];?></a> |
<a href="/admin/power.php" style="color: #ffffff;"><?php echo $lang['power'];?></a> | <a href="/admin/power.php" style="color: #ffffff;"><?php echo $lang['power'];?></a> |
<a href="/admin/update.php" style="color: #ffffff;"><?php echo $lang['update'];?></a> |
<a href="/admin/configure.php" style="color: #ffffff;"><?php echo $lang['configuration'];?></a> <a href="/admin/configure.php" style="color: #ffffff;"><?php echo $lang['configuration'];?></a>
</p> </p>
</div> </div>
<div class="contentwide"> <div class="contentwide">
<h2><?php echo $lang['backup_restore'];?></h2>
<?php if (!empty($_POST)) { <?php if (!empty($_POST)) {
echo '<table width="100%">'."\n"; echo '<div class="settings-card">'."\n";
if ( $_POST["action"] === "download" ) { if ( escapeshellcmd($_POST["action"]) == "download" ) {
echo "<tr><th colspan=\"2\">".$lang['backup_restore']."</th></tr>\n";
$output = "Finding config files to be backed up\n"; $output = "Finding config files to be backed up\n";
$backupDir = "/tmp/config_backup"; $backupDir = "/tmp/config_backup";
$backupZip = "/tmp/config_backup.zip"; $backupZip = "/tmp/config_backup.zip";
$hostNameInfo = exec('cat /etc/hostname'); $hostNameInfo = exec('cat /etc/hostname');
$output .= shell_exec("sudo rm -rf " . escapeshellarg($backupZip) . " 2>&1");
$output .= shell_exec("sudo rm -rf " . escapeshellarg($backupDir) . " 2>&1");
$output .= shell_exec("sudo mkdir " . escapeshellarg($backupDir) . " 2>&1");
// Iterate the canonical backup map so the backup and the
// restore allowlist cannot drift from each other. dhcpcd.conf
// is special-cased (only included when the operator has a
// static IP configured) — everything else is unconditional;
// missing source files are silently skipped (the cp returns
// an error to stderr that nobody reads, same as the legacy
// behaviour).
foreach (backup_files() as $basename => $meta) {
// $meta is [path, mode, owner, group]. The backup half
// only needs the path — mode/owner/group are consumed
// on the restore-half install command.
$srcpath = $meta[0];
if ($basename === 'dhcpcd.conf') {
// Only back up dhcpcd.conf if the operator has set a
// static IP — otherwise restoring would clobber
// working DHCP config on the target device.
if (!shell_exec('cat /etc/dhcpcd.conf | grep "static ip_address" | grep -v "#"')) {
continue;
}
}
if (file_exists($srcpath)) {
$output .= shell_exec(
"sudo cp " . escapeshellarg($srcpath) . " "
. escapeshellarg($backupDir . '/' . $basename) . " 2>&1"
);
}
}
$output .= shell_exec("sudo rm -rf $backupZip 2>&1");
$output .= shell_exec("sudo rm -rf $backupDir 2>&1");
$output .= shell_exec("sudo mkdir $backupDir 2>&1");
if (shell_exec('cat /etc/dhcpcd.conf | grep "static ip_address" | grep -v "#"')) {
$output .= shell_exec("sudo cp /etc/dhcpcd.conf $backupDir 2>&1");
}
$output .= shell_exec("sudo cp /etc/wpa_supplicant/wpa_supplicant.conf $backupDir 2>&1");
$output .= shell_exec("sudo cp /etc/ircddbgateway $backupDir 2>&1");
$output .= shell_exec("sudo cp /etc/mmdvmhost $backupDir 2>&1");
$output .= shell_exec("sudo cp /etc/dstarrepeater $backupDir 2>&1");
$output .= shell_exec("sudo cp /etc/dapnetgateway $backupDir 2>&1");
$output .= shell_exec("sudo cp /etc/p25gateway $backupDir 2>&1");
$output .= shell_exec("sudo cp /etc/m17gateway $backupDir 2>&1");
$output .= shell_exec("sudo cp /etc/ysfgateway $backupDir 2>&1");
$output .= shell_exec("sudo cp /etc/ysf2dmr $backupDir 2>&1");
$output .= shell_exec("sudo cp /etc/dgidgateway $backupDir 2>&1");
$output .= shell_exec("sudo cp /etc/nxdngateway $backupDir 2>&1");
$output .= shell_exec("sudo cp /etc/dmrgateway $backupDir 2>&1");
$output .= shell_exec("sudo cp /etc/mobilegps $backupDir 2>&1");
$output .= shell_exec("sudo cp /etc/starnetserver $backupDir 2>&1");
$output .= shell_exec("sudo cp /etc/timeserver $backupDir 2>&1");
$output .= shell_exec("sudo cp /etc/dstar-radio.* $backupDir 2>&1");
$output .= shell_exec("sudo cp /etc/pistar-remote $backupDir 2>&1");
$output .= shell_exec("sudo cp /etc/hosts $backupDir 2>&1");
$output .= shell_exec("sudo cp /etc/hostname $backupDir 2>&1");
$output .= shell_exec("sudo cp /etc/bmapi.key $backupDir 2>&1");
$output .= shell_exec("sudo cp /etc/dapnetapi.key $backupDir 2>&1");
$output .= shell_exec("sudo cp /etc/pistar-css.ini $backupDir 2>&1");
$output .= shell_exec("sudo cp /usr/local/etc/RSSI.dat $backupDir 2>&1");
$output .= shell_exec("sudo cp /var/www/dashboard/config/ircddblocal.php $backupDir 2>&1");
$output .= shell_exec("sudo cp /var/www/dashboard/config/config.php $backupDir 2>&1");
$output .= "Compressing backup files\n"; $output .= "Compressing backup files\n";
$output .= shell_exec("sudo zip -j " . escapeshellarg($backupZip) . " " . escapeshellarg($backupDir) . "/* 2>&1"); $output .= shell_exec("sudo zip -j $backupZip $backupDir/* 2>&1");
$output .= "Starting download\n"; $output .= "Starting download\n";
echo "<tr><td align=\"left\"><pre>" echo "<pre>$output</pre>\n";
. htmlspecialchars($output, ENT_QUOTES, 'UTF-8') . "</pre></td></tr>\n";
if (file_exists($backupZip)) { if (file_exists($backupZip)) {
$utc_time = gmdate('Y-m-d H:i:s'); $utc_time = gmdate('Y-m-d H:i:s');
@@ -247,12 +101,12 @@ if ($_SERVER["PHP_SELF"] == "/admin/config_backup.php") {
$local_time = $dt->format('Y-M-d'); $local_time = $dt->format('Y-M-d');
header('Content-Description: File Transfer'); header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream'); header('Content-Type: application/octet-stream');
if ($hostNameInfo != "pi-star") { if ($hostNameInfo != "cdn") {
header('Content-Disposition: attachment; filename="'.basename("Pi-Star_Config_".$hostNameInfo."_".$local_time.".zip").'"'); header('Content-Disposition: attachment; filename="'.basename("CDN_Config_".$hostNameInfo."_".$local_time.".zip").'"');
} }
else { else {
header('Content-Disposition: attachment; filename="'.basename("Pi-Star_Config_$local_time.zip").'"'); header('Content-Disposition: attachment; filename="'.basename("CDN_Config_$local_time.zip").'"');
} }
header('Content-Transfer-Encoding: binary'); header('Content-Transfer-Encoding: binary');
header('Expires: 0'); header('Expires: 0');
header('Cache-Control: must-revalidate'); header('Cache-Control: must-revalidate');
@@ -265,353 +119,149 @@ if ($_SERVER["PHP_SELF"] == "/admin/config_backup.php") {
} }
}; };
if ( $_POST["action"] === "restore" ) { if ( escapeshellcmd($_POST["action"]) == "restore" ) {
echo "<tr><th colspan=\"2\">Config Restore</th></tr>\n";
$output = "Uploading your Config data\n"; $output = "Uploading your Config data\n";
// Wrap the whole restore in a do/while(false) so an early $target_dir = "/tmp/config_restore/";
// bail-out (upload validation failure, ZIP parse error, shell_exec("sudo rm -rf $target_dir 2>&1");
// unsafe entry name, etc.) can break cleanly out of the shell_exec("mkdir $target_dir 2>&1");
// sequence without an `if/else` pyramid. PHP 7.0+ idiom. if($_FILES["fileToUpload"]["name"]) {
do { $filename = $_FILES["fileToUpload"]["name"];
$source = $_FILES["fileToUpload"]["tmp_name"];
$type = $_FILES["fileToUpload"]["type"];
// Hardened restore pipeline. See the file-level docblock for $name = explode(".", $filename);
// the security model. Constants here: $accepted_types = array('application/zip', 'application/x-zip-compressed', 'multipart/x-zip', 'application/x-compressed');
// - $target_dir is hardcoded; the operator-supplied filename foreach($accepted_types as $mime_type) {
// never touches a path concat. if($mime_type == $type) {
// - $upload_path is also hardcoded; we move the uploaded $okay = true;
// temp file to a known name before any further processing. break;
// - $max_zip_bytes caps the upload; a real Pi-Star backup }
// is ~25 KB, nginx already caps the body at 512 KB, so }
// 256 KB is generous. }
$target_dir = '/tmp/config_restore'; $continue = strtolower($name[1]) == 'zip' ? true : false;
$upload_path = '/tmp/config_restore_upload.zip'; if(!$continue) {
$max_zip_bytes = 256 * 1024; $output .= "The file you are trying to upload is not a .zip file. Please try again.\n";
$allowlist = backup_files(); }
$target_path = $target_dir.$filename;
shell_exec("sudo rm -rf " . escapeshellarg($target_dir) . " 2>&1"); if(move_uploaded_file($source, $target_path)) {
shell_exec("rm -f " . escapeshellarg($upload_path) . " 2>&1"); $zip = new ZipArchive();
shell_exec("mkdir -p " . escapeshellarg($target_dir) . " 2>&1"); $x = $zip->open($target_path);
if ($x === true) {
$zip->extractTo($target_dir); // change this to the correct site path
$zip->close();
unlink($target_path);
}
$output .= "Your .zip file was uploaded and unpacked.\n";
$output .= "Stopping Services.\n";
// ----- Upload validation ------------------------------------- // Stop the DV Services
$upload_ok = false; shell_exec('sudo systemctl stop cron.service 2>&1'); //Cron
$err_msg = ''; shell_exec('sudo systemctl stop dstarrepeater.service 2>&1'); //D-Star Radio Service
if (!isset($_FILES['fileToUpload']) || shell_exec('sudo systemctl stop mmdvmhost.service 2>&1'); //MMDVMHost Radio Service
$_FILES['fileToUpload']['error'] !== UPLOAD_ERR_OK) { shell_exec('sudo systemctl stop ircddbgateway.service 2>&1'); //ircDDBGateway Service
$err_msg = 'No file uploaded, or upload failed.'; shell_exec('sudo systemctl stop timeserver.service 2>&1'); //Time Server Service
} elseif ($_FILES['fileToUpload']['size'] <= 0 || shell_exec('sudo systemctl stop pistar-watchdog.service 2>&1'); //PiStar-Watchdog Service
$_FILES['fileToUpload']['size'] > $max_zip_bytes) { shell_exec('sudo systemctl stop pistar-remote.service 2>&1'); //PiStar-Remote Service
$err_msg = 'Upload size out of range (expected up to ' shell_exec('sudo systemctl stop ysfgateway.service 2>&1'); //YSFGateway
. round($max_zip_bytes / 1024) . ' KB).'; shell_exec('sudo systemctl stop ysf2dmr.service 2>&1'); //YSF2DMR
} else { shell_exec('sudo systemctl stop p25gateway.service 2>&1'); //P25Gateway
$tmp_name = $_FILES['fileToUpload']['tmp_name']; shell_exec('sudo systemctl stop nxdngateway.service 2>&1'); //NXDNGateway
// finfo_file() reads the magic bytes — much harder for an shell_exec('sudo systemctl stop m17gateway.service 2>&1'); //M17Gateway
// attacker to fake than the client-supplied $_FILES['type']. shell_exec('sudo systemctl stop dapnetgateway.service 2>&1'); //DAPNETGateway
$finfo = finfo_open(FILEINFO_MIME_TYPE); shell_exec('sudo systemctl stop mobilegps.service 2>&1'); //MobileGPS
$magic = $finfo ? finfo_file($finfo, $tmp_name) : '';
if ($finfo) finfo_close($finfo);
if ($magic !== 'application/zip') {
$err_msg = 'Uploaded file is not a ZIP archive (detected '
. htmlspecialchars($magic, ENT_QUOTES, 'UTF-8') . ').';
} elseif (!move_uploaded_file($tmp_name, $upload_path)) {
$err_msg = 'Could not stage upload to ' . $upload_path . '.';
} else {
$upload_ok = true;
}
}
if (!$upload_ok) { // Make the disk Writable
$output .= $err_msg . "\n"; shell_exec('sudo mount -o remount,rw / 2>&1');
echo "<tr><td align=\"left\"><pre>"
. htmlspecialchars($output, ENT_QUOTES, 'UTF-8') . "</pre></td></tr>\n";
// Don't emit </table> here — the outer code does it once
// after the if-action blocks finish. The "Go Back" button
// is rendered AFTER the table closes (see end of file).
break;
}
// ----- ZIP entry validation --------------------------------- // Overwrite the configs
// Open the archive and decide which entries to extract BEFORE $output .= "Writing new Config\n";
// any extraction happens. An entry is admitted iff: $output .= shell_exec("sudo rm -f /etc/dstar-radio.* 2>&1")."\n";
// - its name appears verbatim as a key in $allowlist $output .= shell_exec("sudo mv -f /tmp/config_restore/RSSI.dat /usr/local/etc/ 2>&1")."\n";
// (basename → target map), AND $output .= shell_exec("sudo mv -f /tmp/config_restore/ircddblocal.php /var/www/dashboard/config/ 2>&1")."\n";
// - its name contains no `/`, no `\`, and does not start $output .= shell_exec("sudo mv -f /tmp/config_restore/config.php /var/www/dashboard/config/ 2>&1")."\n";
// with `.` (defence in depth — the backup writer uses $output .= shell_exec("sudo mv -v -f /tmp/config_restore/wpa_supplicant.conf /etc/wpa_supplicant/ 2>&1")."\n";
// `zip -j` so directory components are never legitimate). $output .= shell_exec("sudo mv -v -f /tmp/config_restore/* /etc/ 2>&1")."\n";
// Entries outside the allowlist are silently skipped — that's
// the documented behaviour for forward-compat with future
// Pi-Star versions adding new files.
$zip = new ZipArchive();
if ($zip->open($upload_path) !== true) {
$output .= "Could not open the uploaded ZIP.\n";
@unlink($upload_path);
echo "<tr><td align=\"left\"><pre>"
. htmlspecialchars($output, ENT_QUOTES, 'UTF-8') . "</pre></td></tr>\n";
break;
}
$admit = array(); //Restore the Timezone Config
for ($i = 0; $i < $zip->numFiles; $i++) { $timeZone = shell_exec('grep date /var/www/dashboard/config/config.php | grep -o "\'.*\'" | sed "s/\'//g"');
$name = $zip->getNameIndex($i); $timeZone = preg_replace( "/\r|\n/", "", $timeZone); //Remove the linebreaks
if ($name === false || $name === '') { shell_exec('sudo timedatectl set-timezone '.$timeZone.' 2>&1');
continue;
}
if (strpos($name, '/') !== false ||
strpos($name, '\\') !== false ||
strpos($name, "\0") !== false ||
$name[0] === '.') {
// Path-traversal / hidden / NUL-injection attempt.
// Reject the whole archive — this is well outside any
// Pi-Star-generated backup shape. break 2 exits the
// for() AND the surrounding do/while(false) early-
// exit block in one hop.
$zip->close();
@unlink($upload_path);
$output .= "Refusing ZIP — entry '"
. htmlspecialchars(substr($name, 0, 64), ENT_QUOTES, 'UTF-8')
. "' has an unsafe name.\n";
error_log("config_backup: rejected ZIP with unsafe entry '$name'");
echo "<tr><td align=\"left\"><pre>"
. htmlspecialchars($output, ENT_QUOTES, 'UTF-8') . "</pre></td></tr>\n";
break 2;
}
if (isset($allowlist[$name])) {
$admit[$name] = $i;
}
// else: silently ignored — unknown filename.
}
// Extract only the admitted entries into $target_dir. We use //Restore ircDDGBateway Link Manager Password
// ZipArchive::extractTo() with an explicit entry list so $ircRemotePassword = shell_exec('grep remotePassword /etc/ircddbgateway | awk -F\'=\' \'{print $2}\'');
// ANYTHING outside that list is left in the archive and shell_exec('sudo sed -i "/password=/c\\password='.$ircRemotePassword.'" /root/.Remote\ Control');
// never written to disk.
$entries_to_extract = array_keys($admit);
if (empty($entries_to_extract)) {
$zip->close();
@unlink($upload_path);
$output .= "ZIP contained no recognised Pi-Star config files.\n";
echo "<tr><td align=\"left\"><pre>"
. htmlspecialchars($output, ENT_QUOTES, 'UTF-8') . "</pre></td></tr>\n";
break;
}
if (!$zip->extractTo($target_dir, $entries_to_extract)) {
$zip->close();
@unlink($upload_path);
$output .= "Failed to extract ZIP entries.\n";
echo "<tr><td align=\"left\"><pre>"
. htmlspecialchars($output, ENT_QUOTES, 'UTF-8') . "</pre></td></tr>\n";
break;
}
$zip->close();
@unlink($upload_path);
$output .= "Your .zip file was uploaded and unpacked ("
. count($entries_to_extract) . " files).\n";
// ----- Service stop ---------------------------------------- // Make the disk Read-Only
$output .= "Stopping Services.\n"; shell_exec('sudo mount -o remount,ro / 2>&1');
$stop_services = array(
'cron.service', 'dstarrepeater.service', 'mmdvmhost.service',
'ircddbgateway.service', 'timeserver.service',
'pistar-watchdog.service', 'pistar-remote.service',
'ysfgateway.service', 'ysf2dmr.service', 'p25gateway.service',
'nxdngateway.service', 'm17gateway.service',
'dapnetgateway.service', 'mobilegps.service',
);
foreach ($stop_services as $svc) {
shell_exec('sudo systemctl stop ' . escapeshellarg($svc) . ' 2>&1');
}
shell_exec('sudo mount -o remount,rw / 2>&1'); // Start the services
$output .= "Starting Services.\n";
shell_exec('sudo systemctl start dstarrepeater.service 2>&1'); //D-Star Radio Service
shell_exec('sudo systemctl start mmdvmhost.service 2>&1'); //MMDVMHost Radio Service
shell_exec('sudo systemctl start ircddbgateway.service 2>&1'); //ircDDBGateway Service
shell_exec('sudo systemctl start timeserver.service 2>&1'); //Time Server Service
shell_exec('sudo systemctl start pistar-watchdog.service 2>&1'); //PiStar-Watchdog Service
shell_exec('sudo systemctl start pistar-remote.service 2>&1'); //PiStar-Remote Service
if (substr(exec('grep "pistar-upnp.service" /etc/crontab | cut -c 1'), 0, 1) !== '#') {
shell_exec('sudo systemctl start pistar-upnp.service 2>&1'); //PiStar-UPnP Service
}
shell_exec('sudo systemctl start ysfgateway.service 2>&1'); //YSFGateway
shell_exec('sudo systemctl start ysf2dmr.service 2>&1'); //YSF2DMR
shell_exec('sudo systemctl start p25gateway.service 2>&1'); //P25Gateway
shell_exec('sudo systemctl start nxdngateway.service 2>&1'); //NXDNGateway
shell_exec('sudo systemctl start m17gateway.service 2>&1'); //M17Gateway
shell_exec('sudo systemctl start dapnetgateway.service 2>&1'); //DAPNETGateway
shell_exec('sudo systemctl start mobilegps.service 2>&1'); //MobileGPS
shell_exec('sudo systemctl start cron.service 2>&1'); //Cron
// ----- Per-file install ------------------------------------ // Complete
// Replace the previous blind `sudo mv -v -f /tmp/.../* /etc/` $output .= "Configuration Restore Complete.\n";
// with a per-file `sudo install -m 644 -o root -g root` driven }
// by the canonical map. install is atomic on same-fs (rename) else {
// and falls back to safe copy across filesystems. The mode and $output .= "There was a problem with the upload. Please try again.<br />";
// owner are forced regardless of how the file arrived in the $output .= "\n".'<button onclick="goBack()">Go Back</button><br />'."\n";
// archive. $output .= '<script>'."\n";
$output .= "Writing new Config\n"; $output .= 'function goBack() {'."\n";
$output .= ' window.history.back();'."\n";
$output .= '}'."\n";
$output .= '</script>'."\n";
}
echo "<pre>$output</pre>\n";
};
// Tear down stale dstar-radio.* markers first — only one of echo "</div>\n";
// the two can be in use, and the restored archive may carry
// a different mode than the device currently has. Use
// `rm -rf` (not `rm -f`) so the call matches the
// PISTAR_DASH_MARKERS sudoers entry — different argv tokens
// yield different sudoers matches, and only `-rf` is
// allowlisted.
shell_exec('sudo rm -rf /etc/dstar-radio.* 2>&1');
$installed = 0;
foreach ($admit as $basename => $_idx) {
$src = $target_dir . '/' . $basename;
$meta = $allowlist[$basename];
$target = $meta[0];
$mode = (string)$meta[1];
$owner = $meta[2];
$group = $meta[3];
if (!file_exists($src)) {
// Should be impossible after a successful extractTo,
// but guard anyway — extraction can fail per-entry on
// unusual archives without throwing.
continue;
}
// Per-file install args from backup_files() metadata —
// mode/owner/group must match the deployed sudoers
// PISTAR_DASH_INSTALL line for $target or sudo will
// reject. The hardcoded `install -m 644 -o root -g root`
// here used to silently fail on .key files (require
// 600 www-data:www-data) and wpa_supplicant.conf
// (600 root:root) under the path-scoped sudoers.
$cmd = 'sudo install -m ' . $mode
. ' -o ' . escapeshellarg($owner)
. ' -g ' . escapeshellarg($group) . ' '
. escapeshellarg($src) . ' '
. escapeshellarg($target);
$rc = 0; $cmd_out = array();
exec($cmd . ' 2>&1', $cmd_out, $rc);
if ($rc === 0) {
$installed++;
} else {
$output .= " install failed for $basename: "
. implode(' / ', $cmd_out) . "\n";
}
}
$output .= "Installed $installed file(s).\n";
// ----- Post-restore re-applies (data-side, not shell) -----
// Timezone: the restored config.php contains the operator's
// timezone in a date_default_timezone_set('…') call. We want
// the OS clock to match the dashboard's view. PRE-fix this
// path was a shell pipeline interpolating the grepped value;
// post-fix we extract via PHP-side parsing and validate
// strictly against PHP's own list of valid timezone IDs
// before passing through escapeshellarg().
$cfg_path = '/var/www/dashboard/config/config.php';
if (is_readable($cfg_path)) {
$cfg = file_get_contents($cfg_path);
if (preg_match("/date_default_timezone_set\\(\\s*['\"]([^'\"]+)['\"]\\s*\\)/",
$cfg, $m)) {
$tz = $m[1];
if (in_array($tz, DateTimeZone::listIdentifiers(), true)) {
shell_exec('sudo timedatectl set-timezone '
. escapeshellarg($tz) . ' 2>&1');
} else {
error_log("config_backup: skipping unknown timezone '$tz' from restored config.php");
}
}
}
// ircDDBGateway Remote Control password: the restored
// /etc/ircddbgateway carries `remotePassword=...`; the
// /root/.Remote Control sibling file (mode 600 root:root,
// not in the backup set) needs the same value or the
// remotecontrold tool can't reach the daemon.
//
// Pre-fix this was a shell-interpolated `sudo sed -i ...`.
// Post-fix: read via PHP-side fopen/fgets (the file is mode
// 644 root:root after we just installed it, so www-data can
// read it back), validate, then route through the helper's
// privileged-flat editor — same primitive the C6.7 fix uses
// and also what the configure.php confPassword handler now
// uses. No shell ever sees the value.
$rp_target = '/etc/ircddbgateway';
if (is_readable($rp_target)) {
$rp = '';
foreach (file($rp_target, FILE_IGNORE_NEW_LINES) as $line) {
if (strpos($line, 'remotePassword=') === 0) {
$rp = substr($line, strlen('remotePassword='));
break;
}
}
// ircDDBGateway accepts arbitrary printable bytes for the
// password; the helper's NUL/CR/LF guard is the floor.
if ($rp !== '' && !preg_match('/[\x00\r\n]/', $rp)) {
config_writer_stage_privileged_flat(
'/root/.Remote Control', 'password', $rp
);
config_writer_commit(false);
}
}
shell_exec('sudo mount -o remount,ro / 2>&1');
// ----- Service start ---------------------------------------
$output .= "Starting Services.\n";
$start_services = array(
'dstarrepeater.service', 'mmdvmhost.service',
'ircddbgateway.service', 'timeserver.service',
'pistar-watchdog.service', 'pistar-remote.service',
);
foreach ($start_services as $svc) {
shell_exec('sudo systemctl start ' . escapeshellarg($svc) . ' 2>&1');
}
if (substr(exec('grep "pistar-upnp.service" /etc/crontab | cut -c 1'), 0, 1) !== '#') {
shell_exec('sudo systemctl start pistar-upnp.service 2>&1');
}
$start_services_after_upnp = array(
'ysfgateway.service', 'ysf2dmr.service', 'p25gateway.service',
'nxdngateway.service', 'm17gateway.service',
'dapnetgateway.service', 'mobilegps.service', 'cron.service',
);
foreach ($start_services_after_upnp as $svc) {
shell_exec('sudo systemctl start ' . escapeshellarg($svc) . ' 2>&1');
}
// Cleanup: remove the staged extraction dir so subsequent
// restores don't see leftovers.
shell_exec("sudo rm -rf " . escapeshellarg($target_dir) . " 2>&1");
$output .= "Configuration Restore Complete.\n";
echo "<tr><td align=\"left\"><pre>"
. htmlspecialchars($output, ENT_QUOTES, 'UTF-8') . "</pre></td></tr>\n";
} while (false); // end do/while(false) early-exit block
};
echo "</table>\n";
} else { ?> } else { ?>
<form action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>" method="post" enctype="multipart/form-data"> <form action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>" method="post" enctype="multipart/form-data">
<?php csrf_field(); ?> <div class="settings-card action-tiles">
<table width="100%"> <button class="action-tile" name="action" value="download">
<tr> <img src="/images/download.png" alt="Download Config" />
<th colspan="2"><?php echo $lang['backup_restore'];?></th> <span>Download Configuration</span>
</tr> </button>
<tr> <div class="action-tile action-tile-upload">
<td align="center" valign="top" width="50%">Download Configuration<br /> <img src="/images/restore.png" alt="Restore Config" />
<button style="border: none; background: none;" name="action" value="download"><img src="/images/download.png" border="0" alt="Download Config" /></button> <span>Restore Configuration</span>
</td> <input type="file" name="fileToUpload" id="fileToUpload" />
<td align="center" valign="top">Restore Configuration<br /> <button type="submit" name="action" value="restore">Upload &amp; Restore</button>
<button style="border: none; background: none;" name="action" value="restore"><img src="/images/restore.png" border="0" alt="Restore Config" /></button><br /> </div>
<input type="file" name="fileToUpload" id="fileToUpload" /> </div>
</td> <div class="settings-card">
</tr> <div class="field-row"><div class="field-note" style="flex:1 1 100%;">
<tr> <b>WARNING:</b> Editing the files outside of CDN *could* have un-desireable side effects.<br />
<td colspan="2" align="justify"> This backup and restore tool will back up your config files to a Zip file, and allow you to restore them later, either to this CDN or another one.
<br /> <ul>
<b>WARNING:</b><br /> <li>System Passwords / Dashboard passwords are NOT backed up / restored.</li>
Editing the files outside of Pi-Star *could* have un-desireable side effects.<br /> <li>Wireless Configuration IS backed up and restored</li>
<br /> </ul>
This backup and restore tool, will backup your config files to a Zip file, and allow you to restore them later<br /> </div></div>
either to this Pi-Star or another one.<br /> </div>
<ul>
<li>System Passwords / Dashboard passwords are NOT backed up / restored.</li>
<li>Wireless Configuration IS backed up and restored</li>
</ul>
</td>
</tr>
</table>
</form> </form>
<?php } ?> <?php } ?>
</div>
<div class="footer">
Pi-Star web config, &copy; Andy Taylor (MW0MWZ) 2014-<?php echo date("Y"); ?>.<br />
Need help? Click <a style="color: #ffffff;" href="https://www.facebook.com/groups/pistarusergroup/" target="_new">here for the Support Group</a><br />
Get your copy of Pi-Star from <a style="color: #ffffff;" href="http://www.pistar.uk/downloads/" target="_blank">here</a>.<br />
<br />
</div> </div>
</div> </div>
</body> </body>
</html> </html>
<?php <?php
} }
?>
-1
View File
@@ -1 +0,0 @@
../css
View File
+328
View File
@@ -0,0 +1,328 @@
.container {
width: 820px;
text-align: left;
margin: auto;
}
body, font {
font: 12px verdana,arial,sans-serif;
color: #ffffff;
}
.header {
background : #dd4b39;
text-decoration : none;
color : #ffffff;
font-family : verdana, arial, sans-serif;
text-align : left;
padding : 5px 0px 5px 0px;
border-radius : 10px 10px 0 0;
}
.nav {
float : left;
margin : 0;
padding : 3px 3px 3px 3px;
width : 160px;
background : #242d31;
font-weight : normal;
min-height : 100%;
}
.content {
margin : 0 0 0 166px;
padding : 1px 5px 5px 5px;
color : #000000;
background : #ffffff;
text-align: center;
}
.contentwide {
padding: 5px 5px 5px 5px;
color: #000000;
background: #ffffff;
text-align: center;
}
.footer {
background : #dd4b39;
text-decoration : none;
color : #ffffff;
font-family : verdana, arial, sans-serif;
font-size : 9px;
text-align : center;
padding : 10px 0 10px 0;
border-radius : 0 0 10px 10px;
clear : both;
}
#tail {
height: 450px;
width: 805px;
overflow-y: scroll;
overflow-x: scroll;
color: #00ff00;
background: #000000;
}
table {
vertical-align: middle;
text-align: center;
empty-cells: show;
padding-left: 0px;
padding-right: 0px;
padding-top: 0px;
padding-bottom: 0px;
border-collapse:collapse;
border-color: #000000;
border-style: solid;
border-spacing: 4px;
border-width: 2px;
text-decoration: none;
color: #ffffff;
background: #000000;
font-family: verdana,arial,sans-serif;
width: 100%;
white-space: nowrap;
}
table th {
font-family: "Lucidia Console",Monaco,monospace;
text-shadow: 1px 1px #8b0000;
text-decoration: none;
background: #dd4b39;
border: 1px solid #c0c0c0;
}
table tr:nth-child(even) {
background: #f7f7f7;
}
table tr:nth-child(odd) {
background: #d0d0d0;
}
table td {
color: #000000;
font-family: "Lucidia Console",Monaco,monospace;
text-decoration: none;
border: 1px solid #000000;
overflow-x: hidden;
}
body {
background: #edf0f5;
color: #000000;
}
a {
text-decoration:none;
}
a:link, a:visited {
text-decoration: none;
color: #0000e0;
font-weight: normal;
}
a.tooltip, a.tooltip:link, a.tooltip:visited, a.tooltip:active {
text-decoration: none;
position: relative;
color: #FFFFFF;
}
a.tooltip:hover {
text-shadow: none;
text-decoration: none;
color: #FFFFFF;
background: transparent;
}
a.tooltip span {
text-shadow: none;
text-decoration: none;
display: none;
}
a.tooltip:hover span {
text-shadow: none;
text-decoration: none;
display: block;
position: absolute;
top: 20px;
left: 0;
width: 200px;
z-index: 100;
color: #000000;
border:1px solid #000000;
background: #f7f7f7;
font: 12px Verdana, sans-serif;
text-align: left;
}
a.tooltip span b {
text-shadow: none;
text-decoration: none;
display: block;
color: #000000;
margin: 0;
padding: 0;
font-size: 12px;
font-weight: bold;
border: 0px;
border-bottom: 1px solid black;
background: #d0d0d0;
}
a.tooltip2, a.tooltip2:link, a.tooltip2:visited, a.tooltip2:active {
text-shadow: none;
text-decoration: none;
position: relative;
font-weight: bold;
color: #000000;
}
a.tooltip2:hover {
text-shadow: none;
text-decoration: none;
color: #000000;
background: transparent;
}
a.tooltip2 span {
text-shadow: none;
text-decoration: none;
display: none;
}
a.tooltip2:hover span {
text-shadow: none;
text-decoration: none;
display: block;
position: absolute;
top: 20px;
left: 0;
width: 200px;
z-index: 100;
color: #000000;
border:1px solid #000000;
background: #f7f7f7;
font: 12px Verdana, sans-serif;
text-align: left;
}
a.tooltip2 span b {
text-shadow: none;
text-decoration: none;
display: block;
color: #000000;
margin: 0;
padding: 0;
font-size: 12px;
font-weight: bold;
border: 0px;
border-bottom: 1px solid black;
background: #d0d0d0;
}
ul {
padding: 5px;
margin: 10px 0;
list-style: none;
float: left;
}
ul li {
float: left;
display: inline; /*For ignore double margin in IE6*/
margin: 0 10px;
}
ul li a {
text-decoration: none;
float:left;
color: #999;
cursor: pointer;
font: 900 14px/22px "Arial", Helvetica, sans-serif;
}
ul li a span {
margin: 0 10px 0 -10px;
padding: 1px 8px 5px 18px;
position: relative; /*To fix IE6 problem (not displaying)*/
float:left;
}
ul.mmenu li a.current, ul.mmenu li a:hover {
background: url(/images/buttonbg.png) no-repeat top right;
color: #0d5f83;
}
ul.mmenu li a.current span, ul.mmenu li a:hover span {
background: url(/images/buttonbg.png) no-repeat top left;
}
h1 {
text-shadow: 2px 2px #303030;
text-align: center;
}
/* CSS Toggle Code here */
.toggle {
position: absolute;
margin-left: -9999px;
visibility: hidden;
}
.toggle + label {
display: block;
position: relative;
cursor: pointer;
outline: none;
}
input.toggle-round-flat + label {
padding: 1px;
width: 33px;
height: 18px;
background-color: #dddddd;
border-radius: 10px;
transition: background 0.4s;
}
input.toggle-round-flat + label:before,
input.toggle-round-flat + label:after {
display: block;
position: absolute;
content: "";
}
input.toggle-round-flat + label:before {
top: 1px;
left: 1px;
bottom: 1px;
right: 1px;
background-color: #fff;
border-radius: 10px;
transition: background 0.4s;
}
input.toggle-round-flat + label:after {
top: 2px;
left: 2px;
bottom: 2px;
width: 16px;
background-color: #dddddd;
border-radius: 12px;
transition: margin 0.4s, background 0.4s;
}
input.toggle-round-flat:checked + label {
background-color: #dd4b39;
}
input.toggle-round-flat:checked + label:after {
margin-left: 14px;
background-color: #dd4b39;
}
+330
View File
@@ -0,0 +1,330 @@
.container {
width: 100%;
text-align: left;
margin: auto;
}
body, font {
font: 12px verdana,arial,sans-serif;
color: #ffffff;
}
.header {
background : #dd4b39;
text-decoration : none;
color : #ffffff;
font-family : verdana, arial, sans-serif;
text-align : left;
padding : 5px 0px 5px 0px;
border-radius : 10px 10px 0 0;
}
.nav {
display: none;
float : left;
margin : 0;
padding : 3px 3px 3px 3px;
width : 160px;
background : #242d31;
font-weight : normal;
min-height : 100%;
}
.content {
padding: 5px 5px 5px 5px;
color: #000000;
background: #ffffff;
text-align: center;
font-size: 1.4em;
}
.contentwide {
padding: 5px 5px 5px 5px;
color: #000000;
background: #ffffff;
text-align: center;
font-size: 1.4em;
}
.footer {
background : #dd4b39;
text-decoration : none;
color : #ffffff;
font-family : verdana, arial, sans-serif;
font-size : 9px;
text-align : center;
padding : 10px 0 10px 0;
border-radius : 0 0 10px 10px;
clear : both;
}
#tail {
height: 450px;
width: 805px;
overflow-y: scroll;
overflow-x: scroll;
color: #00ff00;
background: #000000;
}
table {
vertical-align: middle;
text-align: center;
empty-cells: show;
padding-left: 0px;
padding-right: 0px;
padding-top: 0px;
padding-bottom: 0px;
border-collapse:collapse;
border-color: #000000;
border-style: solid;
border-spacing: 4px;
border-width: 2px;
text-decoration: none;
color: #ffffff;
background: #000000;
font-family: verdana,arial,sans-serif;
width: 100%;
white-space: nowrap;
}
table th {
font-family: "Lucidia Console",Monaco,monospace;
text-shadow: 1px 1px #8b0000;
text-decoration: none;
background: #dd4b39;
border: 1px solid #c0c0c0;
}
table tr:nth-child(even) {
background: #f7f7f7;
}
table tr:nth-child(odd) {
background: #d0d0d0;
}
table td {
color: #000000;
font-family: "Lucidia Console",Monaco,monospace;
text-decoration: none;
border: 1px solid #000000;
overflow-x: hidden;
}
body {
background: #edf0f5;
color: #000000;
}
a {
text-decoration:none;
}
a:link, a:visited {
text-decoration: none;
color: #0000e0;
font-weight: normal;
}
a.tooltip, a.tooltip:link, a.tooltip:visited, a.tooltip:active {
text-decoration: none;
position: relative;
color: #FFFFFF;
}
a.tooltip:hover {
text-shadow: none;
text-decoration: none;
color: #FFFFFF;
background: transparent;
}
a.tooltip span {
text-shadow: none;
text-decoration: none;
display: none;
}
a.tooltip:hover span {
text-shadow: none;
text-decoration: none;
display: block;
position: absolute;
top: 20px;
left: 0;
width: 200px;
z-index: 100;
color: #000000;
border:1px solid #000000;
background: #f7f7f7;
font: 12px Verdana, sans-serif;
text-align: left;
}
a.tooltip span b {
text-shadow: none;
text-decoration: none;
display: block;
color: #000000;
margin: 0;
padding: 0;
font-size: 12px;
font-weight: bold;
border: 0px;
border-bottom: 1px solid black;
background: #d0d0d0;
}
a.tooltip2, a.tooltip2:link, a.tooltip2:visited, a.tooltip2:active {
text-shadow: none;
text-decoration: none;
position: relative;
font-weight: bold;
color: #000000;
}
a.tooltip2:hover {
text-shadow: none;
text-decoration: none;
color: #000000;
background: transparent;
}
a.tooltip2 span {
text-shadow: none;
text-decoration: none;
display: none;
}
a.tooltip2:hover span {
text-shadow: none;
text-decoration: none;
display: block;
position: absolute;
top: 20px;
left: 0;
width: 200px;
z-index: 100;
color: #000000;
border:1px solid #000000;
background: #f7f7f7;
font: 12px Verdana, sans-serif;
text-align: left;
}
a.tooltip2 span b {
text-shadow: none;
text-decoration: none;
display: block;
color: #000000;
margin: 0;
padding: 0;
font-size: 12px;
font-weight: bold;
border: 0px;
border-bottom: 1px solid black;
background: #d0d0d0;
}
ul {
padding: 5px;
margin: 10px 0;
list-style: none;
float: left;
}
ul li {
float: left;
display: inline; /*For ignore double margin in IE6*/
margin: 0 10px;
}
ul li a {
text-decoration: none;
float:left;
color: #999;
cursor: pointer;
font: 900 14px/22px "Arial", Helvetica, sans-serif;
}
ul li a span {
margin: 0 10px 0 -10px;
padding: 1px 8px 5px 18px;
position: relative; /*To fix IE6 problem (not displaying)*/
float:left;
}
ul.mmenu li a.current, ul.mmenu li a:hover {
background: url(/images/buttonbg.png) no-repeat top right;
color: #0d5f83;
}
ul.mmenu li a.current span, ul.mmenu li a:hover span {
background: url(/images/buttonbg.png) no-repeat top left;
}
h1 {
text-shadow: 2px 2px #303030;
text-align: center;
}
/* CSS Toggle Code here */
.toggle {
position: absolute;
margin-left: -9999px;
visibility: hidden;
}
.toggle + label {
display: block;
position: relative;
cursor: pointer;
outline: none;
}
input.toggle-round-flat + label {
padding: 1px;
width: 33px;
height: 18px;
background-color: #dddddd;
border-radius: 10px;
transition: background 0.4s;
}
input.toggle-round-flat + label:before,
input.toggle-round-flat + label:after {
display: block;
position: absolute;
content: "";
}
input.toggle-round-flat + label:before {
top: 1px;
left: 1px;
bottom: 1px;
right: 1px;
background-color: #fff;
border-radius: 10px;
transition: background 0.4s;
}
input.toggle-round-flat + label:after {
top: 2px;
left: 2px;
bottom: 2px;
width: 16px;
background-color: #dddddd;
border-radius: 12px;
transition: margin 0.4s, background 0.4s;
}
input.toggle-round-flat:checked + label {
background-color: #dd4b39;
}
input.toggle-round-flat:checked + label:after {
margin-left: 14px;
background-color: #dd4b39;
}
+1
View File
@@ -0,0 +1 @@
.nice-select{-webkit-tap-highlight-color:rgba(0,0,0,0);background-color:#fff;border-radius:5px;border:solid 1px #e8e8e8;box-sizing:border-box;clear:both;cursor:pointer;display:block;float:left;font-family:inherit;font-size:14px;font-weight:normal;height:38px;line-height:36px;outline:none;padding-left:18px;padding-right:30px;position:relative;text-align:left !important;transition:all .2s ease-in-out;user-select:none;white-space:nowrap;width:auto}.nice-select:hover{border-color:#dbdbdb}.nice-select:active,.nice-select.open,.nice-select:focus{border-color:#999}.nice-select:after{border-bottom:2px solid #999;border-right:2px solid #999;content:"";display:block;height:5px;margin-top:-4px;pointer-events:none;position:absolute;right:12px;top:50%;transform-origin:66% 66%;transform:rotate(45deg);transition:all .15s ease-in-out;width:5px}.nice-select.open:after{transform:rotate(-135deg)}.nice-select.open .nice-select-dropdown{opacity:1;pointer-events:auto;transform:scale(1) translateY(0)}.nice-select.disabled{border-color:#ededed;color:#999;pointer-events:none}.nice-select.disabled:after{border-color:#ccc}.nice-select.wide{width:100%}.nice-select.wide .nice-select-dropdown{left:0 !important;right:0 !important}.nice-select.right{float:right}.nice-select.right .nice-select-dropdown{left:auto;right:0}.nice-select.small{font-size:12px;height:36px;line-height:34px}.nice-select.small:after{height:4px;width:4px}.nice-select.small .option{line-height:34px;min-height:34px}.nice-select .nice-select-dropdown{margin-top:4px;background-color:#fff;border-radius:5px;box-shadow:0 0 0 1px rgba(68,68,68,.11);pointer-events:none;position:absolute;top:100%;left:0;transform-origin:50% 0;transform:scale(0.75) translateY(19px);transition:all .2s cubic-bezier(0.5, 0, 0, 1.25),opacity .15s ease-out;z-index:9;opacity:0}.nice-select .list{border-radius:5px;box-sizing:border-box;overflow:hidden;padding:0;max-height:210px;overflow-y:auto}.nice-select .list:hover .option:not(:hover){background-color:transparent !important}.nice-select .option{cursor:pointer;font-weight:400;line-height:40px;list-style:none;outline:none;padding-left:18px;padding-right:29px;text-align:left;transition:all .2s}.nice-select .option:hover,.nice-select .option.focus,.nice-select .option.selected.focus{background-color:#f6f6f6}.nice-select .option.selected{font-weight:bold}.nice-select .option.disabled{background-color:transparent;color:#999;cursor:default}.nice-select .optgroup{font-weight:bold}.no-csspointerevents .nice-select .nice-select-dropdown{display:none}.no-csspointerevents .nice-select.open .nice-select-dropdown{display:block}.nice-select .list::-webkit-scrollbar{width:0}.nice-select .has-multiple{white-space:inherit;height:auto;padding:7px 12px;min-height:36px;line-height:22px}.nice-select .has-multiple span.current{border:1px solid #ccc;background:#eee;padding:0 10px;border-radius:3px;display:inline-block;line-height:24px;font-size:14px;margin-bottom:3px;margin-right:3px}.nice-select .has-multiple .multiple-options{display:block;line-height:24px;padding:0}.nice-select .nice-select-search-box{box-sizing:border-box;width:100%;padding:5px;pointer-events:none;border-radius:5px 5px 0 0}.nice-select .nice-select-search{box-sizing:border-box;background-color:#fff;border:1px solid #e8e8e8;border-radius:3px;color:#444;display:inline-block;vertical-align:middle;padding:7px 12px;margin:0 10px 0 0;width:100%;min-height:36px;line-height:22px;height:auto;outline:0 !important;font-size:14px}
+915
View File
@@ -0,0 +1,915 @@
<?php
// Do not edit this file, instead use the new dashboard editor in the expert section.
// New CSS Overlay for Pi-Star, to allow setting the variables from outside this file.
// Output CSS and not plain text
header("Content-type: text/css");
// Check if the config file exists
if (file_exists('/etc/pistar-css.ini')) {
// Use the values from the file
$piStarCssFile = '/etc/pistar-css.ini';
if (fopen($piStarCssFile,'r')) { $piStarCss = parse_ini_file($piStarCssFile, true); }
// Set the Values from the config file
$backgroundPage = $piStarCss['Background']['Page']; // usually off-white
$backgroundContent = $piStarCss['Background']['Content']; // The White background in the content section
$backgroundBanners = $piStarCss['Background']['Banners']; // The Pi-Star accent colour
$textBanners = $piStarCss['Text']['Banners']; // Usually white
$bannerDropShaddows = $piStarCss['Text']['BannersDrop']; // Banner drop shaddow colour
$tableHeadDropShaddow = $piStarCss['Tables']['HeadDrop']; // Table Headder drop shaddows
$textContent = $piStarCss['Content']['Text']; // Used for the section titles
$tableRowEvenBg = $piStarCss['Tables']['BgEven']; // Table Row BG Colour (Even)
$tableRowOddBg = $piStarCss['Tables']['BgOdd']; // Table Row BG Colour (Odd)
} else {
// Default values - refreshed neutral/indigo theme (was the red Pi-Star theme)
$backgroundPage = "f3f4f8"; // soft neutral page background
$backgroundContent = "ffffff"; // The White background in the content section
$backgroundBanners = "4f46e5"; // Accent colour (was the red dd4b39)
$textBanners = "ffffff"; // Usually white
$bannerDropShaddows = "1e1b4b"; // Banner drop shaddow colour
$tableHeadDropShaddow = "3730a3"; // Table Headder drop shaddows
$textContent = "111827"; // Used for the section titles
$tableRowEvenBg = "f8fafc"; // Table Row BG Colour (Even)
$tableRowOddBg = "eef0f5"; // Table Row BG Colour (Odd)
}
// Purely cosmetic helper: derives lighter/darker shades of the configured accent
// colour for gradients/hover states. Does not change any stored configuration.
if (!function_exists('pistarShade')) {
function pistarShade($hex, $amount) {
$hex = ltrim(trim($hex), '#');
if (strlen($hex) == 3) { $hex = $hex[0].$hex[0].$hex[1].$hex[1].$hex[2].$hex[2]; }
if (strlen($hex) != 6 || !ctype_xdigit($hex)) { return $hex; }
$r = max(0, min(255, hexdec(substr($hex,0,2)) + $amount));
$g = max(0, min(255, hexdec(substr($hex,2,2)) + $amount));
$b = max(0, min(255, hexdec(substr($hex,4,2)) + $amount));
return sprintf('%02x%02x%02x', $r, $g, $b);
}
}
$backgroundBannersDark = pistarShade($backgroundBanners, -28);
$backgroundBannersLight = pistarShade($backgroundBanners, 20);
?>
:root {
--page-bg: #<?php echo $backgroundPage; ?>;
--content-bg: #<?php echo $backgroundContent; ?>;
--accent: #<?php echo $backgroundBanners; ?>;
--accent-dark: #<?php echo $backgroundBannersDark; ?>;
--accent-light: #<?php echo $backgroundBannersLight; ?>;
--accent-text: #<?php echo $textBanners; ?>;
--banner-drop: #<?php echo $bannerDropShaddows; ?>;
--table-head-drop: #<?php echo $tableHeadDropShaddow; ?>;
--content-text: #<?php echo $textContent; ?>;
--table-even: #<?php echo $tableRowEvenBg; ?>;
--table-odd: #<?php echo $tableRowOddBg; ?>;
--sidebar-bg: #111827;
--sidebar-bg-2: #0b1120;
--sidebar-text: #cbd5e1;
--sidebar-text-dim: #94a3b8;
--sidebar-card-bg: rgba(255,255,255,0.05);
--sidebar-card-strong-bg: rgba(255,255,255,0.09);
--radius: 12px;
--radius-sm: 8px;
--card-border: rgba(15,23,42,0.07);
--shadow-card: 0 1px 2px rgba(15,23,42,0.04), 0 10px 28px -14px rgba(15,23,42,0.16);
}
html, body {
margin: 0;
padding: 0;
}
/* Small screens: the rail collapses into a top bar (no fixed sidebar) */
.container {
width: 100%;
text-align: left;
margin: auto;
background: var(--page-bg);
border-radius: 0;
box-shadow: none;
overflow: visible;
}
body, font {
font: 13px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
color: #ffffff;
}
.header {
position: static;
width: auto;
background: linear-gradient(180deg, var(--sidebar-bg), var(--sidebar-bg-2));
color: var(--sidebar-text);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
text-align: left;
padding: 16px 14px;
box-sizing: border-box;
}
.header h1 {
font-size: 1.05em;
line-height: 1.3;
color: #ffffff;
text-shadow: none;
text-align: left;
margin: 0 0 4px 0;
padding: 0;
font-weight: 700;
}
.header > div[style*="font-size: 8px"] {
float: none !important;
text-align: left !important;
color: var(--sidebar-text-dim) !important;
font-size: 10.5px !important;
padding: 0 !important;
margin: 0 0 10px 0;
}
.header p {
font-size: 0;
margin: 6px 0;
padding: 0;
text-align: left !important;
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 2px;
}
.header p a, .header p a:link, .header p a:visited {
display: inline-block;
font-size: 13px;
font-weight: 500;
color: var(--sidebar-text) !important;
padding: 8px 10px;
border-radius: 8px;
transition: background 0.15s ease, color 0.15s ease;
}
.header p a:hover {
background: rgba(255,255,255,0.08);
color: #ffffff !important;
}
.header p a.active {
background: var(--accent);
color: var(--accent-text) !important;
font-weight: 700;
}
.header p b {
font-size: 11px;
color: var(--sidebar-text-dim);
display: block;
width: 100%;
margin: 8px 0 2px 2px;
text-transform: uppercase;
letter-spacing: 0.04em;
}
/* On narrow screens the widgets no longer live in a side column - they
become a full-width panel stacked under the top bar, laid out as a
two-column grid of compact status chips instead of skinny stacked rows. */
.nav {
display: block;
float: none;
box-sizing: border-box;
margin: 0;
padding: 14px;
width: 100%;
background: linear-gradient(180deg, var(--sidebar-bg), var(--sidebar-bg-2));
font-weight: normal;
min-height: 0;
}
.nav table {
width: 100%;
margin: 0 0 12px 0;
border-collapse: separate;
border-spacing: 4px;
table-layout: fixed;
white-space: normal;
}
.nav table th[colspan] {
background: transparent;
color: var(--sidebar-text-dim);
text-align: left;
text-transform: uppercase;
font-size: 10px;
font-weight: 700;
letter-spacing: 0.06em;
padding: 10px 2px 6px 2px;
border: none;
}
.nav table:first-child th[colspan] {
padding-top: 0;
}
.nav table th:not([colspan]) {
background: var(--sidebar-card-bg);
color: var(--sidebar-text-dim);
text-align: left;
font-size: 10.5px;
font-weight: 700;
padding: 7px 8px;
border: none;
border-radius: var(--radius-sm);
width: 30%;
}
.nav table td {
color: var(--sidebar-text);
background: var(--sidebar-card-bg);
font-size: 11px;
font-weight: 600;
padding: 7px 8px;
border: none;
border-radius: var(--radius-sm);
}
.nav table td[style*="fff"] {
background: var(--sidebar-card-strong-bg) !important;
color: var(--sidebar-text) !important;
}
.nav table tr:nth-child(even),
.nav table tr:nth-child(odd) {
background: transparent;
}
.nav table a { color: #ffffff; font-weight: 700; }
#lastHerd, #localTxs, #Pages, #sysInfo,
#refLinks, #cssConnects, #lh, #localTx, #bmConnects, #tgifConnects {
margin: 14px;
background: var(--content-bg);
border: 1px solid var(--card-border);
border-radius: var(--radius);
box-shadow: var(--shadow-card);
padding: 14px 14px 18px 14px;
box-sizing: border-box;
overflow-x: auto;
}
#lastHerd > b, #localTxs > b, #Pages > b, #sysInfo > b,
#refLinks > b, #cssConnects > b, #lh > b, #localTx > b, #bmConnects > b, #tgifConnects > b {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
font-weight: 700;
color: var(--content-text);
margin: 0 0 12px 0;
padding-bottom: 10px;
border-bottom: 1px solid var(--card-border);
}
#lastHerd > b::before, #localTxs > b::before, #Pages > b::before, #sysInfo > b::before,
#refLinks > b::before, #cssConnects > b::before, #lh > b::before, #localTx > b::before, #bmConnects > b::before, #tgifConnects > b::before {
content: "";
width: 8px;
height: 8px;
border-radius: 2px;
background: var(--accent);
flex: none;
}
.content {
padding: 6px 0 5px 0;
color: var(--content-text);
background: var(--page-bg);
text-align: left;
font-size: 1.4em;
}
.contentwide {
padding: 16px;
color: var(--content-text);
background: var(--page-bg);
text-align: center;
font-size: 1.4em;
}
.contentwide h2 {
color: var(--content-text);
font: 600 1em -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
text-align: left;
padding: 0 0 8px 10px;
margin: 24px 0 10px 0;
border-left: 4px solid var(--accent);
border-bottom: 1px solid rgba(0,0,0,0.08);
}
.contentwide h2:first-child {
margin-top: 0;
}
/* Inline "Alert: ..." banners used across the dashboard (config warnings etc.) */
.alert-banner-table {
margin: 0 0 16px 0 !important;
width: 100% !important;
}
.alert-banner-table, .alert-banner-table tr {
background: transparent !important;
}
.alert-cell {
text-align: left !important;
border-radius: 8px;
padding: 12px 16px !important;
font-weight: 500;
border: 1px solid transparent;
}
.alert-warning {
background: #fffbeb;
color: #92400e;
border-color: #fde68a;
}
.alert-danger {
background: #fef2f2;
color: #b91c1c;
border-color: #fecaca;
}
.alert-warning a, .alert-danger a {
color: inherit;
text-decoration: underline;
}
/* Settings-card system (mirrors pistar-css.php) - this was missing from the
mobile stylesheet entirely, so every config/editor page rendered as
unstyled plain divs below the 830px breakpoint. Rows stack by default
here since there's no room for the desktop's label+control row. */
.settings-card {
background: var(--content-bg);
border: 1px solid rgba(0,0,0,0.08);
border-radius: 12px;
box-shadow: 0 1px 3px rgba(0,0,0,0.04);
padding: 4px 16px;
margin: 0 0 20px 0;
text-align: left;
}
.settings-card > b:first-child {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
font-weight: 700;
color: var(--content-text);
margin: 14px 0 12px 0;
padding-bottom: 10px;
border-bottom: 1px solid var(--card-border);
}
.settings-card > b:first-child::before {
content: "";
width: 8px;
height: 8px;
border-radius: 2px;
background: var(--accent);
flex: none;
}
.field-row {
display: flex;
flex-direction: column;
align-items: flex-start;
row-gap: 6px;
padding: 12px 0;
border-bottom: 1px solid rgba(0,0,0,0.06);
}
.field-row:last-child {
border-bottom: none;
}
.field-label, .field-row > div:first-child {
flex: 0 0 auto;
max-width: none;
font-weight: 500;
font-size: 13px;
color: var(--content-text);
text-align: left;
}
.field-control, .field-row > div:not(:first-child) {
flex: 1 1 auto;
width: 100%;
box-sizing: border-box;
text-align: left;
color: var(--content-text);
font-size: 13px;
}
.field-control input[type="text"], .field-control input[type="password"], .field-control input[type="number"],
.field-row input[type="text"], .field-row input[type="password"], .field-row input[type="number"] {
width: 100%;
box-sizing: border-box;
padding: 6px 8px;
border: 1px solid rgba(0,0,0,0.18);
}
.field-control select, .field-row select {
max-width: 100%;
padding: 5px 6px;
border: 1px solid rgba(0,0,0,0.18);
}
.field-note {
font-weight: 400;
font-size: 12.5px;
color: #6b7280;
}
/* Big icon action buttons (power page, reboot/shutdown) */
.action-tiles {
display: flex;
flex-wrap: wrap;
gap: 12px;
padding: 14px;
}
.action-tile {
flex: 1 1 45%;
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
padding: 18px 12px;
background: var(--page-bg);
border: 1px solid rgba(0,0,0,0.08);
border-radius: 10px;
cursor: pointer;
font: 500 13px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
color: var(--content-text);
}
.action-tile img {
width: 40px;
height: 40px;
}
.action-tile-upload {
cursor: default;
}
.action-tile-upload input[type="file"] {
max-width: 100%;
font-size: 11px;
}
/* Calibration tool control panels */
.cal-grid {
display: flex;
flex-wrap: wrap;
gap: 14px;
align-items: flex-start;
}
.cal-panel {
flex: 1 1 100%;
margin: 0;
}
.cal-panel table {
width: 100%;
}
.cal-btn-row {
display: flex;
align-items: center;
gap: 8px;
padding: 6px 0;
flex-wrap: wrap;
}
/* Raw config-file editors (expert "Full Edit" pages) */
.raw-editor {
width: 100%;
min-height: 360px;
box-sizing: border-box;
background: #10141a;
color: #d1d5db;
border: 1px solid rgba(0,0,0,0.15);
border-radius: 8px;
padding: 10px;
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
font-size: 12px;
line-height: 1.5;
}
.power-status {
background: #10141a;
color: #3ce87a;
border-radius: 8px;
padding: 16px;
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
line-height: 1.6;
}
.field-actions {
display: flex;
justify-content: center;
align-items: center;
padding: 18px 0 22px 0;
}
.field-actions input[type="button"], .field-actions input[type="submit"] {
background: var(--accent);
color: var(--accent-text);
border: none;
padding: 11px 40px;
font-size: 14px;
font-weight: 700;
letter-spacing: 0.02em;
border-radius: 999px;
box-shadow: 0 2px 6px rgba(15,23,42,0.16), 0 8px 20px -8px rgba(15,23,42,0.28);
cursor: pointer;
}
.field-actions input[type="button"]:active, .field-actions input[type="submit"]:active {
filter: brightness(0.97);
}
.footer {
background: transparent;
text-decoration: none;
color: #6b7280;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
font-size: 10.5px;
text-align: left;
padding: 14px 16px 20px 16px;
border-top: 1px solid rgba(0,0,0,0.07);
clear: both;
}
.footer a {
color: var(--accent) !important;
}
#tail {
height: 450px;
width: 100%;
overflow-y: scroll;
overflow-x: scroll;
color: #3ce87a;
background: #10141a;
border-radius: 8px;
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
box-sizing: border-box;
}
#tail::-webkit-scrollbar {
width: 10px;
height: 10px;
}
#tail::-webkit-scrollbar-track {
background: #10141a;
}
#tail::-webkit-scrollbar-thumb {
background: rgba(60, 232, 122, 0.35);
border-radius: 6px;
}
table {
vertical-align: middle;
text-align: center;
empty-cells: show;
padding-left: 0px;
padding-right: 0px;
padding-top: 0px;
padding-bottom: 0px;
border-collapse: collapse;
border: none;
text-decoration: none;
color: #ffffff;
background: transparent;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
width: 100%;
white-space: nowrap;
}
table th {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
text-shadow: none;
text-decoration: none;
background: transparent;
color: var(--content-text);
text-transform: uppercase;
font-size: 10.5px;
font-weight: 700;
letter-spacing: 0.03em;
padding: 9px 10px;
border: none;
border-bottom: 2px solid var(--accent);
}
table tr:nth-child(even) {
background: var(--table-even);
}
table tr:nth-child(odd) {
background: var(--table-odd);
}
table td {
color: var(--content-text);
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
text-decoration: none;
padding: 7px 10px;
border: none;
border-bottom: 1px solid var(--card-border);
overflow-x: hidden;
}
table tr:last-child td {
border-bottom: none;
}
body {
background: var(--page-bg);
color: var(--content-text);
}
a {
text-decoration:none;
}
a:link, a:visited {
text-decoration: none;
color: var(--accent);
font-weight: normal;
transition: color 0.15s ease;
}
a:hover {
color: var(--accent-dark);
text-decoration: underline;
}
a.tooltip, a.tooltip:link, a.tooltip:visited, a.tooltip:active {
text-decoration: none;
position: relative;
color: #FFFFFF;
}
a.tooltip:hover {
text-shadow: none;
text-decoration: none;
color: #FFFFFF;
background: transparent;
}
a.tooltip span {
text-shadow: none;
text-decoration: none;
display: none;
}
a.tooltip:hover span {
text-shadow: none;
text-decoration: none;
display: block;
position: absolute;
top: 20px;
left: 0;
width: 200px;
z-index: 100;
color: #000000;
border: none;
border-radius: 6px;
box-shadow: 0 4px 16px rgba(0,0,0,0.25);
background: #f7f7f7;
font: 12px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
text-align: left;
overflow: hidden;
}
a.tooltip span b {
text-shadow: none;
text-decoration: none;
display: block;
color: #000000;
margin: 0;
padding: 4px 8px;
font-size: 12px;
font-weight: bold;
border: 0px;
border-bottom: 1px solid rgba(0,0,0,0.15);
background: #e2e2e2;
}
a.tooltip2, a.tooltip2:link, a.tooltip2:visited, a.tooltip2:active {
text-shadow: none;
text-decoration: none;
position: relative;
font-weight: bold;
color: #000000;
}
a.tooltip2:hover {
text-shadow: none;
text-decoration: none;
color: #000000;
background: transparent;
}
a.tooltip2 span {
text-shadow: none;
text-decoration: none;
display: none;
}
a.tooltip2:hover span {
text-shadow: none;
text-decoration: none;
display: block;
position: absolute;
top: 20px;
left: 0;
width: 200px;
z-index: 100;
color: #000000;
border: none;
border-radius: 6px;
box-shadow: 0 4px 16px rgba(0,0,0,0.25);
background: #f7f7f7;
font: 12px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
text-align: left;
overflow: hidden;
}
a.tooltip2 span b {
text-shadow: none;
text-decoration: none;
display: block;
color: #000000;
margin: 0;
padding: 4px 8px;
font-size: 12px;
font-weight: bold;
border: 0px;
border-bottom: 1px solid rgba(0,0,0,0.15);
background: #e2e2e2;
}
ul {
padding: 5px;
margin: 10px 0;
list-style: none;
float: left;
}
ul li {
float: left;
display: inline; /*For ignore double margin in IE6*/
margin: 0 10px;
}
ul li a {
text-decoration: none;
float:left;
color: #999;
cursor: pointer;
font: 900 14px/22px -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
transition: color 0.15s ease;
}
ul li a span {
margin: 0 10px 0 -10px;
padding: 1px 8px 5px 18px;
position: relative; /*To fix IE6 problem (not displaying)*/
float:left;
}
ul.mmenu li a.current, ul.mmenu li a:hover {
background: url(/images/buttonbg.png) no-repeat top right;
color: #0d5f83;
}
ul.mmenu li a.current span, ul.mmenu li a:hover span {
background: url(/images/buttonbg.png) no-repeat top left;
}
h1 {
text-shadow: 1px 1px 2px rgba(0,0,0,0.35);
text-align: center;
}
/* CSS Toggle Code here */
.toggle {
position: absolute;
margin-left: -9999px;
visibility: hidden;
}
.toggle + label {
display: block;
position: relative;
cursor: pointer;
outline: none;
}
input.toggle-round-flat + label {
padding: 1px;
width: 33px;
height: 18px;
background-color: #dddddd;
border-radius: 10px;
transition: background 0.4s;
}
input.toggle-round-flat + label:before,
input.toggle-round-flat + label:after {
display: block;
position: absolute;
content: "";
}
input.toggle-round-flat + label:before {
top: 1px;
left: 1px;
bottom: 1px;
right: 1px;
background-color: #fff;
border-radius: 10px;
transition: background 0.4s;
}
input.toggle-round-flat + label:after {
top: 2px;
left: 2px;
bottom: 2px;
width: 16px;
background-color: #dddddd;
border-radius: 12px;
transition: margin 0.4s, background 0.4s;
}
input.toggle-round-flat:checked + label {
background-color: var(--accent);
}
input.toggle-round-flat:checked + label:after {
margin-left: 14px;
background-color: var(--accent);
}
/* Gentle, geometry-neutral polish for form controls (no size/layout changes) */
input[type="text"], input[type="password"], input[type="number"], input[type="email"], select, textarea {
border-radius: 4px;
transition: box-shadow 0.15s ease, border-color 0.15s ease;
}
input[type="text"]:focus, input[type="password"]:focus, input[type="number"]:focus, input[type="email"]:focus, select:focus, textarea:focus {
outline: none;
box-shadow: 0 0 0 3px rgba(79,70,229,0.18);
border-color: var(--accent);
}
input[type="submit"], input[type="button"], button {
cursor: pointer;
border-radius: 4px;
transition: filter 0.15s ease, box-shadow 0.15s ease;
}
input[type="submit"]:hover, input[type="button"]:hover, button:hover {
filter: brightness(1.06);
box-shadow: 0 1px 6px rgba(0,0,0,0.25);
}
/* Tame Firefox Buttons */
@-moz-document url-prefix() {
select,
input {
margin : 0;
padding : 0;
border-width : 1px;
font : 12px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}
input[type="button"], button, input[type="submit"] {
padding : 0px 3px 0px 3px;
border-radius : 3px 3px 3px 3px;
-moz-border-radius : 3px 3px 3px 3px;
}
}
/* nice-select dropdowns */
.nice-select.small, .nice-select-dropdown li.option {
height: 24px !important;
min-height: 24px !important;
line-height: 24px !important;
}
.nice-select.small ul li:nth-of-type(2) {
clear: both;
}
File diff suppressed because it is too large Load Diff
+41 -54
View File
@@ -1,63 +1,50 @@
<?php <?php
/**
* MMDVM modem log download.
*
* Sends today's /var/log/pi-star/MMDVM-YYYY-MM-DD.log (or the
* dstarrepeater equivalent) as a download. Picks the right log
* filename based on which mode marker exists in /etc/. Streams the
* file in 8 KiB chunks, with a User-Agent sniff to translate \n to
* \r\n for Windows clients.
*
* NOTE for the security pass: no setSecurityHeaders() call (binary
* download), and no auth check beyond the PHP_SELF guard.
*/
require_once($_SERVER['DOCUMENT_ROOT'] . '/config/security_headers.php');
setSecurityHeaders();
if ($_SERVER["PHP_SELF"] == "/admin/download_modem_log.php") { if ($_SERVER["PHP_SELF"] == "/admin/download_modem_log.php") {
if (file_exists('/etc/dstar-radio.mmdvmhost')) { if (file_exists('/etc/dstar-radio.mmdvmhost')) {
$logfile = "/var/log/pi-star/MMDVM-".gmdate('Y-m-d').".log"; $logfile = "/var/log/pi-star/MMDVM-".gmdate('Y-m-d').".log";
} }
elseif (file_exists('/etc/dstar-radio.dstarrepeater')) { elseif (file_exists('/etc/dstar-radio.dstarrepeater')) {
if (file_exists("/var/log/pi-star/DStarRepeater-".gmdate('Y-m-d').".log")) {$logfile = "/var/log/pi-star/DStarRepeater-".gmdate('Y-m-d').".log";} if (file_exists("/var/log/pi-star/DStarRepeater-".gmdate('Y-m-d').".log")) {$logfile = "/var/log/pi-star/DStarRepeater-".gmdate('Y-m-d').".log";}
if (file_exists("/var/log/pi-star/dstarrepeaterd-".gmdate('Y-m-d').".log")) {$logfile = "/var/log/pi-star/dstarrepeaterd-".gmdate('Y-m-d').".log";} if (file_exists("/var/log/pi-star/dstarrepeaterd-".gmdate('Y-m-d').".log")) {$logfile = "/var/log/pi-star/dstarrepeaterd-".gmdate('Y-m-d').".log";}
} }
$hostNameInfo = exec('cat /etc/hostname'); $unixfile = file_get_contents($logfile);
$dosfile = str_replace("\n", "\r\n", $unixfile);
$hostNameInfo = exec('cat /etc/hostname');
header('Pragma: public'); header('Pragma: public');
header('Expires: 0'); header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Cache-Control: private', false); header('Cache-Control: private', false);
header('Content-Type: text/plain'); header('Content-Type: text/plain');
if ($hostNameInfo != "pi-star") { if ($hostNameInfo != "cdn") {
header('Content-Disposition: attachment; filename="Pi-Star_'.$hostNameInfo.'_'.basename($logfile).'";'); header('Content-Disposition: attachment; filename="CDN_'.$hostNameInfo.'_'.basename($logfile).'";');
} else { } else {
header('Content-Disposition: attachment; filename="Pi-Star_'.basename($logfile).'";'); header('Content-Disposition: attachment; filename="CDN_'.basename($logfile).'";');
} }
header('Content-Length: '.filesize($logfile)); header('Content-Length: '.filesize($logfile));
header('Accept-Ranges: bytes'); header('Accept-Ranges: bytes');
// User Agent Detection // User Agent Detection
if (strpos($_SERVER['HTTP_USER_AGENT'], 'indows') !== false) { if (strpos($_SERVER['HTTP_USER_AGENT'], 'indows') !== false) {
$userAgent = "Windows"; $userAgent = "Windows";
} else { } else {
$userAgent = "NonWindows"; $userAgent = "NonWindows";
} }
// Pre-flight checks done, send the output. // Pre-flight checks done, send the output.
set_time_limit(0); set_time_limit(0);
$file = @fopen($logfile,"rb"); $file = @fopen($logfile,"rb");
while(!feof($file)) { while(!feof($file)) {
if ($userAgent == "Windows") { print(str_replace("\n", "\r\n", @fread($file, 1024*8))); } if ($userAgent == "Windows") { print(str_replace("\n", "\r\n", @fread($file, 1024*8))); }
if ($userAgent == "NonWindows") { print(@fread($file, 1024*8)); } if ($userAgent == "NonWindows") { print(@fread($file, 1024*8)); }
ob_flush(); ob_flush();
flush(); flush();
} }
// Ok we are done, close the file and clean up. // Ok we are done, close the file and clean up.
@fclose($file); @fclose($file);
exit; exit;
} }
else { die; } else { die; }
?>
-1
View File
@@ -1 +0,0 @@
../dstarrepeater/
@@ -0,0 +1,187 @@
<?php include_once $_SERVER['DOCUMENT_ROOT'].'/config/ircddblocal.php';
include_once $_SERVER['DOCUMENT_ROOT'].'/config/language.php'; // Translation Code
$configs = array();
if ($configfile = fopen($gatewayConfigPath,'r')) {
while ($line = fgets($configfile)) {
list($key,$value) = preg_split('/=/',$line);
$value = trim(str_replace('"','',$value));
if ($key != 'ircddbPassword' && strlen($value) > 0)
$configs[$key] = $value;
}
}
$progname = basename($_SERVER['SCRIPT_FILENAME'],".php");
$rev="20141101";
$MYCALL=strtoupper($callsign);
?>
<b><?php echo $lang['d-star_link_status'];?></b>
<table>
<tr>
<th><a class="tooltip" href="#">Radio<span><b>Radio Module</b></span></a></th>
<th><a class="tooltip" href="#">Default<span><b>Default Link Destination</b></span></a></th>
<th><a class="tooltip" href="#">Auto<span><b>AutoLink</b>- green: enabled<br />- red: disabled</span></a></th>
<th><a class="tooltip" href="#">Timer<span><b>Reset/Restart Timer</b></span></a></th>
<th><a class="tooltip" href="#">Link<span><b>Link-Status</b>- green: enabled<br />- red: disabled</span></a></th>
<th><a class="tooltip" href="#">Linked to<span><b>Linked Destination</b></span></a></th>
<th><a class="tooltip" href="#">Mode<span><b>Mode or Protocol used</b></span></a></th>
<th><a class="tooltip" href="#">Direction<span><b>Direction</b>Incoming or Outgoing</span></a></th>
<th><a class="tooltip" href="#">Last Change (<?php echo date('T')?>)<span><b>Timestamp of last change</b>Time of last change in <?php echo date('T')?> time zone</span></a></th>
</tr>
<?php
$tot = array(0=>"Never",1=>"Fixed",2=>"5min",3=>"10min",4=>"15min",5=>"20min",6=>"25min",7=>"30min",8=>"60min",9=>"90min",10=>"120min",11=>"180min",12=>"&nbsp;");
$ci = 0;
$tr = 0;
for($i = 1;$i < 5; $i++){
$param="repeaterBand" . $i;
if((isset($configs[$param])) && strlen($configs[$param]) == 1) {
$ci++;
if($ci > 1) { $ci = 0; }
print "<tr>";
$tr = 1;
$module = $configs[$param];
$rcall = sprintf("%-7.7s%-1.1s",$MYCALL,$module);
$param="repeaterCall" . $i;
if(isset($configs[$param])) { $rptrcall=sprintf("%-7.7s%-1.1s",$configs[$param],$module); } else { $rptrcall = $rcall;}
print "<td>".str_replace(' ', '&nbsp;', substr($rptrcall,0,8))."</td>";
$param="reflector" . $i;
if(isset($configs[$param])) { print "<td>".str_replace(' ', '&nbsp;', substr($configs[$param],0,8))."</td>"; } else { print "<td>&nbsp;</td>";}
$param="atStartup" . $i;
if($configs[$param] == 1){print "<td>Auto</td>"; } else { print "<td>No</td>"; }
$param="reconnect" . $i;
if(isset($configs[$param])) { $t = $configs[$param]; } else { $t = 0; }
if($t > 12){ $t = 12; }
print "<td>$tot[$t]</td>";
$j=0;
if ($linkLog = fopen($linkLogPath,'r')) {
while ($linkLine = fgets($linkLog)) {
//$statimg = "<img src=\"images/20red.png\">";
$statimg = "Down";
$linkDate = "&nbsp;";
$protocol = "&nbsp;";
$linkType = "&nbsp;";
$linkRptr = "&nbsp;";
$linkRefl = "&nbsp;";
// Reflector-Link, sample:
// 2011-09-22 02:15:06: DExtra link - Type: Repeater Rptr: DB0LJ B Refl: XRF023 A Dir: Outgoing
// 2012-10-12 17:15:45: DCS link - Type: Repeater Rptr: DB0LJ B Refl: DCS001 L Dir: Outgoing
// 2012-10-12 17:56:10: DCS link - Type: Repeater Rptr: DB0RPL B Refl: DCS015 B Dir: Outgoing
if(preg_match_all('/^(.{19}).*(D[A-Za-z]*).*Type: ([A-Za-z]*).*Rptr: (.{8}).*Refl: (.{8}).*Dir: Outgoing$/',$linkLine,$linx) > 0){
$statimg = "Up";
$linkDate = date("d-M-Y H:i:s", strtotime(substr($linx[1][0],0,19)));
$protocol = $linx[2][0];
$linkType = $linx[3][0];
$linkRptr = $linx[4][0];
$linkRefl = $linx[5][0];
if($linkRptr == $rptrcall){
print "<td>$statimg</td>";
print "<td>".str_replace(' ', '&nbsp;', substr($linkRefl,0,8))."</td>";
print "<td>$protocol</td>";
print "<td>Outgoing</td>";
$utc_time = $linkDate;
$utc_tz = new DateTimeZone('UTC');
$local_tz = new DateTimeZone(date_default_timezone_get ());
$dt = new DateTime($utc_time, $utc_tz);
$dt->setTimeZone($local_tz);
$local_time = $dt->format('H:i:s M jS');
print "<td>$local_time</td>";
print "</tr>\n";
$tr = 0;
}
}
}
fclose($linkLog);
}
if ($tr == 1){
print"<td>Down</td><td>None</td><td>--</td><td>----</td><td>----</td></tr>\n";
}
// 00000000001111111111222222222233333333334444444444555555555566666666667777777777888888888899999999990000000000111111111122
// 01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901
// 2012-05-08 21:16:31: DExtra link - Type: Repeater Rptr: DB0LJ A Refl: DB0MYK B Dir: Incoming
// 2012-05-08 21:16:31: DPlus link - Type: Dongle User: W1CDG H Dir: Incoming
if ($linkLog = fopen($linkLogPath,'r')) {
while ($linkLine = fgets($linkLog)) {
$statimg = "Down";
$linkDate = "&nbsp;";
$protocol = "&nbsp;";
$linkType = "&nbsp;";
$linkRptr = "&nbsp;";
$linkRefl = "&nbsp;";
if(preg_match_all('/^(.{19}).*(D[A-Za-z]*).*Type: ([A-Za-z]*).*Rptr: (.{8}).*Refl: (.{8}).*Dir: Incoming$/',$linkLine,$linx) > 0){
$statimg = "Up";
$linkDate = date("d-M-Y H:i:s", strtotime(substr($linx[1][0],0,19)));
$protocol = $linx[2][0];
$linkType = $linx[3][0];
$linkRptr = $linx[4][0];
$linkRefl = $linx[5][0];
if($linkRptr == $rptrcall){
$ci++;
if($ci > 1) { $ci = 0; }
print "<tr>";
print "<td>".str_replace(' ', '&nbsp;', substr($rptrcall,0,8))."</td>";
print "<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td>";
print "<td>$statimg</td>";
print "<td>".str_replace(' ', '&nbsp;', substr($linkRefl,0,8))."</td>";
print "<td>$protocol</td>";
print "<td>Incoming</td>";
$utc_time = $linkDate;
$utc_tz = new DateTimeZone('UTC');
$local_tz = new DateTimeZone(date_default_timezone_get ());
$dt = new DateTime($utc_time, $utc_tz);
$dt->setTimeZone($local_tz);
$local_time = $dt->format('H:i:s M jS');
print "<td>$local_time</td>";
print "</tr>\n";
//$tr = 0;
}
}
}
fclose($linkLog);
}
// 00000000001111111111222222222233333333334444444444555555555566666666667777777777888888888899999999990000000000111111111122
// 01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901
// 2012-05-08 21:16:31: DExtra link - Type: Repeater Rptr: DB0LJ A Refl: DB0MYK B Dir: Incoming
// 2012-05-08 21:16:31: DPlus link - Type: Dongle User: W1CDG H Dir: Incoming
if ($linkLog = fopen($linkLogPath,'r')) {
while ($linkLine = fgets($linkLog)) {
$statimg = "Down";
$linkDate = "&nbsp;";
$protocol = "&nbsp;";
$linkType = "&nbsp;";
$linkRptr = "&nbsp;";
$linkRefl = "&nbsp;";
if(preg_match_all('/^(.{19}).*(D[A-Za-z]*).*Type: ([A-Za-z]*).*User: (.[^\s]+).*Dir: Incoming$/',$linkLine,$linx) > 0){
$statimg = "Up";
$linkDate = date("d-M-Y H:i:s", strtotime(substr($linx[1][0],0,19)));
$protocol = $linx[2][0];
$linkType = $linx[3][0];
$linkRptr = $linx[4][0];
$ci++;
if($ci > 1) { $ci = 0; }
print "<tr>";
print "<td>".str_replace(' ', '&nbsp;', substr($rptrcall,0,8))."</td>";
print "<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td>";
print "<td>$statimg</td>";
print "<td>".str_replace(' ', '&nbsp;', substr($linkRptr,0,8))."</td>";
print "<td>$protocol</td>";
print "<td>Incoming</td>";
$utc_time = $linkDate;
$utc_tz = new DateTimeZone('UTC');
$local_tz = new DateTimeZone(date_default_timezone_get ());
$dt = new DateTime($utc_time, $utc_tz);
$dt->setTimeZone($local_tz);
$local_time = $dt->format('H:i:s M jS');
print "<td>$local_time</td>";
print "</tr>\n";
}
}
fclose($linkLog);
}
// End
}
}
?>
</table>
@@ -0,0 +1,125 @@
<?php include_once $_SERVER['DOCUMENT_ROOT'].'/config/ircddblocal.php';
include_once $_SERVER['DOCUMENT_ROOT'].'/config/language.php'; // Translation Code
$configs = array();
if ($configfile = fopen($gatewayConfigPath,'r')) {
while ($line = fgets($configfile)) {
list($key,$value) = preg_split('/=/',$line);
$value = trim(str_replace('"','',$value));
if ($key != 'ircddbPassword' && strlen($value) > 0)
$configs[$key] = $value;
}
}
$progname = basename($_SERVER['SCRIPT_FILENAME'],".php");
$rev="20141101";
$MYCALL=strtoupper($callsign);
?>
<b><?php echo $lang['active_starnet_groups'];?></b>
<table style="table-layout: fixed;">
<tr>
<th><a class="tooltip" href="#"><?php echo $lang['callsign'];?><span><b>Starnet Callsign</b></span></a></th>
<th><a class="tooltip" href="#"><?php echo $lang['logoff'];?><span><b>Starnet Logoff Callsign</b></span></a></th>
<th colspan="3"><a class="tooltip" href="#"><?php echo $lang['info'];?><span><b>Infotext</b></span></a></th>
<th><a class="tooltip" href="#"><?php echo $lang['utot'];?><span><b>User TimeOut (min)</b>inactivity time after which a user will be disconnected</span></a></th>
<th><a class="tooltip" href="#"><?php echo $lang['gtot'];?><span><b>Group TimeOut (min)</b>inactivity time after which the group will be disconnected</span></a></th>
</tr>
<?php
$ci = 0;
$i = 0;
$stngrp = array();
for($i = 1;$i < 6; $i++){
$param="starNetCallsign" . $i;
if(isset($configs[$param])) {
$gname = $configs[$param];
$stngrp[$gname] = $i;
$ci++;
if($ci > 1) { $ci = 0; }
print "<tr>";
print "<td align=\"center\">".str_replace(' ', '&nbsp;', substr($gname,0,8))."</td>";
$param="starNetLogoff" . $i;
if(isset($configs[$param])){ $output = str_replace(' ', '&nbsp;', substr($configs[$param],0,8)); print "<td align=\"center\">$output</td>";} else { print"<td>&nbsp;</td>";}
$param="starNetInfo" . $i;
if(isset($configs[$param])){ print "<td colspan=\"3\" align=\"left\">$configs[$param]</td>";} else { print"<td colspan=\"3\">&nbsp;</td>";}
$param="starNetUserTimeout" . $i;
if(isset($configs[$param])){ print "<td align=\"center\">$configs[$param]</td>";} else { print"<td>&nbsp;</td>";}
$param="starNetGroupTimeout" . $i;
if(isset($configs[$param])){ print "<td align=\"center\">$configs[$param]</td>";} else { print"<td>&nbsp;</td>";}
print "</tr>\n";
}
}
?>
</table><br />
<?php
$groupsx = array();
if ($starLog = fopen($starLogPath,'r')) {
while($logLine = fgets($starLog)) {
preg_match_all('/^(.{19}).*(Adding|Removing) (.{8}).*StarNet group (.{8}).*$/',$logLine,$matches);
$groupz = substr($matches[4][0],0,8);
$member = substr($matches[3][0],0,8);
$action = substr($matches[2][0],0,8);
$date = $matches[1][0];
$guid = $stngrp[$groupz];
if ($action == 'Adding'){
$groupsx[$guid][$groupz][$member] = $date;
}
elseif ($action == 'Removing'){
unset($groupsx[$guid][$groupz][$member]);
}
}
fclose($starLog);
}
//Clean the empty arrays from the multidimensional array
$groupsx = array_map('array_filter', $groupsx);
$active = 0;
for ($i = 1;$i < 6; $i++) {
if (isset($groupsx[$i])) {
$active = $active + count($groupsx[$i]);
}
}
if ($active >= 1) {
echo "<b>".$lang['active_starnet_members']."</b>\n";
echo "<table style=\"table-layout: fixed;\">\n";
echo "<tr>\n";
echo "<th><a class=tooltip href=\"#\">".$lang['time']." (".date('T').")<span><b>Time of Login</b></span></a></th>\n";
echo "<th><a class=tooltip href=\"#\">".$lang['group']."<span><b>Starnet Callsign</b></span></a></th>\n";
echo "<th><a class=tooltip href=\"#\">".$lang['callsign']."<span><b>Callsign</b></span></a></th>\n";
echo "</tr>\n";
$ci = 0;
$ulist = array();
$glist = array();
for($i = 1;$i < 6; $i++){
if(isset($groupsx[$i])){
$glist = $groupsx[$i];
foreach ($glist as $gcall => $ulist){
foreach ($ulist as $ucall => $ulogin){
$ci++;
if($ci > 1) { $ci = 0; }
$ulogin = date("d-M-Y H:i:s", strtotime(substr($ulogin,0,19)));
$utc_time = $ulogin;
$utc_tz = new DateTimeZone('UTC');
$local_tz = new DateTimeZone(date_default_timezone_get ());
$dt = new DateTime($utc_time, $utc_tz);
$dt->setTimeZone($local_tz);
$local_time = $dt->format('H:i:s M jS');
$groupz = str_replace(' ', '&nbsp;', substr($gcall,0,8));
$ucall = str_replace(' ', '', substr($ucall,0,8));
print "<tr>";
print "<td align=\"left\">$local_time</td>";
print "<td align=\"center\">$groupz</td>";
print "<td align=\"center\"><a href=\"http://www.qrz.com/db/$ucall\" target=\"_new\" alt=\"Lookup Callsign\">$ucall</a></td>";
print "</tr>\n";
}
}
}
}
echo "</table>\n<br />\n";
}
?>
+76
View File
@@ -0,0 +1,76 @@
<?php include_once $_SERVER['DOCUMENT_ROOT'].'/config/ircddblocal.php';
$configs = array();
if ($configfile = fopen($gatewayConfigPath,'r')) {
while ($line = fgets($configfile)) {
list($key,$value) = preg_split('/=/',$line);
$value = trim(str_replace('"','',$value));
if ($key != 'ircddbPassword' && strlen($value) > 0)
$configs[$key] = $value;
}
}
$progname = basename($_SERVER['SCRIPT_FILENAME'],".php");
$rev="20141101";
$MYCALL=strtoupper($callsign);
?>
<?php
if (exec('grep "CCS link" '.$linkLogPath.' | wc -l') >=1) {
?>
<b>Active CCS Connections</b>
<table>
<tr>
<th><a class=tooltip href="#">Repeater<span><b>Callsign of connected repeater</b></span></a></th>
<th><a class=tooltip href="#">Linked to<span><b>Actual link status</b></span></a></th>
<th><a class=tooltip href="#">Protocol<span><b>Protocol</b></span></a></th>
<th><a class=tooltip href="#">Direction<span><b>Direction</b>incoming or outgoing</span></a></th>
<th><a class=tooltip href="#">Last Change (<?php echo date('T')?>)<span><b>Timestamp of last change</b><?php echo date('T')?></span></a></th>
</tr>
<?php
$ci = 0;
if ($linkLog = fopen($linkLogPath,'r')) {
$i=0;
while ($linkLine = fgets($linkLog)) {
// 2013-02-27 19:49:27: CCS link - Rptr: DB0LJ B Remote: DL5DI Dir: Incoming
if(preg_match_all('/^(.{19}).*(C[A-Za-z]*).*Rptr: (.{8}).*Remote: (.{8}).*Dir: (.{8})$/',$linkLine,$linx) > 0){
$utc_time = $linx[1][0];
$utc_tz = new DateTimeZone('UTC');
$local_tz = new DateTimeZone(date_default_timezone_get ());
$dt = new DateTime($utc_time, $utc_tz);
$dt->setTimeZone($local_tz);
$local_time = $dt->format('H:i:s M jS');
$linkDate = $local_time;
$linkType = $linx[2][0];
$linkRptr = $linx[3][0];
$linkRem = $linx[4][0];
$linkDir = $linx[5][0];
$ci++;
if($ci > 1) { $ci = 0; }
print "<tr>";
print "<td>$linkRptr</td>";
print "<td>$linkRem</td>";
print "<td>CCS</td>";
print "<td>$linkDir</td>";
print "<td>$linkDate</td>";
print "</tr>\n";
}
}
fclose($linkLog);
}
print "</table>\n<br />\n";
}
$stn_is_set = 0;
for($i = 1;$i < 6; $i++){
$param="starNetCallsign" . $i;
if(isset($configs[$param])) {
$stn_is_set = 1;
break;
}
}
if($stn_is_set > 0){
include_once $_SERVER['DOCUMENT_ROOT'].'/dstarrepeater/active_starnet_groups.php';
}
?>
@@ -0,0 +1,20 @@
<div class="settings-card">
<table>
<tr><th>ircDDB Network</th><th>APRS Host</th><th>CCS</th><th>DCS</th><th>DExtra</th><th>DPlus</th><th>D-Rats</th><th>Info</th><th>ircDDB</th><th>Echo</th><th>Log</th></tr>
<tr>
<td><?php print $configs['ircddbHostname']; ?></td>
<td><?php if($configs['aprsEnabled'] == 1){ print $configs['aprsHostname']; } else { print "<img src=\"images/20red.png\">";} ?></td>
<?php
if($configs['ccsEnabled'] == 1){print "<td style=\"background-color:#1d1;\">ON</td>"; } else { print "<td style=\"background:#606060; color:#b0b0b0;\">OFF</td>"; }
if($configs['dcsEnabled'] == 1){print "<td style=\"background-color:#1d1;\">ON</td>"; } else { print "<td style=\"background:#606060; color:#b0b0b0;\">OFF</td>"; }
if($configs['dextraEnabled'] == 1){print "<td style=\"background-color:#1d1;\">ON</td>"; } else { print "<td style=\"background:#606060; color:#b0b0b0;\">OFF</td>"; }
if($configs['dplusEnabled'] == 1){print "<td style=\"background-color:#1d1;\">ON</td>"; } else { print "<td style=\"background:#606060; color:#b0b0b0;\">OFF</td>"; }
if($configs['dratsEnabled'] == 1){print "<td style=\"background-color:#1d1;\">ON</td>"; } else { print "<td style=\"background:#606060; color:#b0b0b0;\">OFF</td>"; }
if($configs['infoEnabled'] == 1){print "<td style=\"background-color:#1d1;\">ON</td>"; } else { print "<td style=\"background:#606060; color:#b0b0b0;\">OFF</td>"; }
if($configs['ircddbEnabled'] == 1){print "<td style=\"background-color:#1d1;\">ON</td>"; } else { print "<td style=\"background:#606060; color:#b0b0b0;\">OFF</td>"; }
if($configs['echoEnabled'] == 1){print "<td style=\"background-color:#1d1;\">ON</td>"; } else { print "<td style=\"background:#606060; color:#b0b0b0;\">OFF</td>"; }
if($configs['logEnabled'] == 1){print "<td style=\"background-color:#1d1;\">ON</td>"; } else { print "<td style=\"background:#606060; color:#b0b0b0;\">OFF</td>"; }
?>
</tr>
</table>
</div>
View File
+91
View File
@@ -0,0 +1,91 @@
<?php include_once $_SERVER['DOCUMENT_ROOT'].'/config/ircddblocal.php';
include_once $_SERVER['DOCUMENT_ROOT'].'/config/language.php'; // Translation Code
$configs = array();
if ($configfile = fopen($gatewayConfigPath,'r')) {
while ($line = fgets($configfile)) {
list($key,$value) = preg_split('/=/',$line);
$value = trim(str_replace('"','',$value));
if ($key != 'ircddbPassword' && strlen($value) > 0)
$configs[$key] = $value;
}
}
$progname = basename($_SERVER['SCRIPT_FILENAME'],".php");
$rev="20141101";
$MYCALL=strtoupper($callsign);
// Check if the config file exists
if (file_exists('/etc/pistar-css.ini')) {
// Use the values from the file
$piStarCssFile = '/etc/pistar-css.ini';
if (fopen($piStarCssFile,'r')) { $piStarCss = parse_ini_file($piStarCssFile, true); }
// Set the Values from the config file
if (isset($piStarCss['Lookup']['Service'])) { $callsignLookupSvc = $piStarCss['Lookup']['Service']; } // Lookup Service "QRZ" or "RadioID"
else { $callsignLookupSvc = "RadioID"; } // Set the default if its missing // Set the default if its missing
} else {
// Default values
$callsignLookupSvc = "RadioID";
}
// Safety net
if (($callsignLookupSvc != "RadioID") && ($callsignLookupSvc != "QRZ")) { $callsignLookupSvc = "RadioID"; }
// Setup the URL(s)
if ($callsignLookupSvc == "RadioID") { $callsignLookupUrl = "https://database.radioid.net/database/view?callsign="; }
if ($callsignLookupSvc == "QRZ") { $callsignLookupUrl = "https://www.qrz.com/db/"; }
?>
<b><?php echo $lang['last_heard_list'];?></b>
<table>
<tr>
<th><a class="tooltip" href="#"><?php echo $lang['time'];?> (<?php echo date('T')?>)</a></th>
<th><a class="tooltip" href="#"><?php echo $lang['callsign'];?></a></th>
<th><a class="tooltip" href="#"><?php echo $lang['target'];?></a></th>
<th><a class="tooltip" href="#">RPT 1</a></th>
<th><a class="tooltip" href="#">RPT 2</a></th>
</tr>
<?php
// Headers.log sample:
// 0000000001111111111222222222233333333334444444444555555555566666666667777777777888888888899999999990000000000111111111122
// 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901
// 2012-06-05 12:18:41: DCS header - My: PU2ZHZ /T Your: CQCQCQ Rpt1: PU2ZHZ B Rpt2: DCS007 B Flags: 00 00 00
// 2012-05-29 21:33:56: DPlus header - My: PD1RB /IC92 Your: CQCQCQ Rpt1: PE1RJV B Rpt2: REF017 A Flags: 00 00 00
// 2013-02-09 13:49:57: DExtra header - My: DO7MT / Your: CQCQCQ Rpt1: XRF001 G Rpt2: XRF001 C Flags: 00 00 00
//
exec('(grep -v " /TIME" '.$hdrLogPath.'|sort -r -k7,7|sort -u -k7,8|sort -r|head -20 >/tmp/lastheard.log) 2>&1 &');
$ci = 0;
if ($LastHeardLog = fopen("/tmp/lastheard.log",'r')) {
while ($linkLine = fgets($LastHeardLog)) {
if(preg_match_all('/^(.{19}).*My: (.*).*Your: (.*).*Rpt1: (.*).*Rpt2: (.*).*Flags: (.*)$/',$linkLine,$linx) > 0){
$ci++;
if($ci > 1) { $ci = 0; }
print "<tr>";
$QSODate = date("d-M-Y H:i:s", strtotime(substr($linx[1][0],0,19)));
$MyCall = str_replace(' ', '', substr($linx[2][0],0,8));
$MyCallLink = strtok(substr($linx[2][0],0,8), " ");
$MyId = str_replace(' ', '', substr($linx[2][0],9,4));
$YourCall = str_replace(' ', '&nbsp;', substr($linx[3][0],0,8));
$Rpt1 = str_replace(' ', '&nbsp;', substr($linx[4][0],0,8));
$Rpt2 = str_replace(' ', '&nbsp;', substr($linx[5][0],0,8));
$utc_time = $QSODate;
$utc_tz = new DateTimeZone('UTC');
$local_tz = new DateTimeZone(date_default_timezone_get ());
$dt = new DateTime($utc_time, $utc_tz);
$dt->setTimeZone($local_tz);
$local_time = $dt->format('H:i:s M jS');
print "<td align=\"left\">$local_time</td>";
print "<td align=\"left\" width=\"180\"><div style=\"float:left;\"><a href=\"".$callsignLookupUrl.$MyCallLink."\" target=\"_blank\">$MyCall</a>";
if($MyId) { print "/".$MyId."</div> <div style=\"text-align:right;\">&#40;<a href=\"https://aprs.fi/#!call=".$MyCallLink."*\" target=\"_blank\">dPRS</a>&#41;</div></td>"; }
else { print "</div> <div style=\"text-align:right;\">&#40;<a href=\"https://aprs.fi/#!call=".$MyCallLink."*\" target=\"_blank\">dPRS</a>&#41;</div></td>"; }
print "<td align=\"left\" width=\"100\">$YourCall</td>";
print "<td align=\"left\" width=\"100\">$Rpt1</td>";
print "<td align=\"left\" width=\"100\">$Rpt2</td>";
print "</tr>\n";
}
}
fclose($LastHeardLog);
}
?>
</table>
+205
View File
@@ -0,0 +1,205 @@
<?php
if ($_SERVER["PHP_SELF"] == "/admin/index.php") {
include_once $_SERVER['DOCUMENT_ROOT'].'/config/language.php'; // Translation Code
if (!empty($_POST) && isset($_POST["dstrMgrSubmit"])):
//if (!empty($_POST)):
if (preg_match('/[^A-Z]/',$_POST["Link"])) { unset ($_POST["Link"]);}
if ($_POST["Link"] == "LINK") {
if (preg_match('/[^A-Za-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 {
if (strlen($_POST["RefName"]) != 7) {
$targetRef = str_pad($_POST["RefName"], 7, " ");
} else {
$targetRef = $_POST["RefName"];
}
$targetRef = $targetRef.$_POST["Letter"];
$targetRef = strtoupper($targetRef);
$module = $_POST["Module"];
if (strlen($module) != 8) { //Fix the length of the module information
$moduleFixedCs = strlen($module) - 1; //Length of the string, -1
$moduleFixedBand = substr($module, -1); //Single Band Letter in the 8th position
$moduleFixedCallPad = str_pad(substr($module, 0, $moduleFixedCs), 7); //Pad the callsign area to 7 chars
$module = $moduleFixedCallPad.$moduleFixedBand; //Re add the band information
};
$unlinkCommand = "sudo remotecontrold \"".$module."\" unlink";
$linkCommand = "sudo remotecontrold \"".$module."\" link never \"".$targetRef."\"";
if ($module != $targetRef && $_POST["Link"] == "LINK") { // Sanity check that we are not connecting to ourself
echo "<div class=\"settings-card\">\n<b>D-Star Link Manager</b>\n";
echo "<table>\n<tr><th>Command Output</th></tr>\n<tr><td>";
echo exec($linkCommand);
echo "</td></tr>\n</table>\n</div>\n";
}
if ($module == $targetRef && $_POST["Link"] == "LINK") { // Sanity Check Failed
echo "<div class=\"settings-card\">\n<b>D-Star Link Manager</b>\n";
echo "<table>\n<tr><th>Command Output</th></tr>\n<tr><td>";
echo "Cannot link to myself - Aborting link request!";
echo "</td></tr>\n</table>\n</div>\n";
}
if ($_POST["Link"] == "UNLINK") { // Allow Unlink no matter what
echo "<div class=\"settings-card\">\n<b>D-Star Link Manager</b>\n";
echo "<table>\n<tr><th>Command Output</th></tr>\n<tr><td>";
echo exec($unlinkCommand);
echo "</td></tr>\n</table>\n</div>\n";
}
}
unset($_POST);
echo '<script type="text/javascript">setTimeout(function() { window.location=window.location;},2000);</script>';
else: ?>
<div class="settings-card">
<b><?php echo $lang['d-star_link_manager'];?></b>
<form action="//<?php echo htmlentities($_SERVER['HTTP_HOST']).htmlentities($_SERVER['PHP_SELF']); ?>" method="post">
<table>
<tr>
<th width="150"><a class="tooltip" href="#">Radio Module<span><b>Radio Module</b></span></a></th>
<th width="180"><a class="tooltip" href="#">Reflector<span><b>Reflector</b></span></a></th>
<th width="150"><a class="tooltip" href="#">Link / Un-Link<span><b>Link / Un-Link</b></span></a></th>
<th><a class="tooltip" href="#">Action<span><b>Action</b></span></a></th>
</tr>
<tr>
<td>
<select name="Module">
<?php
$ci = 0;
for($i = 1;$i < 5; $i++){
$param="repeaterBand" . $i;
if((isset($configs[$param])) && strlen($configs[$param]) == 1) {
$ci++;
if($ci > 1) { $ci = 0; }
$module = $configs[$param];
$rcall = sprintf("%-7.7s%-1.1s",$MYCALL,$module);
$param="repeaterCall" . $i;
if(isset($configs[$param])) { $rptrcall=sprintf("%-7.7s%-1.1s",$configs[$param],$module); } else { $rptrcall = $rcall;}
print "<option>$rptrcall</option>\n";
}
} ?>
</select>
</td>
<td>
<select name="RefName"
onchange="if (this.options[this.selectedIndex].value == 'customOption') {
toggleField(this,this.nextSibling);
this.selectedIndex='0';
} ">
<?php
$dcsFile = fopen("/usr/local/etc/DCS_Hosts.txt", "r");
$dplusFile = fopen("/usr/local/etc/DPlus_Hosts.txt", "r");
$dextraFile = fopen("/usr/local/etc/DExtra_Hosts.txt", "r");
echo " <option value=\"".substr($configs['reflector1'], 0, 6)."\" selected=\"selected\">".substr($configs['reflector1'], 0, 6)."</option>\n";
echo " <option value=\"customOption\">Text Entry</option>\n";
while (!feof($dcsFile)) {
$dcsLine = fgets($dcsFile);
if (strpos($dcsLine, 'DCS') !== FALSE && strpos($dcsLine, '#') === FALSE) {
echo " <option value=\"".substr($dcsLine, 0, 6)."\">".substr($dcsLine, 0, 6)."</option>\n";
}
if (strpos($dcsLine, 'XLX') !== FALSE && strpos($dcsLine, '#') === FALSE) {
echo " <option value=\"".substr($dcsLine, 0, 6)."\">".substr($dcsLine, 0, 6)."</option>\n";
}
}
fclose($dcsFile);
while (!feof($dplusFile)) {
$dplusLine = fgets($dplusFile);
if (strpos($dplusLine, 'REF') !== FALSE && strpos($dplusLine, '#') === FALSE) {
echo " <option value=\"".substr($dplusLine, 0, 6)."\">".substr($dplusLine, 0, 6)."</option>\n";
}
if (strpos($dplusLine, 'XRF') !== FALSE && strpos($dplusLine, '#') === FALSE) {
echo " <option value=\"".substr($dplusLine, 0, 6)."\">".substr($dplusLine, 0, 6)."</option>\n";
}
}
fclose($dplusFile);
while (!feof($dextraFile)) {
$dextraLine = fgets($dextraFile);
if (strpos($dextraLine, 'XRF') !== FALSE && strpos($dextraLine, '#') === FALSE)
echo " <option value=\"".substr($dextraLine, 0, 6)."\">".substr($dextraLine, 0, 6)."</option>\n";
}
fclose($dextraFile);
?>
</select><input name="RefName" style="display:none;" disabled="disabled" type="text" size="7" maxlength="7"
onblur="if(this.value==''){toggleField(this,this.previousSibling);}" />
<select name="Letter">
<?php echo " <option value=\"".substr($configs['reflector1'], 7)."\" selected=\"selected\">".substr($configs['reflector1'], 7)."</option>\n"; ?>
<option>A</option>
<option>B</option>
<option>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>
<input type="radio" name="Link" value="LINK" checked="checked" />Link
<input type="radio" name="Link" value="UNLINK" />UnLink
</td>
<td>
<input type="submit" name="dstrMgrSubmit" value="Request Change" />
</td>
</tr>
</table>
</form>
</div>
<?php endif; ?>
<?php
exec ("pgrep pistar-keeper", $pids);
if (!empty($pids))
{
echo "<br />\n";
echo "<div class=\"settings-card\">\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++;
}
echo "</table>\n";
echo "</div>\n";
}
}
?>
+89
View File
@@ -0,0 +1,89 @@
<?php include_once $_SERVER['DOCUMENT_ROOT'].'/config/ircddblocal.php';
include_once $_SERVER['DOCUMENT_ROOT'].'/config/language.php'; // Translation Code
$configs = array();
if ($configfile = fopen($gatewayConfigPath,'r')) {
while ($line = fgets($configfile)) {
list($key,$value) = preg_split('/=/',$line);
$value = trim(str_replace('"','',$value));
if ($key != 'ircddbPassword' && strlen($value) > 0)
$configs[$key] = $value;
}
}
$progname = basename($_SERVER['SCRIPT_FILENAME'],".php");
$rev="20141101";
$MYCALL=strtoupper($callsign);
// Check if the config file exists
if (file_exists('/etc/pistar-css.ini')) {
// Use the values from the file
$piStarCssFile = '/etc/pistar-css.ini';
if (fopen($piStarCssFile,'r')) { $piStarCss = parse_ini_file($piStarCssFile, true); }
// Set the Values from the config file
if (isset($piStarCss['Lookup']['Service'])) { $callsignLookupSvc = $piStarCss['Lookup']['Service']; } // Lookup Service "QRZ" or "RadioID"
else { $callsignLookupSvc = "RadioID"; } // Set the default if its missing // Set the default if its missing
} else {
// Default values
$callsignLookupSvc = "RadioID";
}
// Safety net
if (($callsignLookupSvc != "RadioID") && ($callsignLookupSvc != "QRZ")) { $callsignLookupSvc = "RadioID"; }
// Setup the URL(s)
if ($callsignLookupSvc == "RadioID") { $callsignLookupUrl = "https://database.radioid.net/database/view?callsign="; }
if ($callsignLookupSvc == "QRZ") { $callsignLookupUrl = "https://www.qrz.com/db/"; }
?>
<b><?php echo $lang['local_tx_list'];?></b>
<table>
<tr>
<th><a class="tooltip" href="#"><?php echo $lang['time'];?> (<?php echo date('T')?>)</a></th>
<th><a class="tooltip" href="#"><?php echo $lang['callsign'];?></a></th>
<th><a class="tooltip" href="#"><?php echo $lang['target'];?></a></th>
<th><a class="tooltip" href="#">RPT 1</a></th>
<th><a class="tooltip" href="#">RPT 2</a></th>
</tr>
<?php
// Headers.log sample:
// 0000000001111111111222222222233333333334444444444555555555566666666667777777777888888888899999999990000000000111111111122
// 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901
// 2012-05-29 20:31:53: Repeater header - My: PE1AGO /HANS Your: CQCQCQ Rpt1: PI1DEC B Rpt2: PI1DEC G Flags: 00 00 00
//
exec('(grep "Repeater header" '.$hdrLogPath.'|sort -r -k7,7|sort -u -k7,8|sort -r >/tmp/worked.log) 2>&1 &');
$ci = 0;
if ($WorkedLog = fopen("/tmp/worked.log",'r')) {
while ($linkLine = fgets($WorkedLog)) {
if(preg_match_all('/^(.{19}).*My: (.*).*Your: (.*).*Rpt1: (.*).*Rpt2: (.*).*Flags: (.*)$/',$linkLine,$linx) > 0){
$ci++;
if($ci > 1) { $ci = 0; }
print "<tr>";
$QSODate = date("d-M-Y H:i:s", strtotime(substr($linx[1][0],0,19)));
$MyCall = str_replace(' ', '', substr($linx[2][0],0,8));
$MyCallLink = strtok(substr($linx[2][0],0,8), " ");
$MyId = str_replace(' ', '', substr($linx[2][0],9,4));
$YourCall = str_replace(' ', '&nbsp;', substr($linx[3][0],0,8));
$Rpt1 = str_replace(' ', '&nbsp;', substr($linx[4][0],0,8));
$Rpt2 = str_replace(' ', '&nbsp;', substr($linx[5][0],0,8));
$utc_time = $QSODate;
$utc_tz = new DateTimeZone('UTC');
$local_tz = new DateTimeZone(date_default_timezone_get ());
$dt = new DateTime($utc_time, $utc_tz);
$dt->setTimeZone($local_tz);
$local_time = $dt->format('H:i:s M jS');
print "<td align=\"left\">$local_time</td>";
print "<td align=\"left\" width=\"180\"><div style=\"float:left;\"><a href=\"".$callsignLookupUrl.$MyCallLink."\" target=\"_blank\">$MyCall</a>";
if($MyId) { print "/".$MyId."</div> <div style=\"text-align:right;\">&#40;<a href=\"https://aprs.fi/#!call=".$MyCallLink."*\" target=\"_blank\">dPRS</a>&#41;</div></td>"; }
else { print "</div> <div style=\"text-align:right;\">&#40;<a href=\"https://aprs.fi/#!call=".$MyCallLink."*\" target=\"_blank\">dPRS</a>&#41;</div></td>"; }
print "<td align=\"left\" width=\"100\">$YourCall</td>";
print "<td align=\"left\" width=\"100\">$Rpt1</td>";
print "<td align=\"left\" width=\"100\">$Rpt2</td>";
print "</tr>\n";
}
}
fclose($WorkedLog);
}
?>
</table>
+56
View File
@@ -0,0 +1,56 @@
<?php include_once $_SERVER['DOCUMENT_ROOT'].'/config/ircddblocal.php';
include_once $_SERVER['DOCUMENT_ROOT'].'/config/language.php'; // Translation Code
include_once $_SERVER['DOCUMENT_ROOT'].'/mmdvmhost/tools.php';
$configs = array();
if ($configfile = fopen($gatewayConfigPath,'r')) {
while ($line = fgets($configfile)) {
list($key,$value) = preg_split('/=/',$line);
$value = trim(str_replace('"','',$value));
if ($key != 'ircddbPassword' && strlen($value) > 0)
$configs[$key] = $value;
}
}
$progname = basename($_SERVER['SCRIPT_FILENAME'],".php");
$rev="20141101";
$MYCALL=strtoupper($callsign);
?>
<?php
$cpuLoad = sys_getloadavg();
$cpuTempCRaw = floatval(exec('cat /sys/class/thermal/thermal_zone0/temp'));
if ($cpuTempCRaw > 1000) { $cpuTempC = round($cpuTempCRaw / 1000, 1); } else { $cpuTempC = round($cpuTempCRaw, 1); }
$cpuTempF = round(+$cpuTempC * 9 / 5 + 32, 1);
if ($cpuTempC < 50) { $cpuTempHTML = "<td style=\"background: #1d1\">".$cpuTempC."&deg;C / ".$cpuTempF."&deg;F</td>\n"; }
if ($cpuTempC >= 50) { $cpuTempHTML = "<td style=\"background: #fa0\">".$cpuTempC."&deg;C / ".$cpuTempF."&deg;F</td>\n"; }
if ($cpuTempC >= 69) { $cpuTempHTML = "<td style=\"background: #f00\">".$cpuTempC."&deg;C / ".$cpuTempF."&deg;F</td>\n"; }
?>
<b><?php echo $lang['hardware_info'];?></b>
<table style="table-layout: fixed;">
<tr>
<th><a class="tooltip" href="#"><?php echo $lang['hostname'];?><br /><span><b>System IP Address:<br /><?php echo str_replace(',', ',<br />', exec('hostname -I'));?></b></span></a></th>
<th><a class="tooltip" href="#"><?php echo $lang['kernel'];?><span><b>Release</b></span></a></th>
<th colspan="2"><a class="tooltip" href="#"><?php echo $lang['platform'];?><span><b>Uptime:<br /><?php echo str_replace(',', ',<br />', exec('uptime -p'));?></b></span></a></th>
<th><a class="tooltip" href="#"><?php echo $lang['cpu_load'];?><span><b>CPU Load</b></span></a></th>
<th><a class="tooltip" href="#"><?php echo $lang['cpu_temp'];?><span><b>CPU Temp</b></span></a></th>
</tr>
<tr>
<td><?php if (strlen(php_uname('n')) >= 16) { echo substr(php_uname('n'), 0, 14) . '..'; } else { echo php_uname('n'); } ?></td>
<td><?php echo php_uname('r');?></td>
<td colspan="2"><?php echo exec('/usr/local/bin/platformDetect.sh');?></td>
<td><?php echo number_format($cpuLoad[0],2);?> / <?php echo number_format($cpuLoad[1],2);?> / <?php echo number_format($cpuLoad[2],2);?></td>
<?php echo $cpuTempHTML; ?>
</tr>
<tr>
<th colspan="6"><?php echo $lang['service_status'];?></th>
</tr>
<tr>
<td style="background: #<?php if (isProcessRunning('MMDVMHost')) { echo "1d1"; } else { echo "b55"; } ?>">MMDVMHost</td>
<td style="background: #<?php if (isProcessRunning('dstarrepeaterd')) { echo "1d1"; } else { echo "b55"; } ?>">DStarRepeater</td>
<td style="background: #<?php if (isProcessRunning('ircddbgatewayd')) { echo "1d1"; } else { echo "b55"; } ?>">ircDDBGateway</td>
<td style="background: #<?php if (isProcessRunning('timeserverd')) { echo "1d1"; } else { echo "b55"; } ?>">TimeServer</td>
<td style="background: #<?php if (isProcessRunning('/usr/local/sbin/pistar-watchdog',true)) { echo "1d1"; } else { echo "b55"; } ?>">PiStar-Watchdog</td>
<td style="background: #<?php if (isProcessRunning('/usr/local/sbin/pistar-remote',true)) { echo "1d1"; } else { echo "b55"; } ?>">PiStar-Remote</td>
</tr>
</table>
<br />
+56 -116
View File
@@ -1,38 +1,4 @@
<?php <?php
/**
* Expert INI editor for /etc/dapnetgateway.
*
* Renders a per-section / per-key form built from parse_ini_file()
* output, accepts edits via POST, then writes the result back to
* /etc/dapnetgateway using the standard Pi-Star copy-via-/tmp pattern:
* 1. sudo cp /etc/dapnetgateway /tmp/<obfuscated>.tmp + chown www-data
* + chmod 600 (so PHP can edit the temp).
* 2. fopen('w') and fwrite the rebuilt INI text into the temp.
* 3. sudo mount -o remount,rw / + sudo install -m 644 -o root -g
* root temp -> /etc/dapnetgateway + sudo mount -o remount,ro / to
* seal the rootfs again. (See edit_mmdvmhost.php for the full
* rationale on the install vs cp+chmod+chown migration.)
* 4. sudo systemctl restart dapnetgateway.service to pick up the change.
*
* Admin-only access; the dashboard's Apache basic-auth gate is the
* sole protection. The validation is what's in the form (none, in
* effect — operator-typed values are written raw). Treat with care.
*/
require_once($_SERVER['DOCUMENT_ROOT'].'/config/security_headers.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/csrf.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/banner_warnings.inc');
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 rejects forged POSTs cleanly with 403
// before any state change (sed-i, fopen+fwrite, sudo cp, etc.).
csrf_verify();
// Layer 2 of the default-password protection — see config/banner_warnings.inc.
// MUST run BEFORE any output so header('Location: ...') works.
pistar_warnings_enforce_redirect();
// Load the language support // Load the language support
require_once('../config/language.php'); require_once('../config/language.php');
//Load the Pi-Star Release file //Load the Pi-Star Release file
@@ -51,123 +17,97 @@ require_once('../config/version.php');
<meta name="language" content="English" /> <meta name="language" content="English" />
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<meta name="Author" content="Andrew Taylor (MW0MWZ)" /> <meta name="Author" content="Andrew Taylor (MW0MWZ)" />
<meta name="Description" content="Pi-Star Expert Editor" /> <meta name="Description" content="CDN Expert Editor" />
<meta name="KeyWords" content="Pi-Star" /> <meta name="KeyWords" content="CDN" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="pragma" content="no-cache" /> <meta http-equiv="pragma" content="no-cache" />
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon"> <link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon">
<meta http-equiv="Expires" content="0" /> <meta http-equiv="Expires" content="0" />
<title>Pi-Star - Digital Voice Dashboard - Expert Editor</title> <title>CDN - Digital Voice Dashboard - Expert Editor</title>
<link rel="stylesheet" type="text/css" href="../css/pistar-css.php" /> <link rel="stylesheet" type="text/css" href="../css/pistar-css.php" />
</head> </head>
<body> <body>
<?php pistar_warnings_render(); ?>
<div class="container"> <div class="container">
<?php include './header-menu.inc'; ?> <?php include './header-menu.inc'; ?>
<div class="contentwide"> <div class="contentwide">
<?php <?php
// Do some file wrangling... // Do some file wrangling...
// A3-3 — see edit_ircddbgateway.php for the full TOCTOU rationale. exec('sudo cp /etc/dapnetgateway /tmp/oXyEVXYSisDX.tmp');
$filepath = tempnam('/tmp', 'pistar-edit-'); exec('sudo chown www-data:www-data /tmp/oXyEVXYSisDX.tmp');
exec('sudo cp /etc/dapnetgateway ' . escapeshellarg($filepath)); exec('sudo chmod 664 /tmp/oXyEVXYSisDX.tmp');
exec('sudo chown www-data:www-data ' . escapeshellarg($filepath));
exec('sudo chmod 600 ' . escapeshellarg($filepath)); // ini file to open
// Clean up the /tmp staging file on script exit so the $filepath = '/tmp/oXyEVXYSisDX.tmp';
// editor's potentially-secrets-bearing copy of /etc/<config>
// doesn't persist between requests. @-suppression handles
// the case where a sudo mv (e.g. fulledit_bmapikey) already
// consumed the staging file before script end.
register_shutdown_function(function() use ($filepath) { @unlink($filepath); });
// after the form submit // after the form submit
if($_POST) { if($_POST) {
$data = $_POST; $data = $_POST;
//update ini file, call function //update ini file, call function
update_ini_file($data, $filepath); update_ini_file($data, $filepath);
} }
// this is the function going to update your ini file // this is the function going to update your ini file
function update_ini_file($data, $filepath) function update_ini_file($data, $filepath) {
{ $content = "";
$content = "";
// parse the ini file to get the sections // parse the ini file to get the sections
// parse the ini file using default parse_ini_file() PHP function // parse the ini file using default parse_ini_file() PHP function
$parsed_ini = parse_ini_file($filepath, true); $parsed_ini = parse_ini_file($filepath, true);
foreach($data as $section=>$values) { foreach($data as $section=>$values) {
// UnBreak special cases // UnBreak special cases
$section = str_replace("_", " ", $section); $section = str_replace("_", " ", $section);
$content .= "[".$section."]\n"; $content .= "[".$section."]\n";
//append the values //append the values
foreach($values as $key=>$value) { foreach($values as $key=>$value) {
$content .= $key."=".$value."\n"; $content .= $key."=".$value."\n";
} }
$content .= "\n"; $content .= "\n";
} }
// write it into file // write it into file
if (!$handle = fopen($filepath, 'w')) { if (!$handle = fopen($filepath, 'w')) {
return false; return false;
} }
$success = fwrite($handle, $content); $success = fwrite($handle, $content);
fclose($handle); fclose($handle);
// L-5: atomic install replaces the prior cp + chmod + chown // Updates complete - copy the working file back to the proper location
// triplet (rejected by the tightened sudoers — see exec('sudo mount -o remount,rw /'); // Make rootfs writable
// edit_mmdvmhost.php for the full rationale). exec('sudo cp /tmp/oXyEVXYSisDX.tmp /etc/dapnetgateway'); // Move the file back
exec('sudo mount -o remount,rw /'); exec('sudo chmod 644 /etc/dapnetgateway'); // Set the correct runtime permissions
exec('sudo install -m 644 -o root -g root ' exec('sudo chown root:root /etc/dapnetgateway'); // Set the owner
. escapeshellarg($filepath) . ' /etc/dapnetgateway'); exec('sudo mount -o remount,ro /'); // Make rootfs read-only
exec('sudo mount -o remount,ro /');
// Reload the affected daemon // Reload the affected daemon
exec('sudo systemctl restart dapnetgateway.service'); // Reload the daemon exec('sudo systemctl restart dapnetgateway.service'); // Reload the daemon
return $success; return $success;
} }
// parse the ini file using default parse_ini_file() PHP function // parse the ini file using default parse_ini_file() PHP function
$parsed_ini = parse_ini_file($filepath, true); $parsed_ini = parse_ini_file($filepath, true);
echo '<form action="" method="post">'."\n"; echo '<form action="" method="post">'."\n";
echo csrf_field_html()."\n"; foreach($parsed_ini as $section=>$values) {
foreach($parsed_ini as $section=>$values) { // keep the section as hidden text so we can update once the form submitted
// keep the section as hidden text so we can update once the form submitted echo "<input type=\"hidden\" value=\"$section\" name=\"$section\" />\n";
// INI section / key / value all come from the underlying echo "<div class=\"settings-card\">\n";
// /etc/<gateway> file. Same hardening as edit_mmdvmhost.php echo "<h3>$section</h3>\n";
// (#23): htmlspecialchars(ENT_QUOTES) on display so a value // print all other values as input fields, so can edit.
// with a literal `"` or `<` (e.g. an Options string) can't // note the name='' attribute it has both section and key
// break out of the `value="…"` attribute. The save handler foreach($values as $key=>$value) {
// writes the POST bytes verbatim, so legitimate quoted echo "<div class=\"field-row\"><div>$key</div><div><input type=\"text\" name=\"{$section}[$key]\" value=\"$value\" /></div></div>\n";
// values round-trip byte-identically. }
$sectionHtml = htmlspecialchars((string)$section, ENT_QUOTES, 'UTF-8'); echo "</div>\n";
echo "<input type=\"hidden\" value=\"$sectionHtml\" name=\"$sectionHtml\" />\n"; echo '<div class="field-actions"><input type="submit" value="'.$lang['apply'].'" /></div>'."\n";
echo "<table>\n"; }
echo "<tr><th colspan=\"2\">$sectionHtml</th></tr>\n";
// print all other values as input fields, so can edit.
// note the name='' attribute it has both section and key
foreach($values as $key=>$value) {
$keyHtml = htmlspecialchars((string)$key, ENT_QUOTES, 'UTF-8');
$valueHtml = htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
echo "<tr><td align=\"right\" width=\"30%\">$keyHtml</td><td align=\"left\"><input type=\"text\" name=\"{$sectionHtml}[$keyHtml]\" value=\"$valueHtml\" /></td></tr>\n";
}
echo "</table>\n";
echo '<input type="submit" value="'.$lang['apply'].'" />'."\n";
echo "<br />\n";
}
echo "</form>"; echo "</form>";
?> ?>
</div> </div>
<div class="footer">
Pi-Star / Pi-Star Dashboard, &copy; Andy Taylor (MW0MWZ) 2014-<?php echo date("Y"); ?>.<br />
Need help? Click <a style="color: #ffffff;" href="https://www.facebook.com/groups/pistarusergroup/" target="_new">here for the Support Group</a><br />
Get your copy of Pi-Star from <a style="color: #ffffff;" href="http://www.pistar.uk/downloads/" target="_new">here</a>.<br />
</div>
</div> </div>
</body> </body>
</html> </html>
+118 -210
View File
@@ -1,32 +1,4 @@
<?php <?php
/**
* Expert editor for /etc/pistar-css.ini — the dashboard theme overrides.
*
* Same staged-write pattern as the other edit_*.php files except no
* daemon restart is needed (CSS is loaded fresh on the next page hit).
* Provides a 'Reset to defaults' path that does a `sudo rm -rf` on
* /etc/pistar-css.ini inside the mount-rw window — guarded only by a
* JS confirm() prompt; flag for the security pass.
*
* Output of this editor is consumed by css/pistar-css.php,
* css/pistar-css-mini.php, and admin/wifi/styles.php — the three CSS
* emitters that read from /etc/pistar-css.ini.
*/
require_once($_SERVER['DOCUMENT_ROOT'].'/config/security_headers.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/csrf.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/banner_warnings.inc');
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 rejects forged POSTs cleanly with 403
// before any state change (sed-i, fopen+fwrite, sudo cp, etc.).
csrf_verify();
// Layer 2 of the default-password protection — see config/banner_warnings.inc.
// MUST run BEFORE any output so header('Location: ...') works.
pistar_warnings_enforce_redirect();
// Load the language support // Load the language support
require_once('../config/language.php'); require_once('../config/language.php');
//Load the Pi-Star Release file //Load the Pi-Star Release file
@@ -45,166 +17,125 @@ require_once('../config/version.php');
<meta name="language" content="English" /> <meta name="language" content="English" />
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<meta name="Author" content="Andrew Taylor (MW0MWZ)" /> <meta name="Author" content="Andrew Taylor (MW0MWZ)" />
<meta name="Description" content="Pi-Star Expert Editor" /> <meta name="Description" content="CDN Expert Editor" />
<meta name="KeyWords" content="Pi-Star" /> <meta name="KeyWords" content="CDN" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="pragma" content="no-cache" /> <meta http-equiv="pragma" content="no-cache" />
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon"> <link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon">
<meta http-equiv="Expires" content="0" /> <meta http-equiv="Expires" content="0" />
<title>Pi-Star - Digital Voice Dashboard - Expert Editor</title> <title>CDN - Digital Voice Dashboard - Expert Editor</title>
<link rel="stylesheet" type="text/css" href="../css/pistar-css.php" /> <link rel="stylesheet" type="text/css" href="../css/pistar-css.php" />
<script type="text/javascript"> <script type="text/javascript">
function factoryReset() function factoryReset()
{ {
// Typed confirmation. The server-side handler requires if (confirm('WARNING: This will set these settings back to factory defaults.\n\nAre you SURE you want to do this?\n\nPress OK to restore the factory configuration\nPress Cancel to go back.')) {
// factoryResetConfirm === 'RESET' before performing the document.getElementById("factoryReset").submit();
// wipe — so a misclicked button or an accidental form } else {
// replay does NOT reset the dashboard CSS, even though return false;
// the CSRF token is otherwise valid. }
var typed = prompt( }
'WARNING: This will reset these settings to factory defaults.\n\n'
+ 'Type RESET (uppercase) and press OK to proceed.\n'
+ 'Press Cancel to go back.', '');
if (typed === null) { return false; } // Cancel
if (typed !== 'RESET') {
alert('Confirmation text did not match. Factory reset cancelled.');
return false;
}
document.getElementById('factoryResetConfirmInput').value = typed;
document.getElementById('factoryReset').submit();
}
</script> </script>
</head> </head>
<body> <body>
<?php pistar_warnings_render(); ?>
<div class="container"> <div class="container">
<?php include './header-menu.inc'; ?> <?php include './header-menu.inc'; ?>
<div class="contentwide"> <div class="contentwide">
<?php <?php
// A3-3 — see edit_ircddbgateway.php for the full TOCTOU rationale.
// tempnam() up front so both the factory-init branch (when
// /etc/pistar-css.ini doesn't exist yet) and the normal-load
// branch use the same per-request random staging path.
$filepath = tempnam('/tmp', 'pistar-edit-');
register_shutdown_function(function() use ($filepath) { @unlink($filepath); });
if (!file_exists('/etc/pistar-css.ini')) { if (!file_exists('/etc/pistar-css.ini')) {
//The source file does not exist, lets create it.... //The source file does not exist, lets create it....
$outFile = fopen($filepath, "w") or die("Unable to open file!"); $outFile = fopen("/tmp/bW1kd4jg6b3N0DQo.tmp", "w") or die("Unable to open file!");
$fileContent = "[Background]\nPage=edf0f5\nContent=ffffff\nBanners=dd4b39\n\n"; $fileContent = "[Background]\nPage=f3f4f8\nContent=ffffff\nBanners=4f46e5\n\n";
$fileContent .= "[Text]\nBanners=ffffff\nBannersDrop=303030\n\n"; $fileContent .= "[Text]\nBanners=ffffff\nBannersDrop=1e1b4b\n\n";
$fileContent .= "[Tables]\nHeadDrop=8b0000\nBgEven=f7f7f7\nBgOdd=d0d0d0\n\n"; $fileContent .= "[Tables]\nHeadDrop=3730a3\nBgEven=f8fafc\nBgOdd=eef0f5\n\n";
$fileContent .= "[Content]\nText=000000\n\n"; $fileContent .= "[Content]\nText=111827\n\n";
$fileContent .= "[BannerH1]\nEnabled=0\nText=\"Some Text\"\n\n"; $fileContent .= "[BannerH1]\nEnabled=0\nText=\"Some Text\"\n\n";
$fileContent .= "[BannerExtText]\nEnabled=0\nText=\"Some long text entry\"\n\n"; $fileContent .= "[BannerExtText]\nEnabled=0\nText=\"Some long text entry\"\n\n";
$fileContent .= "[Lookup]\nService=\"RadioID\"\n"; $fileContent .= "[Lookup]\nService=\"RadioID\"\n";
fwrite($outFile, $fileContent); fwrite($outFile, $fileContent);
fclose($outFile); fclose($outFile);
// Atomic install: content + mode + owner set in one syscall // Put the file back where it should be
// sequence. Replaces the prior cp + chmod + chown trio so an exec('sudo mount -o remount,rw /'); // Make rootfs writable
// interrupted RW window can't leave /etc/pistar-css.ini at the exec('sudo cp /tmp/bW1kd4jg6b3N0DQo.tmp /etc/pistar-css.ini'); // Move the file back
// staging file's www-data:www-data 600. The dashboard's CSS exec('sudo chmod 644 /etc/pistar-css.ini'); // Set the correct runtime permissions
// renderer (/css/pistar-css.php) reads it via parse_ini_file exec('sudo chown root:root /etc/pistar-css.ini'); // Set the owner
// without sudo, so 644 root:root preserves the read path. exec('sudo mount -o remount,ro /'); // Make rootfs read-only
exec('sudo mount -o remount,rw /');
exec('sudo install -m 644 -o root -g root '
. escapeshellarg($filepath) . ' /etc/pistar-css.ini');
exec('sudo mount -o remount,ro /');
} }
//Do some file wrangling... //Do some file wrangling...
exec('sudo cp /etc/pistar-css.ini ' . escapeshellarg($filepath)); exec('sudo cp /etc/pistar-css.ini /tmp/bW1kd4jg6b3N0DQo.tmp');
exec('sudo chown www-data:www-data ' . escapeshellarg($filepath)); exec('sudo chown www-data:www-data /tmp/bW1kd4jg6b3N0DQo.tmp');
exec('sudo chmod 600 ' . escapeshellarg($filepath)); exec('sudo chmod 664 /tmp/bW1kd4jg6b3N0DQo.tmp');
//ini file to open
$filepath = '/tmp/bW1kd4jg6b3N0DQo.tmp';
//after the form submit //after the form submit
if($_POST) { if($_POST) {
$data = $_POST; $data = $_POST;
// Factory Reset Handler Here // Factory Reset Handler Here
if (empty($_POST['factoryReset']) != TRUE ) { if (empty($_POST['factoryReset']) != TRUE ) {
// Server-side confirmation gate. The form ships a hidden echo "<br />\n";
// factoryResetConfirm input that the JS factoryReset() echo "<div class=\"settings-card\">\n";
// populates only after the operator types `RESET` into echo "<h3>Factory Reset Config</h3>\n";
// the prompt. Comparing strictly to the magic string echo "<div>Loading fresh configuration file(s)...</div>\n";
// (===) means a misclicked button, a replayed POST, or echo "</div>\n";
// a curl with just `factoryReset=1` does NOT trigger the unset($_POST);
// wipe — even with a valid CSRF token. //Reset the config
$confirm = isset($_POST['factoryResetConfirm']) ? $_POST['factoryResetConfirm'] : ''; exec('sudo mount -o remount,rw /'); // Make rootfs writable
if ($confirm !== 'RESET') { exec('sudo rm -rf /etc/pistar-css.ini'); // Delete the Config
echo "<br />\n"; exec('sudo mount -o remount,ro /'); // Make rootfs read-only
echo "<table>\n"; echo '<script type="text/javascript">setTimeout(function() { window.location=window.location;},0);</script>';
echo "<tr><th>Factory Reset NOT performed</th></tr>\n"; die();
echo "<tr><td>Server-side confirmation did not match. Factory reset cancelled.</td><tr>\n"; } else {
echo "</table>\n"; //update ini file, call function
unset($_POST); update_ini_file($data, $filepath);
} else { }
echo "<br />\n";
echo "<table>\n";
echo "<tr><th>Factory Reset Config</th></tr>\n";
echo "<tr><td>Loading fresh configuration file(s)...</td><tr>\n";
echo "</table>\n";
unset($_POST);
//Reset the config
exec('sudo mount -o remount,rw /'); // Make rootfs writable
exec('sudo rm -rf /etc/pistar-css.ini'); // Delete the Config
exec('sudo mount -o remount,ro /'); // Make rootfs read-only
echo '<script type="text/javascript">setTimeout(function() { window.location=window.location;},0);</script>';
die();
}
} else {
//update ini file, call function
update_ini_file($data, $filepath);
}
} }
//this is the function going to update your ini file //this is the function going to update your ini file
function update_ini_file($data, $filepath) function update_ini_file($data, $filepath) {
{ $content = "";
$content = "";
//parse the ini file to get the sections //parse the ini file to get the sections
//parse the ini file using default parse_ini_file() PHP function //parse the ini file using default parse_ini_file() PHP function
$parsed_ini = parse_ini_file($filepath, true); $parsed_ini = parse_ini_file($filepath, true);
foreach($data as $section=>$values) { foreach($data as $section=>$values) {
// UnBreak special cases // UnBreak special cases
$section = str_replace("_", " ", $section); $section = str_replace("_", " ", $section);
$section = str_replace("BannerH2", "BannerH1", $section); $section = str_replace("BannerH2", "BannerH1", $section);
$content .= "[".$section."]\n"; $content .= "[".$section."]\n";
//append the values //append the values
foreach($values as $key=>$value) { foreach($values as $key=>$value) {
if ($value == '') { if ($value == '') {
$content .= $key."=none\n"; $content .= $key."=none\n";
} }
else { else {
$content .= $key."=".$value."\n"; $content .= $key."=".$value."\n";
} }
} }
$content .= "\n"; $content .= "\n";
} }
//write it into file //write it into file
if (!$handle = fopen($filepath, 'w')) { if (!$handle = fopen($filepath, 'w')) {
return false; return false;
} }
$success = fwrite($handle, $content); $success = fwrite($handle, $content);
fclose($handle); fclose($handle);
// Atomic install — see the matching block earlier in this // Updates complete - copy the working file back to the proper location
// file for the rationale. Single sudo call, content + mode exec('sudo mount -o remount,rw /'); // Make rootfs writable
// + owner all set together; no transient state on disk. exec('sudo cp /tmp/bW1kd4jg6b3N0DQo.tmp /etc/pistar-css.ini'); // Move the file back
// $filepath here is the function parameter, which is the exec('sudo chmod 644 /etc/pistar-css.ini'); // Set the correct runtime permissions
// same per-request tempnam path created at file-top (A3-3). exec('sudo chown root:root /etc/pistar-css.ini'); // Set the owner
exec('sudo mount -o remount,rw /'); exec('sudo mount -o remount,ro /'); // Make rootfs read-only
exec('sudo install -m 644 -o root -g root '
. escapeshellarg($filepath) . ' /etc/pistar-css.ini');
exec('sudo mount -o remount,ro /');
return $success; return $success;
} }
//parse the ini file using default parse_ini_file() PHP function //parse the ini file using default parse_ini_file() PHP function
$parsed_ini = parse_ini_file($filepath, true); $parsed_ini = parse_ini_file($filepath, true);
@@ -212,70 +143,47 @@ if (isset($parsed_ini['Lookup']['popupWidth'])) { unset($parsed_ini['Lookup']['
if (isset($parsed_ini['Lookup']['popupHeight'])) { unset($parsed_ini['Lookup']['popupHeight']); } if (isset($parsed_ini['Lookup']['popupHeight'])) { unset($parsed_ini['Lookup']['popupHeight']); }
echo '<form action="" method="post">'."\n"; echo '<form action="" method="post">'."\n";
echo csrf_field_html()."\n"; foreach($parsed_ini as $section=>$values) {
foreach($parsed_ini as $section=>$values) { // keep the section as hidden text so we can update once the form submitted
// Same hardening as edit_mmdvmhost.php (#23): escape every echo "<input type=\"hidden\" value=\"$section\" name=\"$section\" />\n";
// INI section / key / value before HTML interpolation. The echo "<div class=\"settings-card\">\n";
// save handler writes POST bytes verbatim so legitimate echo "<h3>$section</h3>\n";
// values (including any with `"` or `<`) round-trip // print all other values as input fields, so can edit.
// byte-identically. // note the name='' attribute it has both section and key
$sectionHtml = htmlspecialchars((string)$section, ENT_QUOTES, 'UTF-8'); foreach($values as $key=>$value) {
// keep the section as hidden text so we can update once the form submitted if ( $section == "Lookup" && $key == "Service" ) {
echo "<input type=\"hidden\" value=\"$sectionHtml\" name=\"$sectionHtml\" />\n"; echo "<div class=\"field-row\"><div>$key</div><div>\n";
echo "<table>\n"; echo " <select name=\"{$section}[$key]\" />\n";
echo "<tr><th colspan=\"2\">$sectionHtml</th></tr>\n"; if ($value == "RadioID") {
// print all other values as input fields, so can edit. echo " <option value=\"RadioID\" selected=\"selected\">RadioID Callsign Lookup</option>\n";
// note the name='' attribute it has both section and key } else {
foreach($values as $key=>$value) { echo " <option value=\"RadioID\">RadioID Callsign Lookup</option>\n";
$keyHtml = htmlspecialchars((string)$key, ENT_QUOTES, 'UTF-8'); }
$valueHtml = htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8'); if ($value == "QRZ") {
if ( $section == "Lookup" && $key == "Service" ) { echo " <option value=\"QRZ\" selected=\"selected\">QRZ Callsign Lookup</option>\n";
echo "<tr><td align=\"right\" width=\"30%\">$keyHtml</td><td align=\"left\">\n"; } else {
echo " <select name=\"{$sectionHtml}[$keyHtml]\" />\n"; echo " <option value=\"QRZ\">QRZ Callsign Lookup</option>\n";
if ($value == "RadioID") { }
echo " <option value=\"RadioID\" selected=\"selected\">RadioID Callsign Lookup</option>\n"; echo " </select>\n";
} else { echo "</div></div>\n";
echo " <option value=\"RadioID\">RadioID Callsign Lookup</option>\n"; } else {
} echo "<div class=\"field-row\"><div>$key</div><div><input type=\"text\" name=\"{$section}[$key]\" value=\"$value\" /></div></div>\n";
if ($value == "QRZ") { }
echo " <option value=\"QRZ\" selected=\"selected\">QRZ Callsign Lookup</option>\n"; }
} else { echo "</div>\n";
echo " <option value=\"QRZ\">QRZ Callsign Lookup</option>\n"; echo '<div class="field-actions"><input type="submit" value="'.$lang['apply'].'" /></div>'."\n";
} }
echo " </select>\n";
echo "</td></tr>\n";
} else {
echo "<tr><td align=\"right\" width=\"30%\">$keyHtml</td><td align=\"left\"><input type=\"text\" name=\"{$sectionHtml}[$keyHtml]\" value=\"$valueHtml\" /></td></tr>\n";
}
}
echo "</table>\n";
echo '<input type="submit" value="'.$lang['apply'].'" />'."\n";
echo "<br /><br />\n";
}
echo "</form>"; echo "</form>";
echo "<br />\n"; echo "<br />\n";
echo 'if you took it all too far and now it makes you feel sick, click below to reset the changes made on this page, this will ONLY reset the CSS settings above and will not change any other settings or configuration.'."\n"; echo 'if you took it all too far and now it makes you feel sick, click below to reset the changes made on this page, this will ONLY reset the CSS settings above and will not change any other settings or configuration.'."\n";
echo '<form id="factoryReset" action="" method="post">'."\n"; echo '<form id="factoryReset" action="" method="post">'."\n";
echo csrf_field_html()."\n";
echo ' <div><input type="hidden" name="factoryReset" value="1" /></div>'."\n"; echo ' <div><input type="hidden" name="factoryReset" value="1" /></div>'."\n";
// Server-side confirmation. JS factoryReset() prompts for the
// magic word and only populates this input on a match. The
// handler requires factoryResetConfirm === 'RESET' before doing
// the wipe — closes the "valid CSRF token + accidental replay"
// attack class.
echo ' <div><input type="hidden" id="factoryResetConfirmInput" name="factoryResetConfirm" value="" /></div>'."\n";
echo '</form>'."\n"; echo '</form>'."\n";
echo '<input type="button" onclick="javascript:factoryReset();" value="'.$lang['factory_reset'].'" />'."\n"; echo '<input type="button" onclick="javascript:factoryReset();" value="'.$lang['factory_reset'].'" />'."\n";
?> ?>
</div> </div>
<div class="footer">
Pi-Star / Pi-Star Dashboard, &copy; Andy Taylor (MW0MWZ) 2014-<?php echo date("Y"); ?>.<br />
Need help? Click <a style="color: #ffffff;" href="https://www.facebook.com/groups/pistarusergroup/" target="_new">here for the Support Group</a><br />
Get your copy of Pi-Star from <a style="color: #ffffff;" href="http://www.pistar.uk/downloads/" target="_new">here</a>.<br />
</div>
</div> </div>
</body> </body>
</html> </html>
+64 -124
View File
@@ -1,38 +1,4 @@
<?php <?php
/**
* Expert INI editor for /etc/dmrgateway.
*
* Renders a per-section / per-key form built from parse_ini_file()
* output, accepts edits via POST, then writes the result back to
* /etc/dmrgateway using the standard Pi-Star copy-via-/tmp pattern:
* 1. sudo cp /etc/dmrgateway /tmp/<obfuscated>.tmp + chown www-data
* + chmod 600 (so PHP can edit the temp).
* 2. fopen('w') and fwrite the rebuilt INI text into the temp.
* 3. sudo mount -o remount,rw / + sudo install -m 644 -o root -g
* root temp -> /etc/dmrgateway + sudo mount -o remount,ro / to
* seal the rootfs again. (See edit_mmdvmhost.php for the full
* rationale on the install vs cp+chmod+chown migration.)
* 4. sudo systemctl restart dmrgateway.service to pick up the change.
*
* Admin-only access; the dashboard's Apache basic-auth gate is the
* sole protection. The validation is what's in the form (none, in
* effect — operator-typed values are written raw). Treat with care.
*/
require_once($_SERVER['DOCUMENT_ROOT'].'/config/security_headers.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/csrf.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/banner_warnings.inc');
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 rejects forged POSTs cleanly with 403
// before any state change (sed-i, fopen+fwrite, sudo cp, etc.).
csrf_verify();
// Layer 2 of the default-password protection — see config/banner_warnings.inc.
// MUST run BEFORE any output so header('Location: ...') works.
pistar_warnings_enforce_redirect();
// Load the language support // Load the language support
require_once('../config/language.php'); require_once('../config/language.php');
//Load the Pi-Star Release file //Load the Pi-Star Release file
@@ -51,131 +17,105 @@ require_once('../config/version.php');
<meta name="language" content="English" /> <meta name="language" content="English" />
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<meta name="Author" content="Andrew Taylor (MW0MWZ)" /> <meta name="Author" content="Andrew Taylor (MW0MWZ)" />
<meta name="Description" content="Pi-Star Expert Editor" /> <meta name="Description" content="CDN Expert Editor" />
<meta name="KeyWords" content="Pi-Star" /> <meta name="KeyWords" content="CDN" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="pragma" content="no-cache" /> <meta http-equiv="pragma" content="no-cache" />
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon"> <link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon">
<meta http-equiv="Expires" content="0" /> <meta http-equiv="Expires" content="0" />
<title>Pi-Star - Digital Voice Dashboard - Expert Editor</title> <title>CDN - Digital Voice Dashboard - Expert Editor</title>
<link rel="stylesheet" type="text/css" href="../css/pistar-css.php" /> <link rel="stylesheet" type="text/css" href="../css/pistar-css.php" />
</head> </head>
<body> <body>
<?php pistar_warnings_render(); ?>
<div class="container"> <div class="container">
<?php include './header-menu.inc'; ?> <?php include './header-menu.inc'; ?>
<div class="contentwide"> <div class="contentwide">
<?php <?php
//Do some file wrangling... //Do some file wrangling...
// A3-3 — see edit_ircddbgateway.php for the full TOCTOU rationale. exec('sudo cp /etc/dmrgateway /tmp/fmehg65694eg.tmp');
$filepath = tempnam('/tmp', 'pistar-edit-'); exec('sudo chown www-data:www-data /tmp/fmehg65694eg.tmp');
exec('sudo cp /etc/dmrgateway ' . escapeshellarg($filepath)); exec('sudo chmod 664 /tmp/fmehg65694eg.tmp');
exec('sudo chown www-data:www-data ' . escapeshellarg($filepath));
exec('sudo chmod 600 ' . escapeshellarg($filepath)); //ini file to open
// Clean up the /tmp staging file on script exit so the $filepath = '/tmp/fmehg65694eg.tmp';
// editor's potentially-secrets-bearing copy of /etc/<config>
// doesn't persist between requests. @-suppression handles
// the case where a sudo mv (e.g. fulledit_bmapikey) already
// consumed the staging file before script end.
register_shutdown_function(function() use ($filepath) { @unlink($filepath); });
//after the form submit //after the form submit
if($_POST) { if($_POST) {
$data = $_POST; $data = $_POST;
//update ini file, call function //update ini file, call function
update_ini_file($data, $filepath); update_ini_file($data, $filepath);
} }
//this is the function going to update your ini file //this is the function going to update your ini file
function update_ini_file($data, $filepath) function update_ini_file($data, $filepath) {
{ $content = "";
$content = "";
//parse the ini file to get the sections //parse the ini file to get the sections
//parse the ini file using default parse_ini_file() PHP function //parse the ini file using default parse_ini_file() PHP function
$parsed_ini = parse_ini_file($filepath, true); $parsed_ini = parse_ini_file($filepath, true);
foreach($data as $section=>$values) { foreach($data as $section=>$values) {
// UnBreak special cases // UnBreak special cases
$section = str_replace("_", " ", $section); $section = str_replace("_", " ", $section);
$content .= "[".$section."]\n"; $content .= "[".$section."]\n";
//append the values //append the values
foreach($values as $key=>$value) { foreach($values as $key=>$value) {
if (($section == "DMR Network 1" || $section == "DMR Network 2") && $key == "Password" && $value) { if (($section == "DMR Network 1" || $section == "DMR Network 2") && $key == "Password" && $value) {
$value = str_replace('"', "", $value); $value = str_replace('"', "", $value);
$content .= $key."=\"".$value."\"\n"; $content .= $key."=\"".$value."\"\n";
} elseif (($section == "DMR Network 1" || $section == "DMR Network 2") && $key == "Options" && $value) { } elseif (($section == "DMR Network 1" || $section == "DMR Network 2") && $key == "Options" && $value) {
$value = str_replace('"', "", $value); $value = str_replace('"', "", $value);
$content .= $key."=\"".$value."\"\n"; $content .= $key."=\"".$value."\"\n";
} else { } else {
$content .= $key."=".$value."\n"; $content .= $key."=".$value."\n";
} }
} }
$content .= "\n"; $content .= "\n";
} }
//write it into file //write it into file
if (!$handle = fopen($filepath, 'w')) { if (!$handle = fopen($filepath, 'w')) {
return false; return false;
} }
$success = fwrite($handle, $content); $success = fwrite($handle, $content);
fclose($handle); fclose($handle);
// L-5: atomic install replaces the prior cp + chmod + chown // Updates complete - copy the working file back to the proper location
// triplet (rejected by the tightened sudoers — see exec('sudo mount -o remount,rw /'); // Make rootfs writable
// edit_mmdvmhost.php for the full rationale). exec('sudo cp /tmp/fmehg65694eg.tmp /etc/dmrgateway'); // Move the file back
exec('sudo mount -o remount,rw /'); exec('sudo chmod 644 /etc/dmrgateway'); // Set the correct runtime permissions
exec('sudo install -m 644 -o root -g root ' exec('sudo chown root:root /etc/dmrgateway'); // Set the owner
. escapeshellarg($filepath) . ' /etc/dmrgateway'); exec('sudo mount -o remount,ro /'); // Make rootfs read-only
exec('sudo mount -o remount,ro /');
// Reload the affected daemon // Reload the affected daemon
exec('sudo systemctl restart dmrgateway.service'); // Reload the daemon exec('sudo systemctl restart dmrgateway.service'); // Reload the daemon
return $success; return $success;
} }
//parse the ini file using default parse_ini_file() PHP function //parse the ini file using default parse_ini_file() PHP function
$parsed_ini = parse_ini_file($filepath, true); $parsed_ini = parse_ini_file($filepath, true);
echo '<form action="" method="post">'."\n"; echo '<form action="" method="post">'."\n";
echo csrf_field_html()."\n"; foreach($parsed_ini as $section=>$values) {
foreach($parsed_ini as $section=>$values) { // keep the section as hidden text so we can update once the form submitted
// keep the section as hidden text so we can update once the form submitted echo "<input type=\"hidden\" value=\"$section\" name=\"$section\" />\n";
// INI section / key / value all come from the underlying echo "<div class=\"settings-card\">\n";
// /etc/<gateway> file. Same hardening as edit_mmdvmhost.php echo "<h3>$section</h3>\n";
// (#23): htmlspecialchars(ENT_QUOTES) on display so a value // print all other values as input fields, so can edit.
// with a literal `"` or `<` (e.g. an Options string) can't // note the name='' attribute it has both section and key
// break out of the `value="…"` attribute. The save handler foreach($values as $key=>$value) {
// writes the POST bytes verbatim, so legitimate quoted echo "<div class=\"field-row\"><div>$key</div><div><input type=\"text\" name=\"{$section}[$key]\" value=\"$value\" /></div></div>\n";
// values round-trip byte-identically. }
$sectionHtml = htmlspecialchars((string)$section, ENT_QUOTES, 'UTF-8'); echo "</div>\n";
echo "<input type=\"hidden\" value=\"$sectionHtml\" name=\"$sectionHtml\" />\n"; echo '<div class="field-actions"><input type="submit" value="'.$lang['apply'].'" /></div>'."\n";
echo "<table>\n"; }
echo "<tr><th colspan=\"2\">$sectionHtml</th></tr>\n";
// print all other values as input fields, so can edit.
// note the name='' attribute it has both section and key
foreach($values as $key=>$value) {
$keyHtml = htmlspecialchars((string)$key, ENT_QUOTES, 'UTF-8');
$valueHtml = htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
echo "<tr><td align=\"right\" width=\"30%\">$keyHtml</td><td align=\"left\"><input type=\"text\" name=\"{$sectionHtml}[$keyHtml]\" value=\"$valueHtml\" /></td></tr>\n";
}
echo "</table>\n";
echo '<input type="submit" value="'.$lang['apply'].'" />'."\n";
echo "<br />\n";
}
echo "</form>"; echo "</form>";
?> ?>
</div> </div>
<div class="footer">
Pi-Star / Pi-Star Dashboard, &copy; Andy Taylor (MW0MWZ) 2014-<?php echo date("Y"); ?>.<br />
Need help? Click <a style="color: #ffffff;" href="https://www.facebook.com/groups/pistarusergroup/" target="_new">here for the Support Group</a><br />
Get your copy of Pi-Star from <a style="color: #ffffff;" href="http://www.pistar.uk/downloads/" target="_new">here</a>.<br />
</div>
</div> </div>
</body> </body>
</html> </html>
+52 -111
View File
@@ -1,29 +1,4 @@
<?php <?php
/**
* Expert editor for /etc/dstarrepeater (D-Star modem driver config).
*
* The on-disk file is a flat key=value (no [section] header), but the
* editor parses it via parse_ini_file() — so we synthesise a temporary
* `[dstarrepeater]` header on read and `sed -i` it back out before the
* file is committed. Otherwise follows the standard Pi-Star
* copy-via-/tmp / mount-rw / restart pattern; daemon:
* dstarrepeater.service.
*/
require_once($_SERVER['DOCUMENT_ROOT'].'/config/security_headers.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/csrf.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/banner_warnings.inc');
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 rejects forged POSTs cleanly with 403
// before any state change (sed-i, fopen+fwrite, sudo cp, etc.).
csrf_verify();
// Layer 2 of the default-password protection — see config/banner_warnings.inc.
// MUST run BEFORE any output so header('Location: ...') works.
pistar_warnings_enforce_redirect();
// Load the language support // Load the language support
require_once('../config/language.php'); require_once('../config/language.php');
//Load the Pi-Star Release file //Load the Pi-Star Release file
@@ -42,34 +17,28 @@ require_once('../config/version.php');
<meta name="language" content="English" /> <meta name="language" content="English" />
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<meta name="Author" content="Andrew Taylor (MW0MWZ)" /> <meta name="Author" content="Andrew Taylor (MW0MWZ)" />
<meta name="Description" content="Pi-Star Expert Editor" /> <meta name="Description" content="CDN Expert Editor" />
<meta name="KeyWords" content="Pi-Star" /> <meta name="KeyWords" content="CDN" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="pragma" content="no-cache" /> <meta http-equiv="pragma" content="no-cache" />
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon"> <link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon">
<meta http-equiv="Expires" content="0" /> <meta http-equiv="Expires" content="0" />
<title>Pi-Star - Digital Voice Dashboard - Expert Editor</title> <title>CDN - Digital Voice Dashboard - Expert Editor</title>
<link rel="stylesheet" type="text/css" href="../css/pistar-css.php" /> <link rel="stylesheet" type="text/css" href="../css/pistar-css.php" />
</head> </head>
<body> <body>
<?php pistar_warnings_render(); ?>
<div class="container"> <div class="container">
<?php include './header-menu.inc'; ?> <?php include './header-menu.inc'; ?>
<div class="contentwide"> <div class="contentwide">
<?php <?php
// Do some file wrangling... // Do some file wrangling...
// A3-3 — see edit_ircddbgateway.php for the full TOCTOU rationale. exec('sudo cp /etc/dstarrepeater /tmp/ZHN0YXJyZXBlYXRlcg.tmp');
// tempnam() creates the staging file mode 600 owned by www-data exec('sudo chown www-data:www-data /tmp/ZHN0YXJyZXBlYXRlcg.tmp');
// with an unguessable random suffix; cleanup is registered up exec('sudo chmod 664 /tmp/ZHN0YXJyZXBlYXRlcg.tmp');
// front so the file never persists past script exit.
$filepath = tempnam('/tmp', 'pistar-edit-'); // ini file to open
register_shutdown_function(function() use ($filepath) { @unlink($filepath); }); $filepath = '/tmp/ZHN0YXJyZXBlYXRlcg.tmp';
exec('sudo cp /etc/dstarrepeater ' . escapeshellarg($filepath));
// Defensive re-assert; tempnam already 600 www-data and `sudo cp`
// preserves it, but match the surrounding pattern as belt-and-braces.
exec('sudo chown www-data:www-data ' . escapeshellarg($filepath));
exec('sudo chmod 600 ' . escapeshellarg($filepath));
// Mangle the input // Mangle the input
$file_content = "[dstarrepeater]\n".preg_replace('~\r\n?~', "\n", file_get_contents($filepath)); $file_content = "[dstarrepeater]\n".preg_replace('~\r\n?~', "\n", file_get_contents($filepath));
@@ -77,24 +46,23 @@ file_put_contents($filepath, $file_content);
// after the form submit // after the form submit
if($_POST) { if($_POST) {
$data = $_POST; $data = $_POST;
//update ini file, call function //update ini file, call function
update_ini_file($data, $filepath); update_ini_file($data, $filepath);
} }
// this is the function going to update your ini file // this is the function going to update your ini file
function update_ini_file($data, $filepath) function update_ini_file($data, $filepath) {
{ $content = "";
$content = "";
// parse the ini file to get the sections // parse the ini file to get the sections
// parse the ini file using default parse_ini_file() PHP function // parse the ini file using default parse_ini_file() PHP function
$parsed_ini = parse_ini_file($filepath, true); $parsed_ini = parse_ini_file($filepath, true);
foreach($data as $section=>$values) { foreach($data as $section=>$values) {
// UnBreak special cases // UnBreak special cases
$section = str_replace("_", " ", $section); $section = str_replace("_", " ", $section);
$content .= "[".$section."]\n"; $content .= "[".$section."]\n";
//append the values //append the values
foreach($values as $key=>$value) { foreach($values as $key=>$value) {
if ($value == '') { if ($value == '') {
@@ -104,78 +72,51 @@ if($_POST) {
$content .= $key."=".$value."\n"; $content .= $key."=".$value."\n";
} }
} }
} }
// write it into file // write it into file
if (!$handle = fopen($filepath, 'w')) { if (!$handle = fopen($filepath, 'w')) {
return false; return false;
} }
$success = fwrite($handle, $content); $success = fwrite($handle, $content);
fclose($handle); fclose($handle);
// /etc/dstarrepeater is a FLAT key=value file on disk — the // Updates complete - copy the working file back to the proper location
// synthetic [dstarrepeater] header is injected only for exec('sudo mount -o remount,rw /'); // Make rootfs writable
// parse_ini_file()'s benefit. Strip it via PHP and install exec('sudo cp /tmp/ZHN0YXJyZXBlYXRlcg.tmp /etc/dstarrepeater'); // Move the file back
// the cleaned content directly (L-7: drops sudo sed -i; exec('sudo sed -i \'/\\[dstarrepeater\\]/d\' /etc/dstarrepeater'); // Clean up file mangling
// L-5: collapses cp + chmod + chown into one atomic install). exec('sudo chmod 644 /etc/dstarrepeater'); // Set the correct runtime permissions
// See edit_ircddbgateway.php for the full rationale. exec('sudo chown root:root /etc/dstarrepeater'); // Set the owner
$etcContent = preg_replace('/^\[dstarrepeater\]\r?\n/m', '', $content); exec('sudo mount -o remount,ro /'); // Make rootfs read-only
// A3-3: per-request random staging — see edit_ircddbgateway.php
$etcStaging = tempnam('/tmp', 'pistar-edit-etc-');
file_put_contents($etcStaging, $etcContent);
exec('sudo mount -o remount,rw /'); // Reload the affected daemon
exec('sudo install -m 644 -o root -g root ' exec('sudo systemctl restart dstarrepeater.service'); // Reload the daemon
. escapeshellarg($etcStaging) . ' /etc/dstarrepeater'); return $success;
exec('sudo mount -o remount,ro /'); }
@unlink($etcStaging);
// Reload the affected daemon
exec('sudo systemctl restart dstarrepeater.service'); // Reload the daemon
return $success;
}
// parse the ini file using default parse_ini_file() PHP function // parse the ini file using default parse_ini_file() PHP function
$parsed_ini = parse_ini_file($filepath, true); $parsed_ini = parse_ini_file($filepath, true);
echo '<form action="" method="post">'."\n"; echo '<form action="" method="post">'."\n";
echo csrf_field_html()."\n"; foreach($parsed_ini as $section=>$values) {
foreach($parsed_ini as $section=>$values) { // keep the section as hidden text so we can update once the form submitted
// keep the section as hidden text so we can update once the form submitted echo "<input type=\"hidden\" value=\"$section\" name=\"$section\" />\n";
// INI section / key / value all come from the underlying echo "<div class=\"settings-card\">\n";
// /etc/<gateway> file. Same hardening as edit_mmdvmhost.php echo "<h3>$section</h3>\n";
// (#23): htmlspecialchars(ENT_QUOTES) on display so a value // print all other values as input fields, so can edit.
// with a literal `"` or `<` (e.g. an Options string) can't // note the name='' attribute it has both section and key
// break out of the `value="…"` attribute. The save handler foreach($values as $key=>$value) {
// writes the POST bytes verbatim, so legitimate quoted echo "<div class=\"field-row\"><div>$key</div><div><input type=\"text\" name=\"{$section}[$key]\" value=\"$value\" /></div></div>\n";
// values round-trip byte-identically. }
$sectionHtml = htmlspecialchars((string)$section, ENT_QUOTES, 'UTF-8'); echo "</div>\n";
echo "<input type=\"hidden\" value=\"$sectionHtml\" name=\"$sectionHtml\" />\n"; echo '<div class="field-actions"><input type="submit" value="'.$lang['apply'].'" /></div>'."\n";
echo "<table>\n"; }
echo "<tr><th colspan=\"2\">$sectionHtml</th></tr>\n";
// print all other values as input fields, so can edit.
// note the name='' attribute it has both section and key
foreach($values as $key=>$value) {
$keyHtml = htmlspecialchars((string)$key, ENT_QUOTES, 'UTF-8');
$valueHtml = htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
echo "<tr><td align=\"right\" width=\"30%\">$keyHtml</td><td align=\"left\"><input type=\"text\" name=\"{$sectionHtml}[$keyHtml]\" value=\"$valueHtml\" /></td></tr>\n";
}
echo "</table>\n";
echo '<input type="submit" value="'.$lang['apply'].'" />'."\n";
echo "<br />\n";
}
echo "</form>"; echo "</form>";
?> ?>
</div> </div>
<div class="footer">
Pi-Star / Pi-Star Dashboard, &copy; Andy Taylor (MW0MWZ) 2014-<?php echo date("Y"); ?>.<br />
Need help? Click <a style="color: #ffffff;" href="https://www.facebook.com/groups/pistarusergroup/" target="_new">here for the Support Group</a><br />
Get your copy of Pi-Star from <a style="color: #ffffff;" href="http://www.pistar.uk/downloads/" target="_new">here</a>.<br />
</div>
</div> </div>
</body> </body>
</html> </html>
+59 -134
View File
@@ -1,28 +1,4 @@
<?php <?php
/**
* Expert editor for /etc/ircddbgateway (D-Star side gateway config).
*
* Same flat key=value file as /etc/dstarrepeater — uses the synthetic
* `[ircddbgateway]` section header trick on read and sed-strip on
* write so parse_ini_file() can handle it. Standard Pi-Star
* copy-via-/tmp / mount-rw / restart pattern; daemon:
* ircddbgateway.service.
*/
require_once($_SERVER['DOCUMENT_ROOT'].'/config/security_headers.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/csrf.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/banner_warnings.inc');
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 rejects forged POSTs cleanly with 403
// before any state change (sed-i, fopen+fwrite, sudo cp, etc.).
csrf_verify();
// Layer 2 of the default-password protection — see config/banner_warnings.inc.
// MUST run BEFORE any output so header('Location: ...') works.
pistar_warnings_enforce_redirect();
// Load the language support // Load the language support
require_once('../config/language.php'); require_once('../config/language.php');
//Load the Pi-Star Release file //Load the Pi-Star Release file
@@ -41,41 +17,28 @@ require_once('../config/version.php');
<meta name="language" content="English" /> <meta name="language" content="English" />
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<meta name="Author" content="Andrew Taylor (MW0MWZ)" /> <meta name="Author" content="Andrew Taylor (MW0MWZ)" />
<meta name="Description" content="Pi-Star Expert Editor" /> <meta name="Description" content="CDN Expert Editor" />
<meta name="KeyWords" content="Pi-Star" /> <meta name="KeyWords" content="CDN" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="pragma" content="no-cache" /> <meta http-equiv="pragma" content="no-cache" />
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon"> <link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon">
<meta http-equiv="Expires" content="0" /> <meta http-equiv="Expires" content="0" />
<title>Pi-Star - Digital Voice Dashboard - Expert Editor</title> <title>CDN - Digital Voice Dashboard - Expert Editor</title>
<link rel="stylesheet" type="text/css" href="../css/pistar-css.php" /> <link rel="stylesheet" type="text/css" href="../css/pistar-css.php" />
</head> </head>
<body> <body>
<?php pistar_warnings_render(); ?>
<div class="container"> <div class="container">
<?php include './header-menu.inc'; ?> <?php include './header-menu.inc'; ?>
<div class="contentwide"> <div class="contentwide">
<?php <?php
// Stage a copy of /etc/ircddbgateway in /tmp under a random, // Do some file wrangling...
// per-request name (A3-3). tempnam() creates the file mode 600 exec('sudo cp /etc/ircddbgateway /tmp/aXJjZGRiZ2F0ZXdheQ.tmp');
// owned by the calling PHP-FPM user (www-data); the unguessable exec('sudo chown www-data:www-data /tmp/aXJjZGRiZ2F0ZXdheQ.tmp');
// suffix defeats the predictable-name TOCTOU class — an attacker exec('sudo chmod 664 /tmp/aXJjZGRiZ2F0ZXdheQ.tmp');
// who knew the path could otherwise pre-create it as a symlink
// to /etc/shadow or similar and have our `sudo cp` follow the // ini file to open
// link and overwrite the target. Cleanup is registered up front $filepath = '/tmp/aXJjZGRiZ2F0ZXdheQ.tmp';
// so the staging copy never persists past script exit, even on a
// die() / fatal-error path. @-suppression handles the case where
// a sudo mv path consumed the file before script end.
$filepath = tempnam('/tmp', 'pistar-edit-');
register_shutdown_function(function() use ($filepath) { @unlink($filepath); });
exec('sudo cp /etc/ircddbgateway ' . escapeshellarg($filepath));
// Defensively re-assert mode + owner. tempnam already created
// the file 600 www-data, and `sudo cp` against an existing
// regular file truncates-in-place (mode/owner preserved); these
// remain as belt-and-braces to match the surrounding pattern.
exec('sudo chown www-data:www-data ' . escapeshellarg($filepath));
exec('sudo chmod 600 ' . escapeshellarg($filepath));
// Mangle the input // Mangle the input
$file_content = "[ircddbgateway]\n".preg_replace('~\r\n?~', "\n", file_get_contents($filepath)); $file_content = "[ircddbgateway]\n".preg_replace('~\r\n?~', "\n", file_get_contents($filepath));
@@ -83,115 +46,77 @@ file_put_contents($filepath, $file_content);
// after the form submit // after the form submit
if($_POST) { if($_POST) {
$data = $_POST; $data = $_POST;
//update ini file, call function //update ini file, call function
update_ini_file($data, $filepath); update_ini_file($data, $filepath);
} }
// this is the function going to update your ini file // this is the function going to update your ini file
function update_ini_file($data, $filepath) function update_ini_file($data, $filepath) {
{ $content = "";
$content = "";
// parse the ini file to get the sections // parse the ini file to get the sections
// parse the ini file using default parse_ini_file() PHP function // parse the ini file using default parse_ini_file() PHP function
$parsed_ini = parse_ini_file($filepath, true); $parsed_ini = parse_ini_file($filepath, true);
foreach($data as $section=>$values) { foreach($data as $section=>$values) {
// UnBreak special cases // UnBreak special cases
$section = str_replace("_", " ", $section); $section = str_replace("_", " ", $section);
$content .= "[".$section."]\n"; $content .= "[".$section."]\n";
//append the values //append the values
foreach($values as $key=>$value) { foreach($values as $key=>$value) {
if ($value == '') { if ($value == '') {
$content .= $key."= \n"; $content .= $key."= \n";
} }
else { else {
$content .= $key."=".$value."\n"; $content .= $key."=".$value."\n";
} }
} }
} }
// write it into file // write it into file
if (!$handle = fopen($filepath, 'w')) { if (!$handle = fopen($filepath, 'w')) {
return false; return false;
} }
$success = fwrite($handle, $content); $success = fwrite($handle, $content);
fclose($handle); fclose($handle);
// /etc/ircddbgateway is a FLAT key=value file on disk — the // Updates complete - copy the working file back to the proper location
// synthetic [ircddbgateway] header was injected at line ~75 exec('sudo mount -o remount,rw /'); // Make rootfs writable
// only to satisfy parse_ini_file()'s section model. The /tmp exec('sudo cp /tmp/aXJjZGRiZ2F0ZXdheQ.tmp /etc/ircddbgateway'); // Move the file back
// staging file keeps the header (so the form re-render via exec('sudo sed -i \'/\\[ircddbgateway\\]/d\' /etc/ircddbgateway'); // Clean up file mangling
// parse_ini_file at the bottom of this script still finds exec('sudo chmod 644 /etc/ircddbgateway'); // Set the correct runtime permissions
// sections); the on-disk version must not. Strip via PHP's exec('sudo chown root:root /etc/ircddbgateway'); // Set the owner
// preg_replace and install the cleaned content directly — exec('sudo mount -o remount,ro /'); // Make rootfs read-only
// drops the prior `sudo sed -i` from this code path (L-7).
$etcContent = preg_replace('/^\[ircddbgateway\]\r?\n/m', '', $content);
// A3-3: per-request random staging file rather than a
// predictable hardcoded /tmp/<obf>.tmp path. tempnam() also
// creates the file mode 600 — and since this is a freshly
// created file (not yet referenced anywhere), there's no
// race for an attacker to swap in a symlink before our write.
$etcStaging = tempnam('/tmp', 'pistar-edit-etc-');
file_put_contents($etcStaging, $etcContent);
// Atomic install: content + mode + owner set in one syscall // Reload the affected daemon
// sequence (B5 / L-5 pattern). Replaces the prior cp + exec('sudo systemctl restart ircddbgateway.service'); // Reload the daemon
// chmod + chown trio so an interrupted RW window can't leave return $success;
// /etc/ircddbgateway in a transient state. }
exec('sudo mount -o remount,rw /');
exec('sudo install -m 644 -o root -g root '
. escapeshellarg($etcStaging) . ' /etc/ircddbgateway');
exec('sudo mount -o remount,ro /');
@unlink($etcStaging);
// Reload the affected daemon
exec('sudo systemctl restart ircddbgateway.service'); // Reload the daemon
return $success;
}
// parse the ini file using default parse_ini_file() PHP function // parse the ini file using default parse_ini_file() PHP function
$parsed_ini = parse_ini_file($filepath, true); $parsed_ini = parse_ini_file($filepath, true);
echo '<form action="" method="post">'."\n"; echo '<form action="" method="post">'."\n";
echo csrf_field_html()."\n"; foreach($parsed_ini as $section=>$values) {
foreach($parsed_ini as $section=>$values) { // keep the section as hidden text so we can update once the form submitted
// keep the section as hidden text so we can update once the form submitted echo "<input type=\"hidden\" value=\"$section\" name=\"$section\" />\n";
// INI section / key / value all come from the underlying echo "<div class=\"settings-card\">\n";
// /etc/<gateway> file. Same hardening as edit_mmdvmhost.php echo "<h3>$section</h3>\n";
// (#23): htmlspecialchars(ENT_QUOTES) on display so a value // print all other values as input fields, so can edit.
// with a literal `"` or `<` (e.g. an Options string) can't // note the name='' attribute it has both section and key
// break out of the `value="…"` attribute. The save handler foreach($values as $key=>$value) {
// writes the POST bytes verbatim, so legitimate quoted echo "<div class=\"field-row\"><div>$key</div><div><input type=\"text\" name=\"{$section}[$key]\" value=\"$value\" /></div></div>\n";
// values round-trip byte-identically. }
$sectionHtml = htmlspecialchars((string)$section, ENT_QUOTES, 'UTF-8'); echo "</div>\n";
echo "<input type=\"hidden\" value=\"$sectionHtml\" name=\"$sectionHtml\" />\n"; echo '<div class="field-actions"><input type="submit" value="'.$lang['apply'].'" /></div>'."\n";
echo "<table>\n"; }
echo "<tr><th colspan=\"2\">$sectionHtml</th></tr>\n";
// print all other values as input fields, so can edit.
// note the name='' attribute it has both section and key
foreach($values as $key=>$value) {
$keyHtml = htmlspecialchars((string)$key, ENT_QUOTES, 'UTF-8');
$valueHtml = htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
echo "<tr><td align=\"right\" width=\"30%\">$keyHtml</td><td align=\"left\"><input type=\"text\" name=\"{$sectionHtml}[$keyHtml]\" value=\"$valueHtml\" /></td></tr>\n";
}
echo "</table>\n";
echo '<input type="submit" value="'.$lang['apply'].'" />'."\n";
echo "<br />\n";
}
echo "</form>"; echo "</form>";
?> ?>
</div> </div>
<div class="footer">
Pi-Star / Pi-Star Dashboard, &copy; Andy Taylor (MW0MWZ) 2014-<?php echo date("Y"); ?>.<br />
Need help? Click <a style="color: #ffffff;" href="https://www.facebook.com/groups/pistarusergroup/" target="_new">here for the Support Group</a><br />
Get your copy of Pi-Star from <a style="color: #ffffff;" href="http://www.pistar.uk/downloads/" target="_new">here</a>.<br />
</div>
</div> </div>
</body> </body>
</html> </html>
+78 -157
View File
@@ -1,42 +1,4 @@
<?php <?php
/**
* Expert INI editor for /etc/mmdvmhost.
*
* Renders a per-section / per-key form built from parse_ini_file()
* output, accepts edits via POST, then writes the result back to
* /etc/mmdvmhost using the standard Pi-Star copy-via-/tmp pattern:
* 1. sudo cp /etc/mmdvmhost /tmp/<obfuscated>.tmp + chown www-data
* + chmod 600 (so PHP can edit the temp).
* 2. fopen('w') and fwrite the rebuilt INI text into the temp.
* 3. sudo mount -o remount,rw / + sudo install -m 644 -o root -g
* root temp -> /etc/mmdvmhost + sudo mount -o remount,ro / to
* seal the rootfs again. (L-5: collapses the prior cp + chmod +
* chown triplet into one atomic call — same idiom used by
* configure.php's gateway-save block. The triplet would also
* be rejected by the tightened /etc/sudoers.d/pistar-dashboard
* because it allowlists install but not bare cp+chmod+chown
* against /etc/<gateway>.)
* 4. sudo systemctl restart mmdvmhost.service to pick up the change.
*
* Admin-only access; the dashboard's Apache basic-auth gate is the
* sole protection. The validation is what's in the form (none, in
* effect — operator-typed values are written raw). Treat with care.
*/
require_once($_SERVER['DOCUMENT_ROOT'].'/config/security_headers.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/csrf.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/banner_warnings.inc');
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 rejects forged POSTs cleanly with 403
// before any state change (sed-i, fopen+fwrite, sudo cp, etc.).
csrf_verify();
// Layer 2 of the default-password protection — see config/banner_warnings.inc.
// MUST run BEFORE any output so header('Location: ...') works.
pistar_warnings_enforce_redirect();
// Load the language support // Load the language support
require_once('../config/language.php'); require_once('../config/language.php');
//Load the Pi-Star Release file //Load the Pi-Star Release file
@@ -55,162 +17,121 @@ require_once('../config/version.php');
<meta name="language" content="English" /> <meta name="language" content="English" />
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<meta name="Author" content="Andrew Taylor (MW0MWZ)" /> <meta name="Author" content="Andrew Taylor (MW0MWZ)" />
<meta name="Description" content="Pi-Star Expert Editor" /> <meta name="Description" content="CDN Expert Editor" />
<meta name="KeyWords" content="Pi-Star" /> <meta name="KeyWords" content="CDN" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="pragma" content="no-cache" /> <meta http-equiv="pragma" content="no-cache" />
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon"> <link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon">
<meta http-equiv="Expires" content="0" /> <meta http-equiv="Expires" content="0" />
<title>Pi-Star - Digital Voice Dashboard - Expert Editor</title> <title>CDN - Digital Voice Dashboard - Expert Editor</title>
<link rel="stylesheet" type="text/css" href="../css/pistar-css.php" /> <link rel="stylesheet" type="text/css" href="../css/pistar-css.php" />
</head> </head>
<body> <body>
<?php pistar_warnings_render(); ?>
<div class="container"> <div class="container">
<?php include './header-menu.inc'; ?> <?php include './header-menu.inc'; ?>
<div class="contentwide"> <div class="contentwide">
<?php <?php
//Do some file wrangling... //Do some file wrangling...
// A3-3 — see edit_ircddbgateway.php for the full TOCTOU rationale. exec('sudo cp /etc/mmdvmhost /tmp/bW1kdm1ob3N0DQo.tmp');
// tempnam() creates the staging file mode 600 owned by www-data exec('sudo chown www-data:www-data /tmp/bW1kdm1ob3N0DQo.tmp');
// with an unguessable random suffix. exec('sudo chmod 664 /tmp/bW1kdm1ob3N0DQo.tmp');
$filepath = tempnam('/tmp', 'pistar-edit-');
exec('sudo cp /etc/mmdvmhost ' . escapeshellarg($filepath)); //ini file to open
exec('sudo chown www-data:www-data ' . escapeshellarg($filepath)); $filepath = '/tmp/bW1kdm1ob3N0DQo.tmp';
exec('sudo chmod 600 ' . escapeshellarg($filepath));
// Clean up the /tmp staging file on script exit so the
// editor's potentially-secrets-bearing copy of /etc/<config>
// doesn't persist between requests. @-suppression handles
// the case where a sudo mv (e.g. fulledit_bmapikey) already
// consumed the staging file before script end.
register_shutdown_function(function() use ($filepath) { @unlink($filepath); });
//after the form submit //after the form submit
if($_POST) { if($_POST) {
$data = $_POST; $data = $_POST;
//update ini file, call function //update ini file, call function
update_ini_file($data, $filepath); update_ini_file($data, $filepath);
} }
//this is the function going to update your ini file //this is the function going to update your ini file
function update_ini_file($data, $filepath) function update_ini_file($data, $filepath) {
{ $content = "";
$content = "";
//parse the ini file to get the sections //parse the ini file to get the sections
//parse the ini file using default parse_ini_file() PHP function //parse the ini file using default parse_ini_file() PHP function
$parsed_ini = parse_ini_file($filepath, true); $parsed_ini = parse_ini_file($filepath, true);
foreach($data as $section=>$values) { foreach($data as $section=>$values) {
// UnBreak special cases // UnBreak special cases
$section = str_replace("_", " ", $section); $section = str_replace("_", " ", $section);
$content .= "[".$section."]\n"; $content .= "[".$section."]\n";
//append the values //append the values
foreach($values as $key=>$value) { foreach($values as $key=>$value) {
if ($section == "DMR Network" && $key == "Password" && $value) { if ($section == "DMR Network" && $key == "Password" && $value) {
$value = str_replace('"', "", $value); $value = str_replace('"', "", $value);
$content .= $key."=\"".$value."\"\n"; $content .= $key."=\"".$value."\"\n";
} }
elseif ($section == "DMR Network" && $key == "Options" && $value) { elseif ($section == "DMR Network" && $key == "Options" && $value) {
$value = str_replace('"', "", $value); $value = str_replace('"', "", $value);
$content .= $key."=\"".$value."\"\n"; $content .= $key."=\"".$value."\"\n";
} }
elseif ($section == "DMR Network" && $key == "Options" && !$value) { elseif ($section == "DMR Network" && $key == "Options" && !$value) {
$content .= $key."= \n"; $content .= $key."= \n";
} }
elseif ($value == '') { elseif ($value == '') {
$content .= $key."=none\n"; $content .= $key."=none\n";
} }
else { else {
$content .= $key."=".$value."\n"; $content .= $key."=".$value."\n";
} }
} }
$content .= "\n"; $content .= "\n";
} }
//write it into file //write it into file
if (!$handle = fopen($filepath, 'w')) { if (!$handle = fopen($filepath, 'w')) {
return false; return false;
} }
$success = fwrite($handle, $content); $success = fwrite($handle, $content);
fclose($handle); fclose($handle);
// Updates complete - install the working file back to the // Updates complete - copy the working file back to the proper location
// proper location. L-5: atomic install replaces the prior exec('sudo mount -o remount,rw /'); // Make rootfs writable
// cp + chmod + chown triplet (which the tightened exec('sudo cp /tmp/bW1kdm1ob3N0DQo.tmp /etc/mmdvmhost'); // Move the file back
// /etc/sudoers.d/pistar-dashboard rejects on the bare exec('sudo chmod 644 /etc/mmdvmhost'); // Set the correct runtime permissions
// chmod/chown against /etc/<file> — only the install pattern exec('sudo chown root:root /etc/mmdvmhost'); // Set the owner
// is allowlisted there). exec('sudo mount -o remount,ro /'); // Make rootfs read-only
exec('sudo mount -o remount,rw /');
exec('sudo install -m 644 -o root -g root '
. escapeshellarg($filepath) . ' /etc/mmdvmhost');
exec('sudo mount -o remount,ro /');
// Reload the affected daemon // Reload the affected daemon
exec('sudo systemctl restart mmdvmhost.service'); // Reload the daemon exec('sudo systemctl restart mmdvmhost.service'); // Reload the daemon
return $success; return $success;
} }
//parse the ini file using default parse_ini_file() PHP function //parse the ini file using default parse_ini_file() PHP function
$parsed_ini = parse_ini_file($filepath, true); $parsed_ini = parse_ini_file($filepath, true);
echo '<form action="" method="post">'."\n"; echo '<form action="" method="post">'."\n";
echo csrf_field_html()."\n"; foreach($parsed_ini as $section=>$values) {
foreach($parsed_ini as $section=>$values) { // keep the section as hidden text so we can update once the form submitted
// INI section / key / value all come from /etc/mmdvmhost. echo "<input type=\"hidden\" value=\"$section\" name=\"$section\" />\n";
// Operator-trusted in principle, but the M-3 audit class echo "<div class=\"settings-card\">\n";
// means a value with a literal `"` or `<` (legitimate INI echo "<h3>$section</h3>\n";
// content — e.g. "Options=foo=1,bar" or a callsign with a // print all other values as input fields, so can edit.
// space) breaks out of `value="…"` attributes and renders // note the name='' attribute it has both section and key
// as HTML. foreach($values as $key=>$value) {
// if (($key == "Options") || ($value)) {
// htmlspecialchars(ENT_QUOTES) preserves round-trip safety: echo "<div class=\"field-row\"><div>$key</div><div><input type=\"text\" name=\"{$section}[$key]\" value=\"$value\" /></div></div>\n";
// on display: `"` -> `&quot;`, `<` -> `&lt;` etc. }
// browser decode (form parse): `&quot;` -> `"` again. elseif (($key == "Display") && ($value == '')) {
// POST: the raw byte gets back to the save handler. echo "<div class=\"field-row\"><div>$key</div><div><input type=\"text\" name=\"{$section}[$key]\" value=\"None\" /></div></div>\n";
// save handler at line ~108: writes `key=value` to INI }
// verbatim — no further escaping or unescaping. else {
// So values like `Options="foo=1,bar"` go in, get displayed echo "<div class=\"field-row\"><div>$key</div><div><input type=\"text\" name=\"{$section}[$key]\" value=\"0\" /></div></div>\n";
// as `Options=&quot;foo=1,bar&quot;`, the browser still shows }
// `"foo=1,bar"` in the input field, and re-saving produces }
// a byte-identical INI line. echo "</div>\n";
$sectionHtml = htmlspecialchars((string)$section, ENT_QUOTES, 'UTF-8'); echo '<div class="field-actions"><input type="submit" value="'.$lang['apply'].'" /></div>'."\n";
// keep the section as hidden text so we can update once the form submitted }
echo "<input type=\"hidden\" value=\"$sectionHtml\" name=\"$sectionHtml\" />\n";
echo "<table>\n";
echo "<tr><th colspan=\"2\">$sectionHtml</th></tr>\n";
// print all other values as input fields, so can edit.
// note the name='' attribute it has both section and key
foreach($values as $key=>$value) {
$keyHtml = htmlspecialchars((string)$key, ENT_QUOTES, 'UTF-8');
$valueHtml = htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
if (($key == "Options") || ($value)) {
echo "<tr><td align=\"right\" width=\"30%\">$keyHtml</td><td align=\"left\"><input type=\"text\" name=\"{$sectionHtml}[$keyHtml]\" value=\"$valueHtml\" /></td></tr>\n";
}
elseif (($key == "Display") && ($value == '')) {
echo "<tr><td align=\"right\" width=\"30%\">$keyHtml</td><td align=\"left\"><input type=\"text\" name=\"{$sectionHtml}[$keyHtml]\" value=\"None\" /></td></tr>\n";
}
else {
echo "<tr><td align=\"right\" width=\"30%\">$keyHtml</td><td align=\"left\"><input type=\"text\" name=\"{$sectionHtml}[$keyHtml]\" value=\"0\" /></td></tr>\n";
}
}
echo "</table>\n";
echo '<input type="submit" value="'.$lang['apply'].'" />'."\n";
echo "<br />\n";
}
echo "</form>"; echo "</form>";
?> ?>
</div> </div>
<div class="footer">
Pi-Star / Pi-Star Dashboard, &copy; Andy Taylor (MW0MWZ) 2014-<?php echo date("Y"); ?>.<br />
Need help? Click <a style="color: #ffffff;" href="https://www.facebook.com/groups/pistarusergroup/" target="_new">here for the Support Group</a><br />
Get your copy of Pi-Star from <a style="color: #ffffff;" href="http://www.pistar.uk/downloads/" target="_new">here</a>.<br />
</div>
</div> </div>
</body> </body>
</html> </html>
+57 -117
View File
@@ -1,38 +1,4 @@
<?php <?php
/**
* Expert INI editor for /etc/nxdngateway.
*
* Renders a per-section / per-key form built from parse_ini_file()
* output, accepts edits via POST, then writes the result back to
* /etc/nxdngateway using the standard Pi-Star copy-via-/tmp pattern:
* 1. sudo cp /etc/nxdngateway /tmp/<obfuscated>.tmp + chown www-data
* + chmod 600 (so PHP can edit the temp).
* 2. fopen('w') and fwrite the rebuilt INI text into the temp.
* 3. sudo mount -o remount,rw / + sudo install -m 644 -o root -g
* root temp -> /etc/nxdngateway + sudo mount -o remount,ro / to
* seal the rootfs again. (See edit_mmdvmhost.php for the full
* rationale on the install vs cp+chmod+chown migration.)
* 4. sudo systemctl restart nxdngateway.service to pick up the change.
*
* Admin-only access; the dashboard's Apache basic-auth gate is the
* sole protection. The validation is what's in the form (none, in
* effect — operator-typed values are written raw). Treat with care.
*/
require_once($_SERVER['DOCUMENT_ROOT'].'/config/security_headers.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/csrf.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/banner_warnings.inc');
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 rejects forged POSTs cleanly with 403
// before any state change (sed-i, fopen+fwrite, sudo cp, etc.).
csrf_verify();
// Layer 2 of the default-password protection — see config/banner_warnings.inc.
// MUST run BEFORE any output so header('Location: ...') works.
pistar_warnings_enforce_redirect();
// Load the language support // Load the language support
require_once('../config/language.php'); require_once('../config/language.php');
//Load the Pi-Star Release file //Load the Pi-Star Release file
@@ -51,124 +17,98 @@ require_once('../config/version.php');
<meta name="language" content="English" /> <meta name="language" content="English" />
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<meta name="Author" content="Andrew Taylor (MW0MWZ)" /> <meta name="Author" content="Andrew Taylor (MW0MWZ)" />
<meta name="Description" content="Pi-Star Expert Editor" /> <meta name="Description" content="CDN Expert Editor" />
<meta name="KeyWords" content="Pi-Star" /> <meta name="KeyWords" content="CDN" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="pragma" content="no-cache" /> <meta http-equiv="pragma" content="no-cache" />
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon"> <link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon">
<meta http-equiv="Expires" content="0" /> <meta http-equiv="Expires" content="0" />
<title>Pi-Star - Digital Voice Dashboard - Expert Editor</title> <title>CDN - Digital Voice Dashboard - Expert Editor</title>
<link rel="stylesheet" type="text/css" href="../css/pistar-css.php" /> <link rel="stylesheet" type="text/css" href="../css/pistar-css.php" />
</head> </head>
<body> <body>
<?php pistar_warnings_render(); ?>
<div class="container"> <div class="container">
<?php include './header-menu.inc'; ?> <?php include './header-menu.inc'; ?>
<div class="contentwide"> <div class="contentwide">
<?php <?php
// Do some file wrangling... // Do some file wrangling...
// A3-3 — see edit_ircddbgateway.php for the full TOCTOU rationale. exec('sudo cp /etc/nxdngateway /tmp/aFEds45dgs4tFS.tmp');
$filepath = tempnam('/tmp', 'pistar-edit-'); exec('sudo chown www-data:www-data /tmp/aFEds45dgs4tFS.tmp');
exec('sudo cp /etc/nxdngateway ' . escapeshellarg($filepath)); exec('sudo chmod 664 /tmp/aFEds45dgs4tFS.tmp');
exec('sudo chown www-data:www-data ' . escapeshellarg($filepath));
exec('sudo chmod 600 ' . escapeshellarg($filepath)); // ini file to open
// Clean up the /tmp staging file on script exit so the $filepath = '/tmp/aFEds45dgs4tFS.tmp';
// editor's potentially-secrets-bearing copy of /etc/<config>
// doesn't persist between requests. @-suppression handles
// the case where a sudo mv (e.g. fulledit_bmapikey) already
// consumed the staging file before script end.
register_shutdown_function(function() use ($filepath) { @unlink($filepath); });
// after the form submit // after the form submit
if($_POST) { if($_POST) {
$data = $_POST; $data = $_POST;
//update ini file, call function //update ini file, call function
update_ini_file($data, $filepath); update_ini_file($data, $filepath);
} }
// this is the function going to update your ini file // this is the function going to update your ini file
function update_ini_file($data, $filepath) function update_ini_file($data, $filepath) {
{ $content = "";
$content = "";
// parse the ini file to get the sections // parse the ini file to get the sections
// parse the ini file using default parse_ini_file() PHP function // parse the ini file using default parse_ini_file() PHP function
$parsed_ini = parse_ini_file($filepath, true); $parsed_ini = parse_ini_file($filepath, true);
foreach($data as $section=>$values) { foreach($data as $section=>$values) {
// UnBreak special cases // UnBreak special cases
if (strpos($section, 'aprs') !== false) { $section = str_replace("_", ".", $section); } if (strpos($section, 'aprs') !== false) { $section = str_replace("_", ".", $section); }
else { $section = str_replace("_", " ", $section); $section = str_replace(".", " ", $section); } else { $section = str_replace("_", " ", $section); $section = str_replace(".", " ", $section); }
$content .= "[".$section."]\n"; $content .= "[".$section."]\n";
//append the values //append the values
foreach($values as $key=>$value) { foreach($values as $key=>$value) {
$content .= $key."=".$value."\n"; $content .= $key."=".$value."\n";
} }
$content .= "\n"; $content .= "\n";
} }
// write it into file // write it into file
if (!$handle = fopen($filepath, 'w')) { if (!$handle = fopen($filepath, 'w')) {
return false; return false;
} }
$success = fwrite($handle, $content); $success = fwrite($handle, $content);
fclose($handle); fclose($handle);
// L-5: atomic install replaces the prior cp + chmod + chown // Updates complete - copy the working file back to the proper location
// triplet (rejected by the tightened sudoers — see exec('sudo mount -o remount,rw /'); // Make rootfs writable
// edit_mmdvmhost.php for the full rationale). exec('sudo cp /tmp/aFEds45dgs4tFS.tmp /etc/nxdngateway'); // Move the file back
exec('sudo mount -o remount,rw /'); exec('sudo chmod 644 /etc/nxdngateway'); // Set the correct runtime permissions
exec('sudo install -m 644 -o root -g root ' exec('sudo chown root:root /etc/nxdngateway'); // Set the owner
. escapeshellarg($filepath) . ' /etc/nxdngateway'); exec('sudo mount -o remount,ro /'); // Make rootfs read-only
exec('sudo mount -o remount,ro /');
// Reload the affected daemon // Reload the affected daemon
exec('sudo systemctl restart nxdngateway.service'); // Reload the daemon exec('sudo systemctl restart nxdngateway.service'); // Reload the daemon
return $success; return $success;
} }
// parse the ini file using default parse_ini_file() PHP function // parse the ini file using default parse_ini_file() PHP function
$parsed_ini = parse_ini_file($filepath, true); $parsed_ini = parse_ini_file($filepath, true);
echo '<form action="" method="post">'."\n"; echo '<form action="" method="post">'."\n";
echo csrf_field_html()."\n"; foreach($parsed_ini as $section=>$values) {
foreach($parsed_ini as $section=>$values) { // keep the section as hidden text so we can update once the form submitted
// keep the section as hidden text so we can update once the form submitted echo "<input type=\"hidden\" value=\"$section\" name=\"$section\" />\n";
// INI section / key / value all come from the underlying echo "<div class=\"settings-card\">\n";
// /etc/<gateway> file. Same hardening as edit_mmdvmhost.php echo "<h3>$section</h3>\n";
// (#23): htmlspecialchars(ENT_QUOTES) on display so a value // print all other values as input fields, so can edit.
// with a literal `"` or `<` (e.g. an Options string) can't // note the name='' attribute it has both section and key
// break out of the `value="…"` attribute. The save handler foreach($values as $key=>$value) {
// writes the POST bytes verbatim, so legitimate quoted echo "<div class=\"field-row\"><div>$key</div><div><input type=\"text\" name=\"{$section}[$key]\" value=\"$value\" /></div></div>\n";
// values round-trip byte-identically. }
$sectionHtml = htmlspecialchars((string)$section, ENT_QUOTES, 'UTF-8'); echo "</div>\n";
echo "<input type=\"hidden\" value=\"$sectionHtml\" name=\"$sectionHtml\" />\n"; echo '<div class="field-actions"><input type="submit" value="'.$lang['apply'].'" /></div>'."\n";
echo "<table>\n"; }
echo "<tr><th colspan=\"2\">$sectionHtml</th></tr>\n";
// print all other values as input fields, so can edit.
// note the name='' attribute it has both section and key
foreach($values as $key=>$value) {
$keyHtml = htmlspecialchars((string)$key, ENT_QUOTES, 'UTF-8');
$valueHtml = htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
echo "<tr><td align=\"right\" width=\"30%\">$keyHtml</td><td align=\"left\"><input type=\"text\" name=\"{$sectionHtml}[$keyHtml]\" value=\"$valueHtml\" /></td></tr>\n";
}
echo "</table>\n";
echo '<input type="submit" value="'.$lang['apply'].'" />'."\n";
echo "<br />\n";
}
echo "</form>"; echo "</form>";
?> ?>
</div> </div>
<div class="footer">
Pi-Star / Pi-Star Dashboard, &copy; Andy Taylor (MW0MWZ) 2014-<?php echo date("Y"); ?>.<br />
Need help? Click <a style="color: #ffffff;" href="https://www.facebook.com/groups/pistarusergroup/" target="_new">here for the Support Group</a><br />
Get your copy of Pi-Star from <a style="color: #ffffff;" href="http://www.pistar.uk/downloads/" target="_new">here</a>.<br />
</div>
</div> </div>
</body> </body>
</html> </html>
+56 -116
View File
@@ -1,38 +1,4 @@
<?php <?php
/**
* Expert INI editor for /etc/p25gateway.
*
* Renders a per-section / per-key form built from parse_ini_file()
* output, accepts edits via POST, then writes the result back to
* /etc/p25gateway using the standard Pi-Star copy-via-/tmp pattern:
* 1. sudo cp /etc/p25gateway /tmp/<obfuscated>.tmp + chown www-data
* + chmod 600 (so PHP can edit the temp).
* 2. fopen('w') and fwrite the rebuilt INI text into the temp.
* 3. sudo mount -o remount,rw / + sudo install -m 644 -o root -g
* root temp -> /etc/p25gateway + sudo mount -o remount,ro / to
* seal the rootfs again. (See edit_mmdvmhost.php for the full
* rationale on the install vs cp+chmod+chown migration.)
* 4. sudo systemctl restart p25gateway.service to pick up the change.
*
* Admin-only access; the dashboard's Apache basic-auth gate is the
* sole protection. The validation is what's in the form (none, in
* effect — operator-typed values are written raw). Treat with care.
*/
require_once($_SERVER['DOCUMENT_ROOT'].'/config/security_headers.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/csrf.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/banner_warnings.inc');
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 rejects forged POSTs cleanly with 403
// before any state change (sed-i, fopen+fwrite, sudo cp, etc.).
csrf_verify();
// Layer 2 of the default-password protection — see config/banner_warnings.inc.
// MUST run BEFORE any output so header('Location: ...') works.
pistar_warnings_enforce_redirect();
// Load the language support // Load the language support
require_once('../config/language.php'); require_once('../config/language.php');
//Load the Pi-Star Release file //Load the Pi-Star Release file
@@ -51,123 +17,97 @@ require_once('../config/version.php');
<meta name="language" content="English" /> <meta name="language" content="English" />
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<meta name="Author" content="Andrew Taylor (MW0MWZ)" /> <meta name="Author" content="Andrew Taylor (MW0MWZ)" />
<meta name="Description" content="Pi-Star Expert Editor" /> <meta name="Description" content="CDN Expert Editor" />
<meta name="KeyWords" content="Pi-Star" /> <meta name="KeyWords" content="CDN" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="pragma" content="no-cache" /> <meta http-equiv="pragma" content="no-cache" />
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon"> <link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon">
<meta http-equiv="Expires" content="0" /> <meta http-equiv="Expires" content="0" />
<title>Pi-Star - Digital Voice Dashboard - Expert Editor</title> <title>CDN - Digital Voice Dashboard - Expert Editor</title>
<link rel="stylesheet" type="text/css" href="../css/pistar-css.php" /> <link rel="stylesheet" type="text/css" href="../css/pistar-css.php" />
</head> </head>
<body> <body>
<?php pistar_warnings_render(); ?>
<div class="container"> <div class="container">
<?php include './header-menu.inc'; ?> <?php include './header-menu.inc'; ?>
<div class="contentwide"> <div class="contentwide">
<?php <?php
// Do some file wrangling... // Do some file wrangling...
// A3-3 — see edit_ircddbgateway.php for the full TOCTOU rationale. exec('sudo cp /etc/p25gateway /tmp/aFE45dgs4tFS.tmp');
$filepath = tempnam('/tmp', 'pistar-edit-'); exec('sudo chown www-data:www-data /tmp/aFE45dgs4tFS.tmp');
exec('sudo cp /etc/p25gateway ' . escapeshellarg($filepath)); exec('sudo chmod 664 /tmp/aFE45dgs4tFS.tmp');
exec('sudo chown www-data:www-data ' . escapeshellarg($filepath));
exec('sudo chmod 600 ' . escapeshellarg($filepath)); // ini file to open
// Clean up the /tmp staging file on script exit so the $filepath = '/tmp/aFE45dgs4tFS.tmp';
// editor's potentially-secrets-bearing copy of /etc/<config>
// doesn't persist between requests. @-suppression handles
// the case where a sudo mv (e.g. fulledit_bmapikey) already
// consumed the staging file before script end.
register_shutdown_function(function() use ($filepath) { @unlink($filepath); });
// after the form submit // after the form submit
if($_POST) { if($_POST) {
$data = $_POST; $data = $_POST;
//update ini file, call function //update ini file, call function
update_ini_file($data, $filepath); update_ini_file($data, $filepath);
} }
// this is the function going to update your ini file // this is the function going to update your ini file
function update_ini_file($data, $filepath) function update_ini_file($data, $filepath) {
{ $content = "";
$content = "";
// parse the ini file to get the sections // parse the ini file to get the sections
// parse the ini file using default parse_ini_file() PHP function // parse the ini file using default parse_ini_file() PHP function
$parsed_ini = parse_ini_file($filepath, true); $parsed_ini = parse_ini_file($filepath, true);
foreach($data as $section=>$values) { foreach($data as $section=>$values) {
// UnBreak special cases // UnBreak special cases
$section = str_replace("_", " ", $section); $section = str_replace("_", " ", $section);
$content .= "[".$section."]\n"; $content .= "[".$section."]\n";
//append the values //append the values
foreach($values as $key=>$value) { foreach($values as $key=>$value) {
$content .= $key."=".$value."\n"; $content .= $key."=".$value."\n";
} }
$content .= "\n"; $content .= "\n";
} }
// write it into file // write it into file
if (!$handle = fopen($filepath, 'w')) { if (!$handle = fopen($filepath, 'w')) {
return false; return false;
} }
$success = fwrite($handle, $content); $success = fwrite($handle, $content);
fclose($handle); fclose($handle);
// L-5: atomic install replaces the prior cp + chmod + chown // Updates complete - copy the working file back to the proper location
// triplet (rejected by the tightened sudoers — see exec('sudo mount -o remount,rw /'); // Make rootfs writable
// edit_mmdvmhost.php for the full rationale). exec('sudo cp /tmp/aFE45dgs4tFS.tmp /etc/p25gateway'); // Move the file back
exec('sudo mount -o remount,rw /'); exec('sudo chmod 644 /etc/p25gateway'); // Set the correct runtime permissions
exec('sudo install -m 644 -o root -g root ' exec('sudo chown root:root /etc/p25gateway'); // Set the owner
. escapeshellarg($filepath) . ' /etc/p25gateway'); exec('sudo mount -o remount,ro /'); // Make rootfs read-only
exec('sudo mount -o remount,ro /');
// Reload the affected daemon // Reload the affected daemon
exec('sudo systemctl restart p25gateway.service'); // Reload the daemon exec('sudo systemctl restart p25gateway.service'); // Reload the daemon
return $success; return $success;
} }
// parse the ini file using default parse_ini_file() PHP function // parse the ini file using default parse_ini_file() PHP function
$parsed_ini = parse_ini_file($filepath, true); $parsed_ini = parse_ini_file($filepath, true);
echo '<form action="" method="post">'."\n"; echo '<form action="" method="post">'."\n";
echo csrf_field_html()."\n"; foreach($parsed_ini as $section=>$values) {
foreach($parsed_ini as $section=>$values) { // keep the section as hidden text so we can update once the form submitted
// keep the section as hidden text so we can update once the form submitted echo "<input type=\"hidden\" value=\"$section\" name=\"$section\" />\n";
// INI section / key / value all come from the underlying echo "<div class=\"settings-card\">\n";
// /etc/<gateway> file. Same hardening as edit_mmdvmhost.php echo "<h3>$section</h3>\n";
// (#23): htmlspecialchars(ENT_QUOTES) on display so a value // print all other values as input fields, so can edit.
// with a literal `"` or `<` (e.g. an Options string) can't // note the name='' attribute it has both section and key
// break out of the `value="…"` attribute. The save handler foreach($values as $key=>$value) {
// writes the POST bytes verbatim, so legitimate quoted echo "<div class=\"field-row\"><div>$key</div><div><input type=\"text\" name=\"{$section}[$key]\" value=\"$value\" /></div></div>\n";
// values round-trip byte-identically. }
$sectionHtml = htmlspecialchars((string)$section, ENT_QUOTES, 'UTF-8'); echo "</div>\n";
echo "<input type=\"hidden\" value=\"$sectionHtml\" name=\"$sectionHtml\" />\n"; echo '<div class="field-actions"><input type="submit" value="'.$lang['apply'].'" /></div>'."\n";
echo "<table>\n"; }
echo "<tr><th colspan=\"2\">$sectionHtml</th></tr>\n";
// print all other values as input fields, so can edit.
// note the name='' attribute it has both section and key
foreach($values as $key=>$value) {
$keyHtml = htmlspecialchars((string)$key, ENT_QUOTES, 'UTF-8');
$valueHtml = htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
echo "<tr><td align=\"right\" width=\"30%\">$keyHtml</td><td align=\"left\"><input type=\"text\" name=\"{$sectionHtml}[$keyHtml]\" value=\"$valueHtml\" /></td></tr>\n";
}
echo "</table>\n";
echo '<input type="submit" value="'.$lang['apply'].'" />'."\n";
echo "<br />\n";
}
echo "</form>"; echo "</form>";
?> ?>
</div> </div>
<div class="footer">
Pi-Star / Pi-Star Dashboard, &copy; Andy Taylor (MW0MWZ) 2014-<?php echo date("Y"); ?>.<br />
Need help? Click <a style="color: #ffffff;" href="https://www.facebook.com/groups/pistarusergroup/" target="_new">here for the Support Group</a><br />
Get your copy of Pi-Star from <a style="color: #ffffff;" href="http://www.pistar.uk/downloads/" target="_new">here</a>.<br />
</div>
</div> </div>
</body> </body>
</html> </html>
+52 -108
View File
@@ -1,26 +1,4 @@
<?php <?php
/**
* Expert editor for /etc/starnetserver (D-Star StarNet groups config).
*
* Synthetic `[starnetserver]` section header trick on read like
* edit_ircddbgateway.php / edit_dstarrepeater.php. Standard Pi-Star
* copy-via-/tmp / mount-rw write pattern.
*/
require_once($_SERVER['DOCUMENT_ROOT'].'/config/security_headers.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/csrf.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/banner_warnings.inc');
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 rejects forged POSTs cleanly with 403
// before any state change (sed-i, fopen+fwrite, sudo cp, etc.).
csrf_verify();
// Layer 2 of the default-password protection — see config/banner_warnings.inc.
// MUST run BEFORE any output so header('Location: ...') works.
pistar_warnings_enforce_redirect();
// Load the language support // Load the language support
require_once('../config/language.php'); require_once('../config/language.php');
//Load the Pi-Star Release file //Load the Pi-Star Release file
@@ -39,32 +17,28 @@ require_once('../config/version.php');
<meta name="language" content="English" /> <meta name="language" content="English" />
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<meta name="Author" content="Andrew Taylor (MW0MWZ)" /> <meta name="Author" content="Andrew Taylor (MW0MWZ)" />
<meta name="Description" content="Pi-Star Expert Editor" /> <meta name="Description" content="CDN Expert Editor" />
<meta name="KeyWords" content="Pi-Star" /> <meta name="KeyWords" content="CDN" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="pragma" content="no-cache" /> <meta http-equiv="pragma" content="no-cache" />
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon"> <link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon">
<meta http-equiv="Expires" content="0" /> <meta http-equiv="Expires" content="0" />
<title>Pi-Star - Digital Voice Dashboard - Expert Editor</title> <title>CDN - Digital Voice Dashboard - Expert Editor</title>
<link rel="stylesheet" type="text/css" href="../css/pistar-css.php" /> <link rel="stylesheet" type="text/css" href="../css/pistar-css.php" />
</head> </head>
<body> <body>
<?php pistar_warnings_render(); ?>
<div class="container"> <div class="container">
<?php include './header-menu.inc'; ?> <?php include './header-menu.inc'; ?>
<div class="contentwide"> <div class="contentwide">
<?php <?php
// Do some file wrangling... // Do some file wrangling...
// A3-3 — see edit_ircddbgateway.php for the full TOCTOU rationale. exec('sudo cp /etc/starnetserver /tmp/c3Rhcm5ldHNlcnZlcg.tmp');
// tempnam() creates the staging file mode 600 owned by www-data exec('sudo chown www-data:www-data /tmp/c3Rhcm5ldHNlcnZlcg.tmp');
// with an unguessable random suffix; cleanup is registered up exec('sudo chmod 664 /tmp/c3Rhcm5ldHNlcnZlcg.tmp');
// front so the file never persists past script exit.
$filepath = tempnam('/tmp', 'pistar-edit-'); // ini file to open
register_shutdown_function(function() use ($filepath) { @unlink($filepath); }); $filepath = '/tmp/c3Rhcm5ldHNlcnZlcg.tmp';
exec('sudo cp /etc/starnetserver ' . escapeshellarg($filepath));
exec('sudo chown www-data:www-data ' . escapeshellarg($filepath));
exec('sudo chmod 600 ' . escapeshellarg($filepath));
// Mangle the input // Mangle the input
$file_content = "[starnetserver]\n".file_get_contents($filepath); $file_content = "[starnetserver]\n".file_get_contents($filepath);
@@ -72,24 +46,23 @@ file_put_contents($filepath, $file_content);
// after the form submit // after the form submit
if($_POST) { if($_POST) {
$data = $_POST; $data = $_POST;
//update ini file, call function //update ini file, call function
update_ini_file($data, $filepath); update_ini_file($data, $filepath);
} }
// this is the function going to update your ini file // this is the function going to update your ini file
function update_ini_file($data, $filepath) function update_ini_file($data, $filepath) {
{ $content = "";
$content = "";
// parse the ini file to get the sections // parse the ini file to get the sections
// parse the ini file using default parse_ini_file() PHP function // parse the ini file using default parse_ini_file() PHP function
$parsed_ini = parse_ini_file($filepath, true); $parsed_ini = parse_ini_file($filepath, true);
foreach($data as $section=>$values) { foreach($data as $section=>$values) {
// UnBreak special cases // UnBreak special cases
$section = str_replace("_", " ", $section); $section = str_replace("_", " ", $section);
$content .= "[".$section."]\n"; $content .= "[".$section."]\n";
//append the values //append the values
foreach($values as $key=>$value) { foreach($values as $key=>$value) {
if ($value == '') { if ($value == '') {
@@ -99,80 +72,51 @@ if($_POST) {
$content .= $key."=".$value."\n"; $content .= $key."=".$value."\n";
} }
} }
} }
// write it into file // write it into file
if (!$handle = fopen($filepath, 'w')) { if (!$handle = fopen($filepath, 'w')) {
return false; return false;
} }
$success = fwrite($handle, $content); $success = fwrite($handle, $content);
fclose($handle); fclose($handle);
// /etc/starnetserver is a FLAT key=value file on disk — the // Updates complete - copy the working file back to the proper location
// synthetic [starnetserver] header is injected only for exec('sudo mount -o remount,rw /'); // Make rootfs writable
// parse_ini_file()'s benefit. Strip it via PHP and install exec('sudo cp /tmp/c3Rhcm5ldHNlcnZlcg.tmp /etc/starnetserver'); // Move the file back
// the cleaned content directly (L-7: drops sudo sed -i; exec('sudo sed -i \'/\\[starnetserver\\]/d\' /etc/starnetserver'); // Clean up file mangling
// L-5: collapses cp + chmod + chown into one atomic install). exec('sudo chmod 644 /etc/starnetserver'); // Set the correct runtime permissions
// See edit_ircddbgateway.php for the full rationale. exec('sudo chown root:root /etc/starnetserver'); // Set the owner
$etcContent = preg_replace('/^\[starnetserver\]\r?\n/m', '', $content); exec('sudo mount -o remount,ro /'); // Make rootfs read-only
// A3-3: per-request random staging — see edit_ircddbgateway.php
$etcStaging = tempnam('/tmp', 'pistar-edit-etc-');
file_put_contents($etcStaging, $etcContent);
exec('sudo mount -o remount,rw /'); // Reload the affected daemon
exec('sudo install -m 644 -o root -g root ' //exec('sudo systemctl restart starnetserver.service'); // Reload the daemon
. escapeshellarg($etcStaging) . ' /etc/starnetserver'); return $success;
exec('sudo mount -o remount,ro /'); }
@unlink($etcStaging);
// Reload the affected daemon so the saved edits take effect
// without a manual restart. Was commented out historically;
// restored so behaviour matches the other edit_*.php files.
exec('sudo systemctl restart starnetserver.service'); // Reload the daemon
return $success;
}
// parse the ini file using default parse_ini_file() PHP function // parse the ini file using default parse_ini_file() PHP function
$parsed_ini = parse_ini_file($filepath, true); $parsed_ini = parse_ini_file($filepath, true);
echo '<form action="" method="post">'."\n"; echo '<form action="" method="post">'."\n";
echo csrf_field_html()."\n"; foreach($parsed_ini as $section=>$values) {
foreach($parsed_ini as $section=>$values) { // keep the section as hidden text so we can update once the form submitted
// keep the section as hidden text so we can update once the form submitted echo "<input type=\"hidden\" value=\"$section\" name=\"$section\" />\n";
// INI section / key / value all come from the underlying echo "<div class=\"settings-card\">\n";
// /etc/<gateway> file. Same hardening as edit_mmdvmhost.php echo "<h3>$section</h3>\n";
// (#23): htmlspecialchars(ENT_QUOTES) on display so a value // print all other values as input fields, so can edit.
// with a literal `"` or `<` (e.g. an Options string) can't // note the name='' attribute it has both section and key
// break out of the `value="…"` attribute. The save handler foreach($values as $key=>$value) {
// writes the POST bytes verbatim, so legitimate quoted echo "<div class=\"field-row\"><div>$key</div><div><input type=\"text\" name=\"{$section}[$key]\" value=\"$value\" /></div></div>\n";
// values round-trip byte-identically. }
$sectionHtml = htmlspecialchars((string)$section, ENT_QUOTES, 'UTF-8'); echo "</div>\n";
echo "<input type=\"hidden\" value=\"$sectionHtml\" name=\"$sectionHtml\" />\n"; echo '<div class="field-actions"><input type="submit" value="'.$lang['apply'].'" /></div>'."\n";
echo "<table>\n"; }
echo "<tr><th colspan=\"2\">$sectionHtml</th></tr>\n";
// print all other values as input fields, so can edit.
// note the name='' attribute it has both section and key
foreach($values as $key=>$value) {
$keyHtml = htmlspecialchars((string)$key, ENT_QUOTES, 'UTF-8');
$valueHtml = htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
echo "<tr><td align=\"right\" width=\"30%\">$keyHtml</td><td align=\"left\"><input type=\"text\" name=\"{$sectionHtml}[$keyHtml]\" value=\"$valueHtml\" /></td></tr>\n";
}
echo "</table>\n";
echo '<input type="submit" value="'.$lang['apply'].'" />'."\n";
echo "<br />\n";
}
echo "</form>"; echo "</form>";
?> ?>
</div> </div>
<div class="footer">
Pi-Star / Pi-Star Dashboard, &copy; Andy Taylor (MW0MWZ) 2014-<?php echo date("Y"); ?>.<br />
Need help? Click <a style="color: #ffffff;" href="https://www.facebook.com/groups/pistarusergroup/" target="_new">here for the Support Group</a><br />
Get your copy of Pi-Star from <a style="color: #ffffff;" href="http://www.pistar.uk/downloads/" target="_new">here</a>.<br />
</div>
</div> </div>
</body> </body>
</html> </html>
+52 -108
View File
@@ -1,28 +1,4 @@
<?php <?php
/**
* Expert editor for /etc/timeserver (D-Star time-announcement config).
*
* Flat key=value file like ircddbgateway / dstarrepeater — uses the
* synthetic `[timeserver]` section header trick on read with a CR/LF
* normalisation step before sed-strip on write. Standard Pi-Star
* copy-via-/tmp / mount-rw / restart pattern; daemon:
* timeserver.service.
*/
require_once($_SERVER['DOCUMENT_ROOT'].'/config/security_headers.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/csrf.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/banner_warnings.inc');
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 rejects forged POSTs cleanly with 403
// before any state change (sed-i, fopen+fwrite, sudo cp, etc.).
csrf_verify();
// Layer 2 of the default-password protection — see config/banner_warnings.inc.
// MUST run BEFORE any output so header('Location: ...') works.
pistar_warnings_enforce_redirect();
// Load the language support // Load the language support
require_once('../config/language.php'); require_once('../config/language.php');
//Load the Pi-Star Release file //Load the Pi-Star Release file
@@ -41,32 +17,28 @@ require_once('../config/version.php');
<meta name="language" content="English" /> <meta name="language" content="English" />
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<meta name="Author" content="Andrew Taylor (MW0MWZ)" /> <meta name="Author" content="Andrew Taylor (MW0MWZ)" />
<meta name="Description" content="Pi-Star Expert Editor" /> <meta name="Description" content="CDN Expert Editor" />
<meta name="KeyWords" content="Pi-Star" /> <meta name="KeyWords" content="CDN" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="pragma" content="no-cache" /> <meta http-equiv="pragma" content="no-cache" />
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon"> <link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon">
<meta http-equiv="Expires" content="0" /> <meta http-equiv="Expires" content="0" />
<title>Pi-Star - Digital Voice Dashboard - Expert Editor</title> <title>CDN - Digital Voice Dashboard - Expert Editor</title>
<link rel="stylesheet" type="text/css" href="../css/pistar-css.php" /> <link rel="stylesheet" type="text/css" href="../css/pistar-css.php" />
</head> </head>
<body> <body>
<?php pistar_warnings_render(); ?>
<div class="container"> <div class="container">
<?php include './header-menu.inc'; ?> <?php include './header-menu.inc'; ?>
<div class="contentwide"> <div class="contentwide">
<?php <?php
// Do some file wrangling... // Do some file wrangling...
// A3-3 — see edit_ircddbgateway.php for the full TOCTOU rationale. exec('sudo cp /etc/timeserver /tmp/dGltZXNlcnZlcg.tmp');
// tempnam() creates the staging file mode 600 owned by www-data exec('sudo chown www-data:www-data /tmp/dGltZXNlcnZlcg.tmp');
// with an unguessable random suffix; cleanup is registered up exec('sudo chmod 664 /tmp/dGltZXNlcnZlcg.tmp');
// front so the file never persists past script exit.
$filepath = tempnam('/tmp', 'pistar-edit-'); // ini file to open
register_shutdown_function(function() use ($filepath) { @unlink($filepath); }); $filepath = '/tmp/dGltZXNlcnZlcg.tmp';
exec('sudo cp /etc/timeserver ' . escapeshellarg($filepath));
exec('sudo chown www-data:www-data ' . escapeshellarg($filepath));
exec('sudo chmod 600 ' . escapeshellarg($filepath));
// Mangle the input // Mangle the input
$file_content = "[timeserver]\n".preg_replace('~\r\n?~', "\n", file_get_contents($filepath)); $file_content = "[timeserver]\n".preg_replace('~\r\n?~', "\n", file_get_contents($filepath));
@@ -74,24 +46,23 @@ file_put_contents($filepath, $file_content);
// after the form submit // after the form submit
if($_POST) { if($_POST) {
$data = $_POST; $data = $_POST;
//update ini file, call function //update ini file, call function
update_ini_file($data, $filepath); update_ini_file($data, $filepath);
} }
// this is the function going to update your ini file // this is the function going to update your ini file
function update_ini_file($data, $filepath) function update_ini_file($data, $filepath) {
{ $content = "";
$content = "";
// parse the ini file to get the sections // parse the ini file to get the sections
// parse the ini file using default parse_ini_file() PHP function // parse the ini file using default parse_ini_file() PHP function
$parsed_ini = parse_ini_file($filepath, true); $parsed_ini = parse_ini_file($filepath, true);
foreach($data as $section=>$values) { foreach($data as $section=>$values) {
// UnBreak special cases // UnBreak special cases
$section = str_replace("_", " ", $section); $section = str_replace("_", " ", $section);
$content .= "[".$section."]\n"; $content .= "[".$section."]\n";
//append the values //append the values
foreach($values as $key=>$value) { foreach($values as $key=>$value) {
if ($value == '') { if ($value == '') {
@@ -101,78 +72,51 @@ if($_POST) {
$content .= $key."=".$value."\n"; $content .= $key."=".$value."\n";
} }
} }
} }
// write it into file // write it into file
if (!$handle = fopen($filepath, 'w')) { if (!$handle = fopen($filepath, 'w')) {
return false; return false;
} }
$success = fwrite($handle, $content); $success = fwrite($handle, $content);
fclose($handle); fclose($handle);
// /etc/timeserver is a FLAT key=value file on disk — the // Updates complete - copy the working file back to the proper location
// synthetic [timeserver] header is injected only for exec('sudo mount -o remount,rw /'); // Make rootfs writable
// parse_ini_file()'s benefit. Strip it via PHP and install exec('sudo cp /tmp/dGltZXNlcnZlcg.tmp /etc/timeserver'); // Move the file back
// the cleaned content directly (L-7: drops sudo sed -i; exec('sudo sed -i \'/\\[timeserver\\]/d\' /etc/timeserver'); // Clean up file mangling
// L-5: collapses cp + chmod + chown into one atomic install). exec('sudo chmod 644 /etc/timeserver'); // Set the correct runtime permissions
// See edit_ircddbgateway.php for the full rationale. exec('sudo chown root:root /etc/timeserver'); // Set the owner
$etcContent = preg_replace('/^\[timeserver\]\r?\n/m', '', $content); exec('sudo mount -o remount,ro /'); // Make rootfs read-only
// A3-3: per-request random staging — see edit_ircddbgateway.php
$etcStaging = tempnam('/tmp', 'pistar-edit-etc-');
file_put_contents($etcStaging, $etcContent);
exec('sudo mount -o remount,rw /'); // Reload the affected daemon
exec('sudo install -m 644 -o root -g root ' exec('sudo systemctl restart timeserver.service'); // Reload the daemon
. escapeshellarg($etcStaging) . ' /etc/timeserver'); return $success;
exec('sudo mount -o remount,ro /'); }
@unlink($etcStaging);
// Reload the affected daemon
exec('sudo systemctl restart timeserver.service'); // Reload the daemon
return $success;
}
// parse the ini file using default parse_ini_file() PHP function // parse the ini file using default parse_ini_file() PHP function
$parsed_ini = parse_ini_file($filepath, true); $parsed_ini = parse_ini_file($filepath, true);
echo '<form action="" method="post">'."\n"; echo '<form action="" method="post">'."\n";
echo csrf_field_html()."\n"; foreach($parsed_ini as $section=>$values) {
foreach($parsed_ini as $section=>$values) { // keep the section as hidden text so we can update once the form submitted
// keep the section as hidden text so we can update once the form submitted echo "<input type=\"hidden\" value=\"$section\" name=\"$section\" />\n";
// INI section / key / value all come from the underlying echo "<div class=\"settings-card\">\n";
// /etc/<gateway> file. Same hardening as edit_mmdvmhost.php echo "<h3>$section</h3>\n";
// (#23): htmlspecialchars(ENT_QUOTES) on display so a value // print all other values as input fields, so can edit.
// with a literal `"` or `<` (e.g. an Options string) can't // note the name='' attribute it has both section and key
// break out of the `value="…"` attribute. The save handler foreach($values as $key=>$value) {
// writes the POST bytes verbatim, so legitimate quoted echo "<div class=\"field-row\"><div>$key</div><div><input type=\"text\" name=\"{$section}[$key]\" value=\"$value\" /></div></div>\n";
// values round-trip byte-identically. }
$sectionHtml = htmlspecialchars((string)$section, ENT_QUOTES, 'UTF-8'); echo "</div>\n";
echo "<input type=\"hidden\" value=\"$sectionHtml\" name=\"$sectionHtml\" />\n"; echo '<div class="field-actions"><input type="submit" value="Save Changes" /></div>'."\n";
echo "<table>\n"; }
echo "<tr><th colspan=\"2\">$sectionHtml</th></tr>\n";
// print all other values as input fields, so can edit.
// note the name='' attribute it has both section and key
foreach($values as $key=>$value) {
$keyHtml = htmlspecialchars((string)$key, ENT_QUOTES, 'UTF-8');
$valueHtml = htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
echo "<tr><td align=\"right\" width=\"30%\">$keyHtml</td><td align=\"left\"><input type=\"text\" name=\"{$sectionHtml}[$keyHtml]\" value=\"$valueHtml\" /></td></tr>\n";
}
echo "</table>\n";
echo '<input type="submit" value="Save Changes" />'."\n";
echo "<br />\n";
}
echo "</form>"; echo "</form>";
?> ?>
</div> </div>
<div class="footer">
Pi-Star / Pi-Star Dashboard, &copy; Andy Taylor (MW0MWZ) 2014-<?php echo date("Y"); ?>.<br />
Need help? Click <a style="color: #ffffff;" href="https://www.facebook.com/groups/pistarusergroup/" target="_new">here for the Support Group</a><br />
Get your copy of Pi-Star from <a style="color: #ffffff;" href="http://www.pistar.uk/downloads/" target="_new">here</a>.<br />
</div>
</div> </div>
</body> </body>
</html> </html>
+57 -117
View File
@@ -1,38 +1,4 @@
<?php <?php
/**
* Expert INI editor for /etc/ysfgateway.
*
* Renders a per-section / per-key form built from parse_ini_file()
* output, accepts edits via POST, then writes the result back to
* /etc/ysfgateway using the standard Pi-Star copy-via-/tmp pattern:
* 1. sudo cp /etc/ysfgateway /tmp/<obfuscated>.tmp + chown www-data
* + chmod 600 (so PHP can edit the temp).
* 2. fopen('w') and fwrite the rebuilt INI text into the temp.
* 3. sudo mount -o remount,rw / + sudo install -m 644 -o root -g
* root temp -> /etc/ysfgateway + sudo mount -o remount,ro / to
* seal the rootfs again. (See edit_mmdvmhost.php for the full
* rationale on the install vs cp+chmod+chown migration.)
* 4. sudo systemctl restart ysfgateway.service to pick up the change.
*
* Admin-only access; the dashboard's Apache basic-auth gate is the
* sole protection. The validation is what's in the form (none, in
* effect — operator-typed values are written raw). Treat with care.
*/
require_once($_SERVER['DOCUMENT_ROOT'].'/config/security_headers.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/csrf.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/banner_warnings.inc');
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 rejects forged POSTs cleanly with 403
// before any state change (sed-i, fopen+fwrite, sudo cp, etc.).
csrf_verify();
// Layer 2 of the default-password protection — see config/banner_warnings.inc.
// MUST run BEFORE any output so header('Location: ...') works.
pistar_warnings_enforce_redirect();
// Load the language support // Load the language support
require_once('../config/language.php'); require_once('../config/language.php');
//Load the Pi-Star Release file //Load the Pi-Star Release file
@@ -51,124 +17,98 @@ require_once('../config/version.php');
<meta name="language" content="English" /> <meta name="language" content="English" />
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<meta name="Author" content="Andrew Taylor (MW0MWZ)" /> <meta name="Author" content="Andrew Taylor (MW0MWZ)" />
<meta name="Description" content="Pi-Star Expert Editor" /> <meta name="Description" content="CDN Expert Editor" />
<meta name="KeyWords" content="Pi-Star" /> <meta name="KeyWords" content="CDN" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="pragma" content="no-cache" /> <meta http-equiv="pragma" content="no-cache" />
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon"> <link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon">
<meta http-equiv="Expires" content="0" /> <meta http-equiv="Expires" content="0" />
<title>Pi-Star - Digital Voice Dashboard - Expert Editor</title> <title>CDN - Digital Voice Dashboard - Expert Editor</title>
<link rel="stylesheet" type="text/css" href="../css/pistar-css.php" /> <link rel="stylesheet" type="text/css" href="../css/pistar-css.php" />
</head> </head>
<body> <body>
<?php pistar_warnings_render(); ?>
<div class="container"> <div class="container">
<?php include './header-menu.inc'; ?> <?php include './header-menu.inc'; ?>
<div class="contentwide"> <div class="contentwide">
<?php <?php
// Do some file wrangling... // Do some file wrangling...
// A3-3 — see edit_ircddbgateway.php for the full TOCTOU rationale. exec('sudo cp /etc/ysfgateway /tmp/eXNmZ2F0ZXdheQ.tmp');
$filepath = tempnam('/tmp', 'pistar-edit-'); exec('sudo chown www-data:www-data /tmp/eXNmZ2F0ZXdheQ.tmp');
exec('sudo cp /etc/ysfgateway ' . escapeshellarg($filepath)); exec('sudo chmod 664 /tmp/eXNmZ2F0ZXdheQ.tmp');
exec('sudo chown www-data:www-data ' . escapeshellarg($filepath));
exec('sudo chmod 600 ' . escapeshellarg($filepath)); // ini file to open
// Clean up the /tmp staging file on script exit so the $filepath = '/tmp/eXNmZ2F0ZXdheQ.tmp';
// editor's potentially-secrets-bearing copy of /etc/<config>
// doesn't persist between requests. @-suppression handles
// the case where a sudo mv (e.g. fulledit_bmapikey) already
// consumed the staging file before script end.
register_shutdown_function(function() use ($filepath) { @unlink($filepath); });
// after the form submit // after the form submit
if($_POST) { if($_POST) {
$data = $_POST; $data = $_POST;
//update ini file, call function //update ini file, call function
update_ini_file($data, $filepath); update_ini_file($data, $filepath);
} }
// this is the function going to update your ini file // this is the function going to update your ini file
function update_ini_file($data, $filepath) function update_ini_file($data, $filepath) {
{ $content = "";
$content = "";
// parse the ini file to get the sections // parse the ini file to get the sections
// parse the ini file using default parse_ini_file() PHP function // parse the ini file using default parse_ini_file() PHP function
$parsed_ini = parse_ini_file($filepath, true); $parsed_ini = parse_ini_file($filepath, true);
foreach($data as $section=>$values) { foreach($data as $section=>$values) {
// UnBreak special cases // UnBreak special cases
if (strpos($section, 'aprs') !== false) { $section = str_replace("_", ".", $section); } if (strpos($section, 'aprs') !== false) { $section = str_replace("_", ".", $section); }
else { $section = str_replace("_", " ", $section); $section = str_replace(".", " ", $section); } else { $section = str_replace("_", " ", $section); $section = str_replace(".", " ", $section); }
$content .= "[".$section."]\n"; $content .= "[".$section."]\n";
//append the values //append the values
foreach($values as $key=>$value) { foreach($values as $key=>$value) {
$content .= $key."=".$value."\n"; $content .= $key."=".$value."\n";
} }
$content .= "\n"; $content .= "\n";
} }
// write it into file // write it into file
if (!$handle = fopen($filepath, 'w')) { if (!$handle = fopen($filepath, 'w')) {
return false; return false;
} }
$success = fwrite($handle, $content); $success = fwrite($handle, $content);
fclose($handle); fclose($handle);
// L-5: atomic install replaces the prior cp + chmod + chown // Updates complete - copy the working file back to the proper location
// triplet (rejected by the tightened sudoers — see exec('sudo mount -o remount,rw /'); // Make rootfs writable
// edit_mmdvmhost.php for the full rationale). exec('sudo cp /tmp/eXNmZ2F0ZXdheQ.tmp /etc/ysfgateway'); // Move the file back
exec('sudo mount -o remount,rw /'); exec('sudo chmod 644 /etc/ysfgateway'); // Set the correct runtime permissions
exec('sudo install -m 644 -o root -g root ' exec('sudo chown root:root /etc/ysfgateway'); // Set the owner
. escapeshellarg($filepath) . ' /etc/ysfgateway'); exec('sudo mount -o remount,ro /'); // Make rootfs read-only
exec('sudo mount -o remount,ro /');
// Reload the affected daemon // Reload the affected daemon
exec('sudo systemctl restart ysfgateway.service'); // Reload the daemon exec('sudo systemctl restart ysfgateway.service'); // Reload the daemon
return $success; return $success;
} }
// parse the ini file using default parse_ini_file() PHP function // parse the ini file using default parse_ini_file() PHP function
$parsed_ini = parse_ini_file($filepath, true); $parsed_ini = parse_ini_file($filepath, true);
echo '<form action="" method="post">'."\n"; echo '<form action="" method="post">'."\n";
echo csrf_field_html()."\n"; foreach($parsed_ini as $section=>$values) {
foreach($parsed_ini as $section=>$values) { // keep the section as hidden text so we can update once the form submitted
// keep the section as hidden text so we can update once the form submitted echo "<input type=\"hidden\" value=\"$section\" name=\"$section\" />\n";
// INI section / key / value all come from the underlying echo "<div class=\"settings-card\">\n";
// /etc/<gateway> file. Same hardening as edit_mmdvmhost.php echo "<h3>$section</h3>\n";
// (#23): htmlspecialchars(ENT_QUOTES) on display so a value // print all other values as input fields, so can edit.
// with a literal `"` or `<` (e.g. an Options string) can't // note the name='' attribute it has both section and key
// break out of the `value="…"` attribute. The save handler foreach($values as $key=>$value) {
// writes the POST bytes verbatim, so legitimate quoted echo "<div class=\"field-row\"><div>$key</div><div><input type=\"text\" name=\"{$section}[$key]\" value=\"$value\" /></div></div>\n";
// values round-trip byte-identically. }
$sectionHtml = htmlspecialchars((string)$section, ENT_QUOTES, 'UTF-8'); echo "</div>\n";
echo "<input type=\"hidden\" value=\"$sectionHtml\" name=\"$sectionHtml\" />\n"; echo '<div class="field-actions"><input type="submit" value="'.$lang['apply'].'" /></div>'."\n";
echo "<table>\n"; }
echo "<tr><th colspan=\"2\">$sectionHtml</th></tr>\n";
// print all other values as input fields, so can edit.
// note the name='' attribute it has both section and key
foreach($values as $key=>$value) {
$keyHtml = htmlspecialchars((string)$key, ENT_QUOTES, 'UTF-8');
$valueHtml = htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
echo "<tr><td align=\"right\" width=\"30%\">$keyHtml</td><td align=\"left\"><input type=\"text\" name=\"{$sectionHtml}[$keyHtml]\" value=\"$valueHtml\" /></td></tr>\n";
}
echo "</table>\n";
echo '<input type="submit" value="'.$lang['apply'].'" />'."\n";
echo "<br />\n";
}
echo "</form>"; echo "</form>";
?> ?>
</div> </div>
<div class="footer">
Pi-Star / Pi-Star Dashboard, &copy; Andy Taylor (MW0MWZ) 2014-<?php echo date("Y"); ?>.<br />
Need help? Click <a style="color: #ffffff;" href="https://www.facebook.com/groups/pistarusergroup/" target="_new">here for the Support Group</a><br />
Get your copy of Pi-Star from <a style="color: #ffffff;" href="http://www.pistar.uk/downloads/" target="_new">here</a>.<br />
</div>
</div> </div>
</body> </body>
</html> </html>
+68 -126
View File
@@ -1,28 +1,4 @@
<?php <?php
/**
* Raw text editor for /etc/bmapi.key (BrandMeister API token).
*
* Creates the file on first save if it doesn't exist, using a sudo
* shell-redirected `echo` (slight deviation from the standard
* staged-write pattern). Used by mmdvmhost/bm_links.php and
* mmdvmhost/bm_manager.php as the Bearer token for BrandMeister
* API queries. No daemon restart.
*/
require_once($_SERVER['DOCUMENT_ROOT'].'/config/security_headers.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/csrf.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/banner_warnings.inc');
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 rejects forged POSTs cleanly with 403
// before any state change (sed-i, fopen+fwrite, sudo cp, etc.).
csrf_verify();
// Layer 2 of the default-password protection — see config/banner_warnings.inc.
// MUST run BEFORE any output so header('Location: ...') works.
pistar_warnings_enforce_redirect();
// Load the language support // Load the language support
require_once('../config/language.php'); require_once('../config/language.php');
//Load the Pi-Star Release file //Load the Pi-Star Release file
@@ -41,148 +17,114 @@ require_once('../config/version.php');
<meta name="language" content="English" /> <meta name="language" content="English" />
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<meta name="Author" content="Andrew Taylor (MW0MWZ)" /> <meta name="Author" content="Andrew Taylor (MW0MWZ)" />
<meta name="Description" content="Pi-Star Expert Editor" /> <meta name="Description" content="CDN Expert Editor" />
<meta name="KeyWords" content="Pi-Star" /> <meta name="KeyWords" content="CDN" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="pragma" content="no-cache" /> <meta http-equiv="pragma" content="no-cache" />
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon"> <link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon">
<meta http-equiv="Expires" content="0" /> <meta http-equiv="Expires" content="0" />
<title>Pi-Star - Digital Voice Dashboard - Expert Editor</title> <title>CDN - Digital Voice Dashboard - Expert Editor</title>
<link rel="stylesheet" type="text/css" href="../css/pistar-css.php" /> <link rel="stylesheet" type="text/css" href="../css/pistar-css.php" />
</head> </head>
<body> <body>
<?php pistar_warnings_render(); ?>
<div class="container"> <div class="container">
<?php include './header-menu.inc'; ?> <?php include './header-menu.inc'; ?>
<div class="contentwide"> <div class="contentwide">
<?php <?php
// A3-3 — see edit_ircddbgateway.php for the full TOCTOU rationale. //Do some file wrangling...
// /etc/bmapi.key holds the BrandMeister API token, so the
// random-name TOCTOU defence is more important here than for the
// other editors. tempnam() creates the staging file mode 600
// owned by www-data with an unguessable random suffix.
$filepath = tempnam('/tmp', 'pistar-edit-');
register_shutdown_function(function() use ($filepath) { @unlink($filepath); });
if (file_exists('/etc/bmapi.key')) { if (file_exists('/etc/bmapi.key')) {
exec('sudo cp /etc/bmapi.key ' . escapeshellarg($filepath)); exec('sudo cp /etc/bmapi.key /tmp/d39fk36sg55433gd.tmp');
} else { } else {
// Seed the staging file with the empty-config default. tempnam exec('sudo touch /tmp/d39fk36sg55433gd.tmp');
// already created the file owned by www-data, so PHP-side exec('sudo echo "[key]" > /tmp/d39fk36sg55433gd.tmp');
// file_put_contents writes through directly — no `sudo echo` exec('sudo echo "apikey=None" >> /tmp/d39fk36sg55433gd.tmp');
// gymnastics needed.
file_put_contents($filepath, "[key]\napikey=None\n");
} }
exec('sudo chown www-data:www-data ' . escapeshellarg($filepath)); exec('sudo chown www-data:www-data /tmp/d39fk36sg55433gd.tmp');
exec('sudo chmod 600 ' . escapeshellarg($filepath)); exec('sudo chmod 664 /tmp/d39fk36sg55433gd.tmp');
//ini file to open
$filepath = '/tmp/d39fk36sg55433gd.tmp';
//after the form submit //after the form submit
if($_POST) { if($_POST) {
$data = $_POST; $data = $_POST;
//update ini file, call function //update ini file, call function
update_ini_file($data, $filepath); update_ini_file($data, $filepath);
} }
//this is the function going to update your ini file //this is the function going to update your ini file
function update_ini_file($data, $filepath) function update_ini_file($data, $filepath) {
{ $content = "";
$content = "";
//parse the ini file using default parse_ini_file() PHP function //parse the ini file using default parse_ini_file() PHP function
$parsed_ini = parse_ini_file($filepath, true); $parsed_ini = parse_ini_file($filepath, true);
foreach($data as $section=>$values) { foreach($data as $section=>$values) {
// UnBreak special cases // UnBreak special cases
$section = str_replace("_", " ", $section); $section = str_replace("_", " ", $section);
$content .= "[".$section."]\n"; $content .= "[".$section."]\n";
//append the values //append the values
foreach($values as $key=>$value) { foreach($values as $key=>$value) {
// Strip CR/LF from values before they reach the INI if ($value == '') {
// line. Same rationale as fulledit_dapnetapi.php: a
// newline inside $value would split into a fresh
// INI line and let an attacker inject extra keys.
$value = str_replace(array("\r", "\n"), "", (string)$value);
if ($value == '') {
$content .= $key."=none\n"; $content .= $key."=none\n";
} else { } else {
$content .= $key."=".$value."\n"; $content .= $key."=".$value."\n";
} }
} }
$content .= "\n"; $content .= "\n";
} }
//write it into file //write it into file
if (!$handle = fopen($filepath, 'w')) { if (!$handle = fopen($filepath, 'w')) {
return false; return false;
} }
$success = fwrite($handle, $content); $success = fwrite($handle, $content);
fclose($handle); fclose($handle);
// Atomic install: mode + owner set in one syscall sequence. // Updates complete - copy the working file back to the proper location
// /etc/bmapi.key holds the BrandMeister API token — mode 600 exec('sudo mount -o remount,rw /'); // Make rootfs writable
// keeps it readable only by www-data (the dashboard user). exec('sudo mv /tmp/d39fk36sg55433gd.tmp /etc/bmapi.key'); // Move the file back
// Owner left as www-data because banner_warnings.inc / bm_links.php / exec('sudo chmod 644 /etc/bmapi.key'); // Set the correct runtime permissions
// bm_manager.php read the file directly via parse_ini_file() exec('sudo chown root:root /etc/bmapi.key'); // Set the owner
// without sudo — switching to root:root here would silently exec('sudo mount -o remount,ro /'); // Make rootfs read-only
// break those reads. (Tightening to root:root is a follow-up
// once the read sites move to a sudo-cat helper.)
exec('sudo mount -o remount,rw /');
exec('sudo install -m 600 -o www-data -g www-data '
. escapeshellarg($filepath) . ' /etc/bmapi.key');
exec('sudo mount -o remount,ro /');
return $success; return $success;
} }
//parse the ini file using default parse_ini_file() PHP function //parse the ini file using default parse_ini_file() PHP function
$parsed_ini = parse_ini_file($filepath, true); $parsed_ini = parse_ini_file($filepath, true);
if (!isset($parsed_ini['key']['apikey'])) { $parsed_ini['key']['apikey'] = ""; } if (!isset($parsed_ini['key']['apikey'])) { $parsed_ini['key']['apikey'] = ""; }
echo '<form action="" method="post">'."\n"; echo '<form action="" method="post">'."\n";
echo csrf_field_html()."\n"; foreach($parsed_ini as $section=>$values) {
foreach($parsed_ini as $section=>$values) { // keep the section as hidden text so we can update once the form submitted
// INI section / key / value all come from /etc/bmapi.key. Same echo "<input type=\"hidden\" value=\"$section\" name=\"$section\" />\n";
// hardening as edit_mmdvmhost.php (#23) but the value lands echo "<div class=\"settings-card\">\n";
// inside a <textarea>...</textarea> body — htmlspecialchars echo "<h3>$section</h3>\n";
// covers both attribute and body contexts safely. Browser // print all other values as input fields, so can edit.
// decodes the named entities on form submit, so legitimate // note the name='' attribute it has both section and key
// values containing `<`, `>`, `&`, `"` round-trip byte-identically. foreach($values as $key=>$value) {
$sectionHtml = htmlspecialchars((string)$section, ENT_QUOTES, 'UTF-8'); if (($key == "Options") || ($value)) {
// keep the section as hidden text so we can update once the form submitted echo "<div class=\"field-row\"><div>$key</div><div><textarea name=\"{$section}[$key]\" cols=\"60\" rows=\"13\">$value</textarea></div></div>\n";
echo "<input type=\"hidden\" value=\"$sectionHtml\" name=\"$sectionHtml\" />\n"; }
echo "<table>\n"; elseif (($key == "Display") && ($value == '')) {
echo "<tr><th colspan=\"2\">$sectionHtml</th></tr>\n"; echo "<div class=\"field-row\"><div>$key</div><div><textarea name=\"{$section}[$key]\" cols=\"60\" rows=\"13\">$value</textarea></div></div>\n";
// print all other values as input fields, so can edit. }
// note the name='' attribute it has both section and key else {
foreach($values as $key=>$value) { echo "<div class=\"field-row\"><div>$key</div><div><textarea name=\"{$section}[$key]\" cols=\"60\" rows=\"13\">$value</textarea></div></div>\n";
$keyHtml = htmlspecialchars((string)$key, ENT_QUOTES, 'UTF-8'); }
$valueHtml = htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8'); }
if (($key == "Options") || ($value)) { echo "</div>\n";
echo "<tr><td align=\"right\" width=\"30%\">$keyHtml</td><td align=\"left\"><textarea name=\"{$sectionHtml}[$keyHtml]\" cols=\"60\" rows=\"13\">$valueHtml</textarea></td></tr>\n"; echo '<input type="submit" value="'.$lang['apply'].'" />'."\n";
} echo "<br />\n";
elseif (($key == "Display") && ($value == '')) { }
echo "<tr><td align=\"right\" width=\"30%\">$keyHtml</td><td align=\"left\"><textarea name=\"{$sectionHtml}[$keyHtml]\" cols=\"60\" rows=\"13\">$valueHtml</textarea></td></tr>\n";
}
else {
echo "<tr><td align=\"right\" width=\"30%\">$keyHtml</td><td align=\"left\"><textarea name=\"{$sectionHtml}[$keyHtml]\" cols=\"60\" rows=\"13\">$valueHtml</textarea></td></tr>\n";
}
}
echo "</table>\n";
echo '<input type="submit" value="'.$lang['apply'].'" />'."\n";
echo "<br />\n";
}
echo "</form>"; echo "</form>";
?> ?>
</div> </div>
<div class="footer">
Pi-Star / Pi-Star Dashboard, &copy; Andy Taylor (MW0MWZ) 2014-<?php echo date("Y"); ?>.<br />
Need help? Click <a style="color: #ffffff;" href="https://www.facebook.com/groups/pistarusergroup/" target="_new">here for the Support Group</a><br />
Get your copy of Pi-Star from <a style="color: #ffffff;" href="http://www.pistar.uk/downloads/" target="_new">here</a>.<br />
</div>
</div> </div>
</body> </body>
</html> </html>
+36 -330
View File
@@ -1,30 +1,4 @@
<?php <?php
/**
* Raw text editor for /etc/crontab.
*
* Drops the parse_ini_file dance — the file is a plain text crontab,
* not an INI. POST data lands in a textarea, gets staged to
* /tmp/<obfuscated>.tmp, then sudo-copied back into /etc/crontab.
* No daemon restart (cron rereads its config automatically).
*
* Operator can edit any cron line, including the pistar-* timer hooks
* — read carefully before saving.
*/
require_once($_SERVER['DOCUMENT_ROOT'].'/config/security_headers.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/csrf.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/banner_warnings.inc');
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 rejects forged POSTs cleanly with 403
// before any state change (sed-i, fopen+fwrite, sudo cp, etc.).
csrf_verify();
// Layer 2 of the default-password protection — see config/banner_warnings.inc.
// MUST run BEFORE any output so header('Location: ...') works.
pistar_warnings_enforce_redirect();
// Load the language support // Load the language support
require_once('../config/language.php'); require_once('../config/language.php');
//Load the Pi-Star Release file //Load the Pi-Star Release file
@@ -43,334 +17,66 @@ require_once('../config/version.php');
<meta name="language" content="English" /> <meta name="language" content="English" />
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<meta name="Author" content="Andrew Taylor (MW0MWZ)" /> <meta name="Author" content="Andrew Taylor (MW0MWZ)" />
<meta name="Description" content="Pi-Star Expert Editor" /> <meta name="Description" content="CDN Expert Editor" />
<meta name="KeyWords" content="Pi-Star" /> <meta name="KeyWords" content="CDN" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="pragma" content="no-cache" /> <meta http-equiv="pragma" content="no-cache" />
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon"> <link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon">
<meta http-equiv="Expires" content="0" /> <meta http-equiv="Expires" content="0" />
<title>Pi-Star - Digital Voice Dashboard - Expert Editor</title> <title>CDN - Digital Voice Dashboard - Expert Editor</title>
<link rel="stylesheet" type="text/css" href="../css/pistar-css.php" /> <link rel="stylesheet" type="text/css" href="../css/pistar-css.php" />
</head> </head>
<body> <body>
<?php pistar_warnings_render(); ?>
<div class="container"> <div class="container">
<?php include './header-menu.inc'; ?> <?php include './header-menu.inc'; ?>
<div class="contentwide"> <div class="contentwide">
<?php <?php
/**
* Cron-content denylist scanner.
*
* The dashboard sits behind HTTP basic auth. An operator (or anyone
* with the basic-auth credential) can edit /etc/crontab through this
* page. Cron runs lines as root, so a malicious cron entry is a
* direct privilege escalation off whatever access the dashboard
* already grants.
*
* This is BASIC HARDENING — not airtight. A determined attacker can
* still bypass via:
*
* - Variable indirection: `* * * * * root $X user` where $X is
* defined in a sourced file.
* - External script paths: a benign-looking `/opt/myapp/run.sh`
* can do anything; we scan the cron line, not the script.
* - Brace expansion: `/usr/bin/{passwd,sh}` produces `passwd`
* after shell expansion but our `\b` boundary doesn't trip on
* the literal `passwd,` substring.
*
* Obfuscation patterns are NOT an accepted bypass — there's no
* legitimate cron use for inline base64 / hex / eval / etc., so
* the second rule block below blocks every common decoder
* (base64 / b64decode / fromhex / xxd / openssl -d / shell hex
* escapes / shell unicode escapes / eval). Operators with a
* legitimate need to encode/decode data should put that logic in
* a script file, where this denylist doesn't apply.
*
* The goal is to catch the obvious-bad patterns described in the
* threat brief: password mutation, direct writes to /etc/shadow /
* /etc/passwd / .htpasswd, and the standard shell-listener /
* reverse-shell idioms (nc, socat exec, /dev/tcp redirects,
* download-pipe-shell, python/perl one-liner shells), plus the
* obfuscation patterns commonly used to wrap them.
*
* Comment lines (starting with `#`) and blank lines are exempt.
* Cron variable assignments (PATH=, MAILTO=, SHELL=) are scanned
* the same as commands — the rules below tolerate the false-positive
* surface for the safety win. Known accepted false positive in the
* wild: `MAILTO=passwd@example.com` triggers the password-command
* rule. Operators with that exact email can use a different alias
* or edit /etc/crontab via SSH to bypass the dashboard guard.
*
* Returns an array of human-readable diagnostics, one per problem
* line. Empty array means OK to save.
*
* @param string $content Raw POSTed cron content (already \r-stripped
* and decoded back to bytes by the browser).
* @return array<int,string>
*/
function pistar_cron_validate($content)
{
$rules = array(
// Password modification commands. The leading character class
// matches start-of-line OR a typical token boundary (whitespace,
// path separator, shell metas, or `=` so we catch X=passwd).
// \b at the end stops "passwords" / "chpasswdtest" matching.
'#(?:^|[\s/;|&`$()=])(passwd|chpasswd|htpasswd)\b#i'
=> 'invokes a password-modification command (passwd / chpasswd / htpasswd)',
// usermod -p sets a password hash directly, no PAM, no audit.
'#\busermod\b[^\n]*\s-p\b#i'
=> 'usermod -p sets a password hash directly',
// Reference to any auth file. Even reading these from cron is
// unusual; writing is highly suspicious. Catches /etc/shadow,
// /etc/passwd, /etc/passwd-, and any .htpasswd path including
// /var/www/.htpasswd.
'#(/etc/shadow|/etc/passwd|\.htpasswd)\b#i'
=> 'references an authentication file path',
// Netcat — block any invocation. Operators with a legitimate
// use should script it outside cron. Blocking outright is
// simpler than disambiguating safe vs. unsafe nc flags.
'#(?:^|[\s/;|&`$()=])(nc|ncat|netcat)\b#i'
=> 'invokes nc / ncat / netcat',
// socat with EXEC: or SYSTEM: targets — process-spawning.
'#\bsocat\b[^\n]*\b(EXEC|SYSTEM):#i'
=> 'socat with EXEC/SYSTEM token (spawns a process)',
// Bash builtin /dev/tcp / /dev/udp redirect — classic reverse-
// shell idiom (`bash -i >& /dev/tcp/h/p 0>&1`). No legitimate
// cron use.
'#/dev/(?:tcp|udp)/#'
=> 'reads or writes /dev/tcp/* or /dev/udp/* (reverse-shell pattern)',
// Download-pipe-shell. Catches `curl ... | sh`, `wget ... | bash`,
// etc. The `[^|\n]*` between the fetch tool and the pipe stops
// a stray pipe later in a long line giving false positives.
'#\b(curl|wget|fetch)\b[^|\n]*\|\s*(?:bash|sh|zsh|ksh|dash|exec)\b#i'
=> 'pipes downloaded content directly into a shell',
// python reverse-shell one-liners — heuristic on imports/calls
// commonly used in the published payloads.
'#\bpython[23]?\b[^\n]*-c[^\n]*(?:socket\.socket|os\.dup2)#i'
=> 'python reverse-shell pattern',
// perl reverse-shell one-liner — `perl -e \'use Socket; ...\'`
'#\bperl\b[^\n]*-e[^\n]*\bSocket\b#i'
=> 'perl reverse-shell pattern',
// ====== Obfuscation / decoder patterns ======
// No legitimate cron use for inline encoded payloads. Operators
// who need to encode/decode something should do it in a script,
// not in a cron line. Catches the common payload-wrapper idioms.
// base64 in any form — the command, the python module, perl's
// decode_base64, etc. Substring match (no \b) so it also
// catches "decode_base64", "MIME::Base64", "base64.b64decode".
'#base64#i'
=> 'references base64 (no legitimate cron use; payload-obfuscation idiom)',
// python's base64 module exposes b64decode / b64encode aliases
// that don't contain the literal "base64" substring.
'#\bb64(?:decode|encode)\b#i'
=> 'python b64decode/b64encode (payload decoder)',
// python's bytes.fromhex(...) — turns a hex string into bytes
// for runtime execution.
'#\bfromhex\b#i'
=> 'fromhex (hex payload decoder)',
// xxd dumps / reverses hex. Reverse mode (-r) is a payload
// decoder; forward mode is hex-dumping which has no cron use.
// Block the binary outright.
'#\bxxd\b#i'
=> 'invokes xxd (hex dump / reverse-mode payload decoder)',
// Shell hex / unicode escape literals. `printf \'\x70\x61\x73\x73\'`
// spells "pass" at runtime — pure obfuscation in cron context.
'#\\\\x[0-9a-fA-F]{2}#'
=> 'shell hex-escape literal (\\xNN — payload obfuscation)',
'#\\\\u[0-9a-fA-F]{4}#'
=> 'shell unicode-escape literal (\\uNNNN — payload obfuscation)',
// eval interprets its argument as shell — opaque to anyone
// reading the cron line.
'#(?:^|[\s/;|&`$()=])eval\b#i'
=> 'invokes eval (opaque shell-string execution)',
// openssl -d in any context (enc -d, base64 -d, ...) is a
// generic decoder.
'#\bopenssl\b[^\n]*\s-d\b#i'
=> 'openssl with -d (decoder)',
);
$blockers = array();
$lines = explode("\n", $content);
foreach ($lines as $i => $line) {
$trimmed = ltrim($line);
if ($trimmed === '' || $trimmed[0] === '#') {
continue;
}
foreach ($rules as $pattern => $reason) {
if (preg_match($pattern, $line)) {
$blockers[] = sprintf(
'line %d (%s): %s',
$i + 1,
$reason,
substr(trim($line), 0, 100)
);
break; // one diagnostic per line is enough
}
}
}
return $blockers;
}
$cronBlockers = array();
$saveError = false;
$saveOk = false;
$readError = false;
if(isset($_POST['data'])) { if(isset($_POST['data'])) {
// Normalise CRLF → LF before validation so a Windows browser's // File Wrangling
// submission scans byte-identical to the on-disk form. exec('sudo cp /etc/crontab /tmp/a8h4d8n3c83h4.tmp');
$rawData = str_replace("\r", "", (string)$_POST['data']); exec('sudo chown www-data:www-data /tmp/a8h4d8n3c83h4.tmp');
$cronBlockers = pistar_cron_validate($rawData); exec('sudo chmod 664 /tmp/a8h4d8n3c83h4.tmp');
if (!empty($cronBlockers)) { // Open the file and write the data
// Validation failed — DO NOT touch /etc/crontab. Surface $filepath = '/tmp/a8h4d8n3c83h4.tmp';
// the diagnostics in the page and re-render the form $fh = fopen($filepath, 'w');
// with the operator's submitted content so they can fwrite($fh, str_replace("\r", "", $_POST['data']));
// fix in place without retyping. fclose($fh);
$theData = $rawData; exec('sudo mount -o remount,rw /');
error_log('Pi-Star fulledit_cron.php: rejected save with ' exec('sudo cp /tmp/a8h4d8n3c83h4.tmp /etc/crontab');
. count($cronBlockers) . ' blocked line(s)'); exec('sudo chmod 644 /etc/crontab');
} else { exec('sudo chown root:root /etc/crontab');
// A3-3 — see edit_ircddbgateway.php for the full TOCTOU exec('sudo mount -o remount,ro /');
// rationale. Per-request random staging path defeats
// the predictable-name pre-create / symlink class.
$filepath = tempnam('/tmp', 'pistar-edit-');
register_shutdown_function(function() use ($filepath) { @unlink($filepath); });
$fh = fopen($filepath, 'w');
if ($fh !== false) {
fwrite($fh, $rawData);
fclose($fh);
}
// Write through the one sudoers-allowlisted primitive for // Re-open the file and read it
// this file: `sudo sed -i … /etc/crontab`. (The previous
// `install … /etc/crontab` had no matching sudoers rule and
// was silently rejected — saves looked successful but never
// touched disk.) The operator's bytes are poured over the
// file with sed's `1r <file>` + `d` idiom: read the staged
// temp file in on the first cycle, delete every original
// line, leaving an exact byte-for-byte copy. The operator's
// content never enters the sed script — only the temp file
// PATH does — so there is no sed/shell-injection surface.
// sed -i preserves the existing root:root 644 (verified on a
// live host), so no follow-up chown/chmod is needed.
// $sedOut is an unused placeholder (sed -i is silent on
// success); the save status we act on is the exit code $rc
// plus the read-back comparison below.
$rc = 0;
$sedOut = array();
exec('sudo mount -o remount,rw /');
exec('sudo sed -i -e ' . escapeshellarg('1r ' . $filepath) . ' -e d /etc/crontab', $sedOut, $rc);
exec('sudo mount -o remount,ro /'); // always re-protect the rootfs
// Loud-failure check. The old code unconditionally rendered
// "as saved", which is exactly how the broken write hid for
// weeks. Read /etc/crontab back (world-readable) and compare
// byte-for-byte: a non-zero sed exit OR any mismatch means
// the save did not land. This also covers the degenerate
// empty-crontab corner, where `1r` never fires and the file
// would be left empty.
$onDisk = @file_get_contents('/etc/crontab');
if ($rc !== 0 || $onDisk !== $rawData) {
$saveError = true;
error_log('Pi-Star fulledit_cron.php: crontab save FAILED (sed rc='
. $rc . ', readback '
. ($onDisk === $rawData ? 'matched' : 'MISMATCHED') . ')');
} else {
$saveOk = true;
}
// Re-render the submitted content either way so a failed
// save never discards the operator's edits.
$theData = $rawData;
}
} else {
// A3-3 — see edit_ircddbgateway.php for the full TOCTOU rationale.
$filepath = tempnam('/tmp', 'pistar-edit-');
register_shutdown_function(function() use ($filepath) { @unlink($filepath); });
exec('sudo cp /etc/crontab ' . escapeshellarg($filepath));
exec('sudo chown www-data:www-data ' . escapeshellarg($filepath));
exec('sudo chmod 600 ' . escapeshellarg($filepath));
$fh = fopen($filepath, 'r'); $fh = fopen($filepath, 'r');
if ($fh === false) { $theData = fread($fh, filesize($filepath));
// The privileged copy of /etc/crontab could not be staged
// (sudo cp/chown/chmod failed). Surface a read error and show } else {
// an empty editor rather than a blank-but-editable textarea — // File Wrangling
// saving from the latter would overwrite /etc/crontab with exec('sudo cp /etc/crontab /tmp/a8h4d8n3c83h4.tmp');
// nothing. exec('sudo chown www-data:www-data /tmp/a8h4d8n3c83h4.tmp');
$theData = ''; exec('sudo chmod 664 /tmp/a8h4d8n3c83h4.tmp');
$readError = true;
error_log('Pi-Star fulledit_cron.php: could not stage /etc/crontab for reading'); // Open the file and read it
} else { $filepath = '/tmp/a8h4d8n3c83h4.tmp';
$sz = filesize($filepath); $fh = fopen($filepath, 'r');
$theData = $sz > 0 ? fread($fh, $sz) : ''; $theData = fread($fh, filesize($filepath));
fclose($fh);
}
} }
fclose($fh);
?> ?>
<?php if (!empty($cronBlockers)) { ?> <h2>System Cron (Full Edit)</h2>
<div style="background-color: #ff9090; color: #f01010; padding: 10px; margin: 0 0 10px 0;"> <div class="settings-card" style="padding-top:16px;">
<b>Save rejected &mdash; the cron editor blocks lines that match common privilege-escalation patterns.</b>
<ul style="margin: 8px 0 0 16px;">
<?php foreach ($cronBlockers as $b) {
echo '<li>' . htmlspecialchars($b, ENT_QUOTES, 'UTF-8') . "</li>\n";
} ?>
</ul>
<p style="margin: 8px 0 0 0;">Edit the offending line(s) and re-submit, or revert your changes.
If you have a legitimate need to schedule one of these patterns, use SSH and edit
<code>/etc/crontab</code> directly &mdash; the dashboard editor enforces these checks
to limit the damage of stolen dashboard credentials.</p>
</div>
<?php } ?>
<?php if ($saveError) { ?>
<div style="background-color: #ff9090; color: #f01010; padding: 10px; margin: 0 0 10px 0;">
<b>Save failed &mdash; /etc/crontab was not updated.</b>
<p style="margin: 8px 0 0 0;">The write was rejected or the file did not match after saving. Your edits are
preserved below &mdash; try again, or edit <code>/etc/crontab</code> directly over SSH.</p>
</div>
<?php } elseif ($saveOk) { ?>
<div id="cronSaveOk" style="background-color: #c0f0c0; color: #106010; padding: 10px; margin: 0 0 10px 0;">
<b>Crontab saved.</b>
</div>
<script>
// Auto-dismiss the success banner after 5s. The error banners are left in
// place on purpose so a failed save can never scroll away unnoticed.
setTimeout(function () {
var el = document.getElementById('cronSaveOk');
if (!el) { return; }
// Honour reduced-motion preferences: snap rather than fade.
var reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
el.style.transition = reduce ? 'none' : 'opacity 0.6s ease';
el.style.opacity = '0';
setTimeout(function () { if (el.parentNode) { el.parentNode.removeChild(el); } }, 650);
}, 5000);
</script>
<?php } ?>
<?php if ($readError) { ?>
<div style="background-color: #ff9090; color: #f01010; padding: 10px; margin: 0 0 10px 0;">
<b>Could not read /etc/crontab.</b>
<p style="margin: 8px 0 0 0;">The current crontab could not be loaded, so the editor below is empty.
<b>Do not save</b> &mdash; doing so would overwrite /etc/crontab with nothing. Reload the page, or edit
<code>/etc/crontab</code> directly over SSH.</p>
</div>
<?php } ?>
<form name="test" method="post" action=""> <form name="test" method="post" action="">
<?php csrf_field(); ?> <textarea class="raw-editor" name="data"><?php echo $theData; ?></textarea><br />
<textarea name="data" cols="80" rows="45"><?php echo htmlspecialchars((string)$theData, ENT_QUOTES, 'UTF-8'); ?></textarea><br /> <div class="field-actions"><input type="submit" name="submit" value="<?php echo $lang['apply']; ?>" /></div>
<input type="submit" name="submit" value="<?php echo $lang['apply']; ?>" />
</form> </form>
</div>
</div> </div>
<div class="footer">
Pi-Star / Pi-Star Dashboard, &copy; Andy Taylor (MW0MWZ) 2014-<?php echo date("Y"); ?>.<br />
Need help? Click <a style="color: #ffffff;" href="https://www.facebook.com/groups/pistarusergroup/" target="_new">here for the Support Group</a><br />
Get your copy of Pi-Star from <a style="color: #ffffff;" href="http://www.pistar.uk/downloads/" target="_new">here</a>.<br />
</div>
</div> </div>
</body> </body>
</html> </html>
+72 -122
View File
@@ -1,26 +1,4 @@
<?php <?php
/**
* Raw text editor for /etc/dapnetapi.key (DAPNET credentials).
*
* Same first-save-via-shell-echo pattern as fulledit_bmapikey.php.
* Read by dapnetgateway at startup; no daemon restart from this
* editor.
*/
require_once($_SERVER['DOCUMENT_ROOT'].'/config/security_headers.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/csrf.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/banner_warnings.inc');
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 rejects forged POSTs cleanly with 403
// before any state change (sed-i, fopen+fwrite, sudo cp, etc.).
csrf_verify();
// Layer 2 of the default-password protection — see config/banner_warnings.inc.
// MUST run BEFORE any output so header('Location: ...') works.
pistar_warnings_enforce_redirect();
// Load the language support // Load the language support
require_once('../config/language.php'); require_once('../config/language.php');
//Load the Pi-Star Release file //Load the Pi-Star Release file
@@ -39,143 +17,115 @@ require_once('../config/version.php');
<meta name="language" content="English" /> <meta name="language" content="English" />
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<meta name="Author" content="Andrew Taylor (MW0MWZ)" /> <meta name="Author" content="Andrew Taylor (MW0MWZ)" />
<meta name="Description" content="Pi-Star Expert Editor" /> <meta name="Description" content="CDN Expert Editor" />
<meta name="KeyWords" content="Pi-Star" /> <meta name="KeyWords" content="CDN" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="pragma" content="no-cache" /> <meta http-equiv="pragma" content="no-cache" />
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon"> <link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon">
<meta http-equiv="Expires" content="0" /> <meta http-equiv="Expires" content="0" />
<title>Pi-Star - Digital Voice Dashboard - Expert Editor</title> <title>CDN - Digital Voice Dashboard - Expert Editor</title>
<link rel="stylesheet" type="text/css" href="../css/pistar-css.php" /> <link rel="stylesheet" type="text/css" href="../css/pistar-css.php" />
</head> </head>
<body> <body>
<?php pistar_warnings_render(); ?>
<div class="container"> <div class="container">
<?php include './header-menu.inc'; ?> <?php include './header-menu.inc'; ?>
<div class="contentwide"> <div class="contentwide">
<?php <?php
// A3-3 — see edit_ircddbgateway.php for the full TOCTOU rationale. // Make the bare config if we dont have one
// /etc/dapnetapi.key holds DAPNET credentials, so the random-name
// TOCTOU defence is more important here than for the other editors.
$filepath = tempnam('/tmp', 'pistar-edit-');
register_shutdown_function(function() use ($filepath) { @unlink($filepath); });
if (file_exists('/etc/dapnetapi.key')) { if (file_exists('/etc/dapnetapi.key')) {
exec('sudo cp /etc/dapnetapi.key ' . escapeshellarg($filepath)); exec('sudo cp /etc/dapnetapi.key /tmp/jsADGHwf9sj294.tmp');
exec('sudo chown www-data:www-data /tmp/jsADGHwf9sj294.tmp');
} else { } else {
// Seed the staging file with the empty-config skeleton. exec('sudo touch /tmp/jsADGHwf9sj294.tmp');
// tempnam already created it owned by www-data, so PHP-side exec('sudo chown www-data:www-data /tmp/jsADGHwf9sj294.tmp');
// file_put_contents writes through directly. exec('echo "[DAPNETAPI]" > /tmp/jsADGHwf9sj294.tmp');
file_put_contents( exec('echo "USER=" >> /tmp/jsADGHwf9sj294.tmp');
$filepath, exec('echo "PASS=" >> /tmp/jsADGHwf9sj294.tmp');
"[DAPNETAPI]\nUSER=\nPASS=\nTRXAREA=\n" exec('echo "TRXAREA=" >> /tmp/jsADGHwf9sj294.tmp');
);
} }
exec('sudo chown www-data:www-data ' . escapeshellarg($filepath));
exec('sudo chmod 600 ' . escapeshellarg($filepath)); //Do some file wrangling...
exec('sudo chmod 664 /tmp/jsADGHwf9sj294.tmp');
//ini file to open
$filepath = '/tmp/jsADGHwf9sj294.tmp';
//after the form submit //after the form submit
if($_POST) { if($_POST) {
$data = $_POST; $data = $_POST;
//update ini file, call function //update ini file, call function
update_ini_file($data, $filepath); update_ini_file($data, $filepath);
} }
//this is the function going to update your ini file //this is the function going to update your ini file
function update_ini_file($data, $filepath) function update_ini_file($data, $filepath) {
{ $content = "";
$content = "";
//parse the ini file to get the sections //parse the ini file to get the sections
//parse the ini file using default parse_ini_file() PHP function //parse the ini file using default parse_ini_file() PHP function
$parsed_ini = parse_ini_file($filepath, true); $parsed_ini = parse_ini_file($filepath, true);
foreach($data as $section=>$values) { foreach($data as $section=>$values) {
// UnBreak special cases // UnBreak special cases
$section = str_replace("_", " ", $section); $section = str_replace("_", " ", $section);
$content .= "[".$section."]\n"; $content .= "[".$section."]\n";
//append the values //append the values
foreach($values as $key=>$value) { foreach($values as $key=>$value) {
// Strip CR/LF from values before they land in the INI $content .= $key."=".$value."\n";
// file. The save handler writes `$key=$value\n` and a }
// newline inside $value would split the value into a $content .= "\n";
// new INI line, allowing injection of arbitrary }
// additional keys (e.g. `value=foo\nDEBUG=1`). On a
// single-operator device the practical risk is low
// but the sanitiser is one line.
$value = str_replace(array("\r", "\n"), "", (string)$value);
$content .= $key."=".$value."\n";
}
$content .= "\n";
}
//write it into file //write it into file
if (!$handle = fopen($filepath, 'w')) { if (!$handle = fopen($filepath, 'w')) {
return false; return false;
} }
$success = fwrite($handle, $content); $success = fwrite($handle, $content);
fclose($handle); fclose($handle);
// Atomic install: mode + owner set in one syscall sequence. // Updates complete - copy the working file back to the proper location
// /etc/dapnetapi.key holds DAPNET credentials — mode 600 keeps exec('sudo mount -o remount,rw /'); // Make rootfs writable
// them readable only by www-data. Owner left as www-data exec('sudo cp /tmp/jsADGHwf9sj294.tmp /etc/dapnetapi.key'); // Move the file back
// because the dapnetgateway daemon reads via the dashboard exec('sudo chmod 644 /etc/dapnetapi.key'); // Set the correct runtime permissions
// (and dashboard reads it directly without sudo elsewhere); exec('sudo chown root:root /etc/dapnetapi.key'); // Set the owner
// see fulledit_bmapikey.php for the same rationale. exec('sudo mount -o remount,ro /'); // Make rootfs read-only
exec('sudo mount -o remount,rw /');
exec('sudo install -m 600 -o www-data -g www-data '
. escapeshellarg($filepath) . ' /etc/dapnetapi.key');
exec('sudo mount -o remount,ro /');
return $success; return $success;
} }
//parse the ini file using default parse_ini_file() PHP function //parse the ini file using default parse_ini_file() PHP function
$parsed_ini = parse_ini_file($filepath, true); $parsed_ini = parse_ini_file($filepath, true);
echo '<form action="" method="post">'."\n"; echo '<form action="" method="post">'."\n";
echo csrf_field_html()."\n"; foreach($parsed_ini as $section=>$values) {
foreach($parsed_ini as $section=>$values) { // keep the section as hidden text so we can update once the form submitted
// Same hardening as edit_mmdvmhost.php (#23): escape every INI echo "<input type=\"hidden\" value=\"$section\" name=\"$section\" />\n";
// section / key / value before HTML interpolation. The save echo "<div class=\"settings-card\">\n";
// handler writes POST bytes verbatim so legitimate values echo "<h3>$section</h3>\n";
// (including any with `"` or `<`) round-trip byte-identically. // print all other values as input fields, so can edit.
$sectionHtml = htmlspecialchars((string)$section, ENT_QUOTES, 'UTF-8'); // note the name='' attribute it has both section and key
// keep the section as hidden text so we can update once the form submitted foreach($values as $key=>$value) {
echo "<input type=\"hidden\" value=\"$sectionHtml\" name=\"$sectionHtml\" />\n"; if (($key == "Options") || ($value)) {
echo "<table>\n"; echo "<div class=\"field-row\"><div>$key</div><div><input type=\"text\" name=\"{$section}[$key]\" value=\"$value\" /></div></div>\n";
echo "<tr><th colspan=\"2\">$sectionHtml</th></tr>\n"; }
// print all other values as input fields, so can edit. elseif (($key == "Display") && ($value == '')) {
// note the name='' attribute it has both section and key echo "<div class=\"field-row\"><div>$key</div><div><input type=\"text\" name=\"{$section}[$key]\" value=\"None\" /></div></div>\n";
foreach($values as $key=>$value) { }
$keyHtml = htmlspecialchars((string)$key, ENT_QUOTES, 'UTF-8'); else {
$valueHtml = htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8'); echo "<div class=\"field-row\"><div>$key</div><div><input type=\"text\" name=\"{$section}[$key]\" value=\"0\" /></div></div>\n";
if (($key == "Options") || ($value)) { }
echo "<tr><td align=\"right\" width=\"30%\">$keyHtml</td><td align=\"left\"><input type=\"text\" name=\"{$sectionHtml}[$keyHtml]\" value=\"$valueHtml\" /></td></tr>\n"; }
} echo "</div>\n";
elseif (($key == "Display") && ($value == '')) { echo '<input type="submit" value="'.$lang['apply'].'" />'."\n";
echo "<tr><td align=\"right\" width=\"30%\">$keyHtml</td><td align=\"left\"><input type=\"text\" name=\"{$sectionHtml}[$keyHtml]\" value=\"None\" /></td></tr>\n"; echo "<br />\n";
} }
else {
echo "<tr><td align=\"right\" width=\"30%\">$keyHtml</td><td align=\"left\"><input type=\"text\" name=\"{$sectionHtml}[$keyHtml]\" value=\"0\" /></td></tr>\n";
}
}
echo "</table>\n";
echo '<input type="submit" value="'.$lang['apply'].'" />'."\n";
echo "<br />\n";
}
echo "</form>"; echo "</form>";
?> ?>
</div> </div>
<div class="footer">
Pi-Star / Pi-Star Dashboard, &copy; Andy Taylor (MW0MWZ) 2014-<?php echo date("Y"); ?>.<br />
Need help? Click <a style="color: #ffffff;" href="https://www.facebook.com/groups/pistarusergroup/" target="_new">here for the Support Group</a><br />
Get your copy of Pi-Star from <a style="color: #ffffff;" href="http://www.pistar.uk/downloads/" target="_new">here</a>.<br />
</div>
</div> </div>
</body> </body>
</html> </html>
+35 -56
View File
@@ -1,28 +1,4 @@
<?php <?php
/**
* Raw text editor for /etc/dmrgateway.
*
* Companion to edit_dmrgateway.php — same target file, but this view
* exposes the entire INI as a single textarea so the operator can
* make edits the structured form doesn't cover. POST data is staged
* to /tmp/<obfuscated>.tmp then sudo-copied back to /etc/dmrgateway.
* Restarts BOTH mmdvmhost.service AND dmrgateway.service after save.
*/
require_once($_SERVER['DOCUMENT_ROOT'].'/config/security_headers.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/csrf.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/banner_warnings.inc');
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 rejects forged POSTs cleanly with 403
// before any state change (sed-i, fopen+fwrite, sudo cp, etc.).
csrf_verify();
// Layer 2 of the default-password protection — see config/banner_warnings.inc.
// MUST run BEFORE any output so header('Location: ...') works.
pistar_warnings_enforce_redirect();
// Load the language support // Load the language support
require_once('../config/language.php'); require_once('../config/language.php');
//Load the Pi-Star Release file //Load the Pi-Star Release file
@@ -41,67 +17,70 @@ require_once('../config/version.php');
<meta name="language" content="English" /> <meta name="language" content="English" />
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<meta name="Author" content="Andrew Taylor (MW0MWZ)" /> <meta name="Author" content="Andrew Taylor (MW0MWZ)" />
<meta name="Description" content="Pi-Star Expert Editor" /> <meta name="Description" content="CDN Expert Editor" />
<meta name="KeyWords" content="Pi-Star" /> <meta name="KeyWords" content="CDN" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="pragma" content="no-cache" /> <meta http-equiv="pragma" content="no-cache" />
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon"> <link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon">
<meta http-equiv="Expires" content="0" /> <meta http-equiv="Expires" content="0" />
<title>Pi-Star - Digital Voice Dashboard - Expert Editor</title> <title>CDN - Digital Voice Dashboard - Expert Editor</title>
<link rel="stylesheet" type="text/css" href="../css/pistar-css.php" /> <link rel="stylesheet" type="text/css" href="../css/pistar-css.php" />
</head> </head>
<body> <body>
<?php pistar_warnings_render(); ?>
<div class="container"> <div class="container">
<?php include './header-menu.inc'; ?> <?php include './header-menu.inc'; ?>
<div class="contentwide"> <div class="contentwide">
<?php <?php
// A3-3 — see edit_ircddbgateway.php for the full TOCTOU rationale.
$filepath = tempnam('/tmp', 'pistar-edit-');
register_shutdown_function(function() use ($filepath) { @unlink($filepath); });
exec('sudo cp /etc/dmrgateway ' . escapeshellarg($filepath));
exec('sudo chown www-data:www-data ' . escapeshellarg($filepath));
exec('sudo chmod 600 ' . escapeshellarg($filepath));
if(isset($_POST['data'])) { if(isset($_POST['data'])) {
// Write submitted data into the staging file. // File Wrangling
exec('sudo cp /etc/dmrgateway /tmp/fmehg65694eg.tmp');
exec('sudo chown www-data:www-data /tmp/fmehg65694eg.tmp');
exec('sudo chmod 664 /tmp/fmehg65694eg.tmp');
// Open the file and write the data
$filepath = '/tmp/fmehg65694eg.tmp';
$fh = fopen($filepath, 'w'); $fh = fopen($filepath, 'w');
fwrite($fh, $_POST['data']); fwrite($fh, $_POST['data']);
fclose($fh); fclose($fh);
// L-5: atomic install replaces the prior cp + chmod + chown
// triplet (rejected by the tightened sudoers — see
// edit_mmdvmhost.php for the full rationale).
exec('sudo mount -o remount,rw /'); exec('sudo mount -o remount,rw /');
exec('sudo install -m 644 -o root -g root ' exec('sudo cp /tmp/fmehg65694eg.tmp /etc/dmrgateway');
. escapeshellarg($filepath) . ' /etc/dmrgateway'); exec('sudo chmod 644 /etc/dmrgateway');
exec('sudo chown root:root /etc/dmrgateway');
exec('sudo mount -o remount,ro /'); exec('sudo mount -o remount,ro /');
// Reload the affected daemon // Reload the affected daemon
exec('sudo systemctl restart mmdvmhost.service'); // Reload MMDVMHost exec('sudo systemctl restart mmdvmhost.service'); // Reload MMDVMHost
exec('sudo systemctl restart dmrgateway.service'); // Reload DMRGateway exec('sudo systemctl restart dmrgateway.service'); // Reload DMRGateway
}
// Re-read for the form's textarea. // Re-open the file and read it
$fh = fopen($filepath, 'r'); $fh = fopen($filepath, 'r');
$theData = fread($fh, filesize($filepath)); $theData = fread($fh, filesize($filepath));
} else {
// File Wrangling
exec('sudo cp /etc/dmrgateway /tmp/fmehg65694eg.tmp');
exec('sudo chown www-data:www-data /tmp/fmehg65694eg.tmp');
exec('sudo chmod 664 /tmp/fmehg65694eg.tmp');
// Open the file and read it
$filepath = '/tmp/fmehg65694eg.tmp';
$fh = fopen($filepath, 'r');
$theData = fread($fh, filesize($filepath));
}
fclose($fh); fclose($fh);
?> ?>
<h2>DMR Gateway (Full Edit)</h2>
<div class="settings-card" style="padding-top:16px;">
<form name="test" method="post" action=""> <form name="test" method="post" action="">
<?php csrf_field(); ?> <textarea class="raw-editor" name="data"><?php echo $theData; ?></textarea><br />
<textarea name="data" cols="80" rows="45"><?php echo htmlspecialchars((string)$theData, ENT_QUOTES, 'UTF-8'); ?></textarea><br /> <div class="field-actions"><input type="submit" name="submit" value="<?php echo $lang['apply']; ?>" /></div>
<input type="submit" name="submit" value="<?php echo $lang['apply']; ?>" />
</form> </form>
</div>
</div> </div>
<div class="footer">
Pi-Star / Pi-Star Dashboard, &copy; Andy Taylor (MW0MWZ) 2014-<?php echo date("Y"); ?>.<br />
Need help? Click <a style="color: #ffffff;" href="https://www.facebook.com/groups/pistarusergroup/" target="_new">here for the Support Group</a><br />
Get your copy of Pi-Star from <a style="color: #ffffff;" href="http://www.pistar.uk/downloads/" target="_new">here</a>.<br />
</div>
</div> </div>
</body> </body>
</html> </html>
+34 -60
View File
@@ -1,26 +1,4 @@
<?php <?php
/**
* Raw text editor for /etc/pistar-remote.
*
* Pi-Star Remote-Control daemon config (DTMF-driven actions, command
* mappings, etc.). Saved via the standard staged-write pattern;
* daemon: pistar-remote.service.
*/
require_once($_SERVER['DOCUMENT_ROOT'].'/config/security_headers.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/csrf.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/banner_warnings.inc');
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 rejects forged POSTs cleanly with 403
// before any state change (sed-i, fopen+fwrite, sudo cp, etc.).
csrf_verify();
// Layer 2 of the default-password protection — see config/banner_warnings.inc.
// MUST run BEFORE any output so header('Location: ...') works.
pistar_warnings_enforce_redirect();
// Load the language support // Load the language support
require_once('../config/language.php'); require_once('../config/language.php');
//Load the Pi-Star Release file //Load the Pi-Star Release file
@@ -39,73 +17,69 @@ require_once('../config/version.php');
<meta name="language" content="English" /> <meta name="language" content="English" />
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<meta name="Author" content="Andrew Taylor (MW0MWZ)" /> <meta name="Author" content="Andrew Taylor (MW0MWZ)" />
<meta name="Description" content="Pi-Star Expert Editor" /> <meta name="Description" content="CDN Expert Editor" />
<meta name="KeyWords" content="Pi-Star" /> <meta name="KeyWords" content="CDN" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="pragma" content="no-cache" /> <meta http-equiv="pragma" content="no-cache" />
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon"> <link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon">
<meta http-equiv="Expires" content="0" /> <meta http-equiv="Expires" content="0" />
<title>Pi-Star - Digital Voice Dashboard - Expert Editor</title> <title>CDN - Digital Voice Dashboard - Expert Editor</title>
<link rel="stylesheet" type="text/css" href="../css/pistar-css.php" /> <link rel="stylesheet" type="text/css" href="../css/pistar-css.php" />
</head> </head>
<body> <body>
<?php pistar_warnings_render(); ?>
<div class="container"> <div class="container">
<?php include './header-menu.inc'; ?> <?php include './header-menu.inc'; ?>
<div class="contentwide"> <div class="contentwide">
<?php <?php
// A3-3 — see edit_ircddbgateway.php for the full TOCTOU rationale.
// tempnam() up front so both POST and GET branches share the same
// per-request random staging path. Cleanup is registered once.
$filepath = tempnam('/tmp', 'pistar-edit-');
register_shutdown_function(function() use ($filepath) { @unlink($filepath); });
exec('sudo cp /etc/pistar-remote ' . escapeshellarg($filepath));
exec('sudo chown www-data:www-data ' . escapeshellarg($filepath));
exec('sudo chmod 600 ' . escapeshellarg($filepath));
if(isset($_POST['data'])) { if(isset($_POST['data'])) {
// Write submitted data into the staging file. // File Wrangling
exec('sudo cp /etc/pistar-remote /tmp/fmehg65934eg.tmp');
exec('sudo chown www-data:www-data /tmp/fmehg65934eg.tmp');
exec('sudo chmod 664 /tmp/fmehg65934eg.tmp');
// Open the file and write the data
$filepath = '/tmp/fmehg65934eg.tmp';
$fh = fopen($filepath, 'w'); $fh = fopen($filepath, 'w');
fwrite($fh, $_POST['data']); fwrite($fh, $_POST['data']);
fclose($fh); fclose($fh);
// Atomic install: content + mode + owner set in one syscall
// sequence. /etc/pistar-remote is read by the pistar-remote
// service daemon and by dashboard pages via parse_ini_file
// (no sudo); 644 root:root keeps both working — same target
// as the bmapikey/dapnetapi B5 migration.
exec('sudo mount -o remount,rw /'); exec('sudo mount -o remount,rw /');
exec('sudo install -m 644 -o root -g root ' exec('sudo cp /tmp/fmehg65934eg.tmp /etc/pistar-remote');
. escapeshellarg($filepath) . ' /etc/pistar-remote'); exec('sudo chmod 644 /etc/pistar-remote');
exec('sudo chown root:root /etc/pistar-remote');
exec('sudo mount -o remount,ro /'); exec('sudo mount -o remount,ro /');
// Reload the affected daemon // Reload the affected daemon
exec('sudo systemctl restart pistar-remote.service'); // Reload the daemon exec('sudo systemctl restart pistar-remote.service'); // Reload the daemon
}
// Re-read the staging file for the form's textarea — in the POST // Re-open the file and read it
// branch this returns what the operator just submitted (and what's $fh = fopen($filepath, 'r');
// now in /etc); in the GET branch it returns the unmodified /etc $theData = fread($fh, filesize($filepath));
// content.
$fh = fopen($filepath, 'r'); } else {
$theData = fread($fh, filesize($filepath)); // File Wrangling
exec('sudo cp /etc/pistar-remote /tmp/fmehg65934eg.tmp');
exec('sudo chown www-data:www-data /tmp/fmehg65934eg.tmp');
exec('sudo chmod 664 /tmp/fmehg65934eg.tmp');
// Open the file and read it
$filepath = '/tmp/fmehg65934eg.tmp';
$fh = fopen($filepath, 'r');
$theData = fread($fh, filesize($filepath));
}
fclose($fh); fclose($fh);
?> ?>
<h2>PiStar-Remote (Full Edit)</h2>
<div class="settings-card" style="padding-top:16px;">
<form name="test" method="post" action=""> <form name="test" method="post" action="">
<?php csrf_field(); ?> <textarea class="raw-editor" name="data"><?php echo $theData; ?></textarea><br />
<textarea name="data" cols="80" rows="45"><?php echo htmlspecialchars((string)$theData, ENT_QUOTES, 'UTF-8'); ?></textarea><br /> <div class="field-actions"><input type="submit" name="submit" value="<?php echo $lang['apply']; ?>" /></div>
<input type="submit" name="submit" value="<?php echo $lang['apply']; ?>" />
</form> </form>
</div>
</div> </div>
<div class="footer">
Pi-Star / Pi-Star Dashboard, &copy; Andy Taylor (MW0MWZ) 2014-<?php echo date("Y"); ?>.<br />
Need help? Click <a style="color: #ffffff;" href="https://www.facebook.com/groups/pistarusergroup/" target="_new">here for the Support Group</a><br />
Get your copy of Pi-Star from <a style="color: #ffffff;" href="http://www.pistar.uk/downloads/" target="_new">here</a>.<br />
</div>
</div> </div>
</body> </body>
</html> </html>
+33 -53
View File
@@ -1,26 +1,4 @@
<?php <?php
/**
* Raw text editor for /usr/local/etc/RSSI.dat.
*
* The modem RSSI calibration table — non-/etc location, edited the
* same way as the other fulledit_* files. No daemon restart (MMDVMHost
* re-reads RSSI.dat on its own).
*/
require_once($_SERVER['DOCUMENT_ROOT'].'/config/security_headers.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/csrf.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/banner_warnings.inc');
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 rejects forged POSTs cleanly with 403
// before any state change (sed-i, fopen+fwrite, sudo cp, etc.).
csrf_verify();
// Layer 2 of the default-password protection — see config/banner_warnings.inc.
// MUST run BEFORE any output so header('Location: ...') works.
pistar_warnings_enforce_redirect();
// Load the language support // Load the language support
require_once('../config/language.php'); require_once('../config/language.php');
//Load the Pi-Star Release file //Load the Pi-Star Release file
@@ -39,64 +17,66 @@ require_once('../config/version.php');
<meta name="language" content="English" /> <meta name="language" content="English" />
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<meta name="Author" content="Andrew Taylor (MW0MWZ)" /> <meta name="Author" content="Andrew Taylor (MW0MWZ)" />
<meta name="Description" content="Pi-Star Expert Editor" /> <meta name="Description" content="CDN Expert Editor" />
<meta name="KeyWords" content="Pi-Star" /> <meta name="KeyWords" content="CDN" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="pragma" content="no-cache" /> <meta http-equiv="pragma" content="no-cache" />
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon"> <link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon">
<meta http-equiv="Expires" content="0" /> <meta http-equiv="Expires" content="0" />
<title>Pi-Star - Digital Voice Dashboard - Expert Editor</title> <title>CDN - Digital Voice Dashboard - Expert Editor</title>
<link rel="stylesheet" type="text/css" href="../css/pistar-css.php" /> <link rel="stylesheet" type="text/css" href="../css/pistar-css.php" />
</head> </head>
<body> <body>
<?php pistar_warnings_render(); ?>
<div class="container"> <div class="container">
<?php include './header-menu.inc'; ?> <?php include './header-menu.inc'; ?>
<div class="contentwide"> <div class="contentwide">
<?php <?php
// A3-3 — see edit_ircddbgateway.php for the full TOCTOU rationale.
$filepath = tempnam('/tmp', 'pistar-edit-');
register_shutdown_function(function() use ($filepath) { @unlink($filepath); });
exec('sudo cp /usr/local/etc/RSSI.dat ' . escapeshellarg($filepath));
exec('sudo chown www-data:www-data ' . escapeshellarg($filepath));
exec('sudo chmod 600 ' . escapeshellarg($filepath));
if(isset($_POST['data'])) { if(isset($_POST['data'])) {
// Write submitted data into the staging file. // File Wrangling
exec('sudo cp /usr/local/etc/RSSI.dat /tmp/yAw432GHs5.tmp');
exec('sudo chown www-data:www-data /tmp/yAw432GHs5.tmp');
exec('sudo chmod 664 /tmp/yAw432GHs5.tmp');
// Open the file and write the data
$filepath = '/tmp/yAw432GHs5.tmp';
$fh = fopen($filepath, 'w'); $fh = fopen($filepath, 'w');
fwrite($fh, $_POST['data']); fwrite($fh, $_POST['data']);
fclose($fh); fclose($fh);
// Atomic install: /usr/local/etc/RSSI.dat is read by the
// MMDVMHost daemon (root) and is world-readable for the
// dashboard's editor round-trip; 644 root:root preserves both.
exec('sudo mount -o remount,rw /'); exec('sudo mount -o remount,rw /');
exec('sudo install -m 644 -o root -g root ' exec('sudo cp /tmp/yAw432GHs5.tmp /usr/local/etc/RSSI.dat');
. escapeshellarg($filepath) . ' /usr/local/etc/RSSI.dat'); exec('sudo chmod 644 /usr/local/etc/RSSI.dat');
exec('sudo chown root:root /usr/local/etc/RSSI.dat');
exec('sudo mount -o remount,ro /'); exec('sudo mount -o remount,ro /');
}
// Re-read for the form's textarea (POST: shows just-saved content; // Re-open the file and read it
// GET: shows current /etc content). $fh = fopen($filepath, 'r');
$fh = fopen($filepath, 'r'); $theData = fread($fh, filesize($filepath));
$theData = fread($fh, filesize($filepath));
} else {
// File Wrangling
exec('sudo cp /usr/local/etc/RSSI.dat /tmp/yAw432GHs5.tmp');
exec('sudo chown www-data:www-data /tmp/yAw432GHs5.tmp');
exec('sudo chmod 664 /tmp/yAw432GHs5.tmp');
// Open the file and read it
$filepath = '/tmp/yAw432GHs5.tmp';
$fh = fopen($filepath, 'r');
$theData = fread($fh, filesize($filepath));
}
fclose($fh); fclose($fh);
?> ?>
<h2>RSSI Dat (Full Edit)</h2>
<div class="settings-card" style="padding-top:16px;">
<form name="test" method="post" action=""> <form name="test" method="post" action="">
<?php csrf_field(); ?> <textarea class="raw-editor" name="data"><?php echo $theData; ?></textarea><br />
<textarea name="data" cols="80" rows="45"><?php echo htmlspecialchars((string)$theData, ENT_QUOTES, 'UTF-8'); ?></textarea><br /> <div class="field-actions"><input type="submit" name="submit" value="<?php echo $lang['apply']; ?>" /></div>
<input type="submit" name="submit" value="<?php echo $lang['apply']; ?>" />
</form> </form>
</div>
</div> </div>
<div class="footer">
Pi-Star / Pi-Star Dashboard, &copy; Andy Taylor (MW0MWZ) 2014-<?php echo date("Y"); ?>.<br />
Need help? Click <a style="color: #ffffff;" href="https://www.facebook.com/groups/pistarusergroup/" target="_new">here for the Support Group</a><br />
Get your copy of Pi-Star from <a style="color: #ffffff;" href="http://www.pistar.uk/downloads/" target="_new">here</a>.<br />
</div>
</div> </div>
</body> </body>
</html> </html>
+33 -61
View File
@@ -1,27 +1,4 @@
<?php <?php
/**
* Raw text editor for /etc/wpa_supplicant/wpa_supplicant.conf.
*
* Operator-editable WiFi configuration. Standard staged-write pattern.
*
* No daemon restart from this file; the operator must reset the WiFi
* adapter (admin/wifi.php has buttons) for changes to take effect.
*/
require_once($_SERVER['DOCUMENT_ROOT'].'/config/security_headers.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/csrf.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/banner_warnings.inc');
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 rejects forged POSTs cleanly with 403
// before any state change (sed-i, fopen+fwrite, sudo cp, etc.).
csrf_verify();
// Layer 2 of the default-password protection — see config/banner_warnings.inc.
// MUST run BEFORE any output so header('Location: ...') works.
pistar_warnings_enforce_redirect();
// Load the language support // Load the language support
require_once('../config/language.php'); require_once('../config/language.php');
//Load the Pi-Star Release file //Load the Pi-Star Release file
@@ -40,71 +17,66 @@ require_once('../config/version.php');
<meta name="language" content="English" /> <meta name="language" content="English" />
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<meta name="Author" content="Andrew Taylor (MW0MWZ)" /> <meta name="Author" content="Andrew Taylor (MW0MWZ)" />
<meta name="Description" content="Pi-Star Expert Editor" /> <meta name="Description" content="CDN Expert Editor" />
<meta name="KeyWords" content="Pi-Star" /> <meta name="KeyWords" content="CDN" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="pragma" content="no-cache" /> <meta http-equiv="pragma" content="no-cache" />
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon"> <link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon">
<meta http-equiv="Expires" content="0" /> <meta http-equiv="Expires" content="0" />
<title>Pi-Star - Digital Voice Dashboard - Expert Editor</title> <title>CDN - Digital Voice Dashboard - Expert Editor</title>
<link rel="stylesheet" type="text/css" href="../css/pistar-css.php" /> <link rel="stylesheet" type="text/css" href="../css/pistar-css.php" />
</head> </head>
<body> <body>
<?php pistar_warnings_render(); ?>
<div class="container"> <div class="container">
<?php include './header-menu.inc'; ?> <?php include './header-menu.inc'; ?>
<div class="contentwide"> <div class="contentwide">
<?php <?php
// A3-3 — see edit_ircddbgateway.php for the full TOCTOU rationale.
// wpa_supplicant.conf carries the WPA PSK in cleartext, so the
// random-name TOCTOU defence is more important here than for the
// other editors: a predictable name attack would let a local
// attacker pre-create /tmp/<known>.tmp as a symlink to a target
// they control reading and have our `sudo cp` follow it.
$filepath = tempnam('/tmp', 'pistar-edit-');
register_shutdown_function(function() use ($filepath) { @unlink($filepath); });
exec('sudo cp /etc/wpa_supplicant/wpa_supplicant.conf ' . escapeshellarg($filepath));
exec('sudo chown www-data:www-data ' . escapeshellarg($filepath));
exec('sudo chmod 600 ' . escapeshellarg($filepath));
if(isset($_POST['data'])) { if(isset($_POST['data'])) {
// Write submitted data into the staging file. // File Wrangling
exec('sudo cp /etc/wpa_supplicant/wpa_supplicant.conf /tmp/k45s7h5s9k3.tmp');
exec('sudo chown www-data:www-data /tmp/k45s7h5s9k3.tmp');
exec('sudo chmod 664 /tmp/k45s7h5s9k3.tmp');
// Open the file and write the data
$filepath = '/tmp/k45s7h5s9k3.tmp';
$fh = fopen($filepath, 'w'); $fh = fopen($filepath, 'w');
fwrite($fh, $_POST['data']); fwrite($fh, $_POST['data']);
fclose($fh); fclose($fh);
// Atomic install: mode + owner set in one syscall sequence.
// wpa_supplicant.conf carries the WPA PSK in cleartext as
// `psk=…hex…` — mode 600 root:root keeps every other local
// user (and any sandbox-escape from another service) from
// reading it. wpa_supplicant runs as root, so the daemon's
// own access is unaffected.
exec('sudo mount -o remount,rw /'); exec('sudo mount -o remount,rw /');
exec('sudo install -m 600 -o root -g root ' exec('sudo cp /tmp/k45s7h5s9k3.tmp /etc/wpa_supplicant/wpa_supplicant.conf');
. escapeshellarg($filepath) . ' /etc/wpa_supplicant/wpa_supplicant.conf'); exec('sudo chmod 644 /etc/wpa_supplicant/wpa_supplicant.conf');
exec('sudo chown www-data:www-data /etc/wpa_supplicant/wpa_supplicant.conf');
exec('sudo mount -o remount,ro /'); exec('sudo mount -o remount,ro /');
}
// Re-read for the form's textarea. // Re-open the file and read it
$fh = fopen($filepath, 'r'); $fh = fopen($filepath, 'r');
$theData = fread($fh, filesize($filepath)); $theData = fread($fh, filesize($filepath));
} else {
// File Wrangling
exec('sudo cp /etc/wpa_supplicant/wpa_supplicant.conf /tmp/k45s7h5s9k3.tmp');
exec('sudo chown www-data:www-data /tmp/k45s7h5s9k3.tmp');
exec('sudo chmod 664 /tmp/k45s7h5s9k3.tmp');
// Open the file and read it
$filepath = '/tmp/k45s7h5s9k3.tmp';
$fh = fopen($filepath, 'r');
$theData = fread($fh, filesize($filepath));
}
fclose($fh); fclose($fh);
?> ?>
<h2>WiFi Config (Full Edit)</h2>
<div class="settings-card" style="padding-top:16px;">
<form name="test" method="post" action=""> <form name="test" method="post" action="">
<?php csrf_field(); ?> <textarea class="raw-editor" name="data"><?php echo $theData; ?></textarea><br />
<textarea name="data" cols="80" rows="45"><?php echo htmlspecialchars((string)$theData, ENT_QUOTES, 'UTF-8'); ?></textarea><br /> <div class="field-actions"><input type="submit" name="submit" value="<?php echo $lang['apply']; ?>" /></div>
<input type="submit" name="submit" value="<?php echo $lang['apply']; ?>" />
</form> </form>
</div>
</div> </div>
<div class="footer">
Pi-Star / Pi-Star Dashboard, &copy; Andy Taylor (MW0MWZ) 2014-<?php echo date("Y"); ?>.<br />
Need help? Click <a style="color: #ffffff;" href="https://www.facebook.com/groups/pistarusergroup/" target="_new">here for the Support Group</a><br />
Get your copy of Pi-Star from <a style="color: #ffffff;" href="http://www.pistar.uk/downloads/" target="_new">here</a>.<br />
</div>
</div> </div>
</body> </body>
</html> </html>
+17 -43
View File
@@ -1,21 +1,4 @@
<?php <?php
/**
* Expert-section landing page.
*
* Renders the warning banner explaining what the expert editors do
* and that hand-edits can be overwritten by the dashboard. The
* navigation strip with links to every editor lives in
* header-menu.inc which is included from this file (and every
* editor page).
*/
require_once($_SERVER['DOCUMENT_ROOT'].'/config/security_headers.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/banner_warnings.inc');
setSecurityHeaders();
// Layer 2 of the default-password protection — see config/banner_warnings.inc.
// MUST run BEFORE any output so header('Location: ...') works.
pistar_warnings_enforce_redirect();
// Load the language support // Load the language support
require_once('../config/language.php'); require_once('../config/language.php');
//Load the Pi-Star Release file //Load the Pi-Star Release file
@@ -34,51 +17,42 @@ require_once('../config/version.php');
<meta name="language" content="English" /> <meta name="language" content="English" />
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<meta name="Author" content="Andrew Taylor (MW0MWZ)" /> <meta name="Author" content="Andrew Taylor (MW0MWZ)" />
<meta name="Description" content="Pi-Star Expert Editor" /> <meta name="Description" content="CDN Expert Editor" />
<meta name="KeyWords" content="Pi-Star" /> <meta name="KeyWords" content="CDN" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="pragma" content="no-cache" /> <meta http-equiv="pragma" content="no-cache" />
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon"> <link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon">
<meta http-equiv="Expires" content="0" /> <meta http-equiv="Expires" content="0" />
<title>Pi-Star - Digital Voice Dashboard - Expert Editor</title> <title>CDN - Digital Voice Dashboard - Expert Editor</title>
<link rel="stylesheet" type="text/css" href="../css/pistar-css.php" /> <link rel="stylesheet" type="text/css" href="../css/pistar-css.php" />
</head> </head>
<body> <body>
<?php pistar_warnings_render(); ?>
<div class="container"> <div class="container">
<?php include './header-menu.inc'; ?> <?php include './header-menu.inc'; ?>
<div class="contentwide"> <div class="contentwide">
<table width="100%"> <h2>Expert Editors</h2>
<tr><th>Expert Editors</th></tr> <div class="settings-card">
<tr><td align="center"> <div class="field-row"><div class="field-note" style="flex:1 1 100%;font-size:13px;">
<h2>**WARNING**</h2> <b>**WARNING**</b><br /><br />
Pi-Star Expert editors have been created to make editing some of the extra settings in the<br /> CDN Expert editors have been created to make editing some of the extra settings in the
config files more simple, allowing you to update some areas of the config files without the<br /> config files more simple, allowing you to update some areas of the config files without the
need to login to your Pi over SSH.<br /> need to login to your Pi over SSH.<br />
<br /> <br />
Please keep in mind when making your edits here, that these config files can be updated by<br /> Please keep in mind when making your edits here, that these config files can be updated by
the dashboard, and that your edits can be over-written. It is assumed that you already know<br /> the dashboard, and that your edits can be over-written. It is assumed that you already know
what you are doing editing the files by hand, and that you understand what parts of the files<br /> what you are doing editing the files by hand, and that you understand what parts of the files
are maintained by the dashboard.<br /> are maintained by the dashboard.<br />
<br /> <br />
With that warning in mind, you are free to make any changes you like, for help come to the Facebook<br /> With that warning in mind, you are free to make any changes you like, for help come to the Facebook
group (link at the bottom of the page) and ask for help if / when you need it.<br /> group (link at the bottom of the page) and ask for help if / when you need it.<br />
73 and enjoy your Pi-Star experiance.<br /> 73 and enjoy your CDN experiance.<br />
Pi-Star UK Team.<br /> CDN Team.
<br /> </div></div>
</td></tr> </div>
</table>
</div> </div>
<div class="footer">
Pi-Star / Pi-Star Dashboard, &copy; Andy Taylor (MW0MWZ) 2014-<?php echo date("Y"); ?>.<br />
Need help? Click <a style="color: #ffffff;" href="https://www.facebook.com/groups/pistarusergroup/" target="_new">here for the Support Group</a><br />
Get your copy of Pi-Star from <a style="color: #ffffff;" href="http://www.pistar.uk/downloads/" target="_new">here</a>.<br />
</div>
</div> </div>
</body> </body>
</html> </html>
+9 -35
View File
@@ -1,23 +1,4 @@
<?php <?php
/**
* Network jitter test runner.
*
* Lets the operator pick a target (BrandMeister / DMR+ / HBLink) and
* runs `sudo /usr/local/sbin/pistar-jittertest <target>` on the
* device; output streams to /var/log/pi-star/pi-star_icmptest.log
* which this page tails via AJAX (jquery-timing $.repeat).
*
* Uses system()/exec() rather than the staged-write pattern — no
* file edits, just a privileged background process.
*/
require_once($_SERVER['DOCUMENT_ROOT'].'/config/security_headers.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/banner_warnings.inc');
setSecurityHeaders();
// Layer 2 of the default-password protection — see config/banner_warnings.inc.
// MUST run BEFORE any output so header('Location: ...') works.
pistar_warnings_enforce_redirect();
// Load the language support // Load the language support
require_once('../config/language.php'); require_once('../config/language.php');
// Load the Pi-Star Release file // Load the Pi-Star Release file
@@ -37,9 +18,8 @@ if ($_SERVER["PHP_SELF"] == "/admin/expert/jitter_test.php") {
} else { $target = "DMR+"; } } else { $target = "DMR+"; }
if (!isset($_GET['ajax'])) { if (!isset($_GET['ajax'])) {
// truncate creates+clears the log file in one synchronous call — system('sudo touch /var/log/pi-star/pi-star_icmptest.log > /dev/null 2>&1 &');
// see admin/update.php for the full rationale. system('sudo echo "" > /var/log/pi-star/pi-star_icmptest.log > /dev/null 2>&1 &');
system('sudo truncate -s 0 /var/log/pi-star/pi-star_icmptest.log');
system('sudo /usr/local/sbin/pistar-jittertest '.$target.' > /dev/null 2>&1 &'); system('sudo /usr/local/sbin/pistar-jittertest '.$target.' > /dev/null 2>&1 &');
} }
@@ -88,13 +68,13 @@ if ($_SERVER["PHP_SELF"] == "/admin/expert/jitter_test.php") {
<meta name="language" content="English" /> <meta name="language" content="English" />
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<meta name="Author" content="Andrew Taylor (MW0MWZ)" /> <meta name="Author" content="Andrew Taylor (MW0MWZ)" />
<meta name="Description" content="Pi-Star Update" /> <meta name="Description" content="CDN Update" />
<meta name="KeyWords" content="Pi-Star" /> <meta name="KeyWords" content="CDN" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="pragma" content="no-cache" /> <meta http-equiv="pragma" content="no-cache" />
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon"> <link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon">
<meta http-equiv="Expires" content="0" /> <meta http-equiv="Expires" content="0" />
<title>Pi-Star - <?php echo $lang['digital_voice']." ".$lang['dashboard']." - ".$lang['update'];?></title> <title>CDN - <?php echo $lang['digital_voice']." ".$lang['dashboard']." - ".$lang['update'];?></title>
<link rel="stylesheet" type="text/css" href="../css/pistar-css.php" /> <link rel="stylesheet" type="text/css" href="../css/pistar-css.php" />
<script type="text/javascript" src="/jquery.min.js"></script> <script type="text/javascript" src="/jquery.min.js"></script>
<script type="text/javascript" src="/jquery-timing.min.js"></script> <script type="text/javascript" src="/jquery-timing.min.js"></script>
@@ -114,20 +94,13 @@ if ($_SERVER["PHP_SELF"] == "/admin/expert/jitter_test.php") {
</script> </script>
</head> </head>
<body> <body>
<?php pistar_warnings_render(); ?>
<div class="container"> <div class="container">
<?php include './header-menu.inc'; ?> <?php include './header-menu.inc'; ?>
<div class="contentwide"> <div class="contentwide">
<table width="100%"> <h2>Jitter Test Running</h2>
<tr><th>Test Running</th></tr> <div class="settings-card" style="padding-top:16px;">
<tr><td align="left"><div id="tail">Starting test, please wait...<br /></div></td></tr> <div id="tail">Starting test, please wait...<br /></div>
</table>
</div> </div>
<div class="footer">
Pi-Star web config, &copy; Andy Taylor (MW0MWZ) 2014-<?php echo date("Y"); ?>.<br />
Need help? Click <a style="color: #ffffff;" href="https://www.facebook.com/groups/pistarusergroup/" target="_new">here for the Support Group</a><br />
Get your copy of Pi-Star from <a style="color: #ffffff;" href="http://www.pistar.uk/downloads/" target="_blank">here</a>.<br />
<br />
</div> </div>
</div> </div>
</body> </body>
@@ -135,3 +108,4 @@ if ($_SERVER["PHP_SELF"] == "/admin/expert/jitter_test.php") {
<?php <?php
} }
?>
+39 -120
View File
@@ -1,31 +1,4 @@
<?php <?php
/**
* Modem firmware upgrade runner.
*
* Calls `sudo /usr/local/sbin/pistar-modemupgrade list` to enumerate
* supported modem variants, then `sudo /usr/local/sbin/pistar-
* modemupgrade <variant>` to flash the selected one. Output streams
* to /var/log/pi-star/pi-star_modemflash.log; tailed via AJAX.
*
* The selected variant is run through escapeshellarg() before being
* passed to the upgrade script (defence-in-depth — the picklist is
* already constrained to script-reported names).
*/
require_once($_SERVER['DOCUMENT_ROOT'].'/config/security_headers.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/csrf.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/banner_warnings.inc');
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 rejects forged POSTs cleanly with 403
// before any state change (sed-i, fopen+fwrite, sudo cp, etc.).
csrf_verify();
// Layer 2 of the default-password protection — see config/banner_warnings.inc.
// MUST run BEFORE any output so header('Location: ...') works.
pistar_warnings_enforce_redirect();
// Load the language support // Load the language support
require_once('../config/language.php'); require_once('../config/language.php');
// Load the Pi-Star Release file // Load the Pi-Star Release file
@@ -42,58 +15,26 @@ setlocale(LC_ALL, "LC_CTYPE=en_GB.UTF-8;LC_NUMERIC=C;LC_TIME=C;LC_COLLATE=C;LC_M
if ($_SERVER["PHP_SELF"] == "/admin/expert/modem_fw_upgrade.php") { if ($_SERVER["PHP_SELF"] == "/admin/expert/modem_fw_upgrade.php") {
if ($_SERVER['REQUEST_METHOD'] === 'POST') { if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['modem'])) { if (isset($_POST['modem'])) {
// Whitelist-validate against the canonical modem list that $selectedOption = $_POST['modem'];
// pistar-modemupgrade itself reports. The script is the }
// authoritative source of "what modem variants this device
// knows how to flash" — POSTed values that don't appear in
// that list are silently dropped (the operator just sees no
// flash happen). escapeshellarg() below is still defence-
// in-depth at the shell layer; this is the application-layer
// gate that stops the script from being invoked with
// attacker-controlled args at all. Mirrors the mmdvmcal
// command whitelist in admin/calibration.php.
$validModems = array_filter(array_map('trim',
explode("\n", (string)shell_exec('sudo /usr/local/sbin/pistar-modemupgrade list'))
));
if (in_array($_POST['modem'], $validModems, true)) {
$selectedOption = $_POST['modem'];
} else {
error_log('Pi-Star modem_fw_upgrade.php: rejected modem=' . substr((string)$_POST['modem'], 0, 64));
}
}
} }
if (!isset($_GET['ajax'])) { if (!isset($_GET['ajax'])) {
// truncate creates+clears the log file in one synchronous call — system('sudo touch /var/log/pi-star/pi-star_modemflash.log > /dev/null 2>&1 &');
// see admin/update.php for the full rationale. system('sudo echo "" > /var/log/pi-star/pi-star_modemflash.log > /dev/null 2>&1 &');
system('sudo truncate -s 0 /var/log/pi-star/pi-star_modemflash.log'); if (isset($selectedOption)) { system('sudo NP=1 /usr/local/sbin/pistar-modemupgrade ' . escapeshellarg($selectedOption) . ' > /dev/null 2>&1 &'); }
// No `NP=1` env-var prefix: the path-scoped sudoers refuses
// env-var pass-through without a SETENV: tag, so `sudo NP=1
// pistar-modemupgrade …` was rejected outright with "you are
// not allowed to set the following environment variables: NP"
// and the flash silently never started. The wrapper now
// auto-detects non-interactive mode via `[ -t 0 ]` (it's
// backgrounded here with no controlling terminal), so the
// explicit env var is no longer needed.
if (isset($selectedOption)) { system('sudo /usr/local/sbin/pistar-modemupgrade ' . escapeshellarg($selectedOption) . ' > /dev/null 2>&1 &'); }
} }
// passed sanity chk. // passed sanity chk.
header('Cache-Control: no-cache'); header('Cache-Control: no-cache');
// csrf_verify() at the top of the file already started the session_start();
// session via csrf_session_start(). Calling session_start() a
// second time emits a PHP Notice on PHP 8.x ("Ignoring
// session_start() because a session is already active") that
// gets logged to nginx error.log on every page load. The
// existing $_SESSION['update_offset'] log-tail logic below
// works unchanged against the already-active session.
if (!isset($_GET['ajax'])) { if (!isset($_GET['ajax'])) {
if (file_exists('/var/log/pi-star/pi-star_modemflash.log')) { if (file_exists('/var/log/pi-star/pi-star_modemflash.log')) {
$_SESSION['update_offset'] = filesize('/var/log/pi-star/pi-star_modemflash.log'); $_SESSION['update_offset'] = filesize('/var/log/pi-star/pi-star_modemflash.log');
} else { } else {
$_SESSION['update_offset'] = 0; $_SESSION['update_offset'] = 0;
} }
} }
@@ -140,32 +81,30 @@ if ($_SERVER["PHP_SELF"] == "/admin/expert/modem_fw_upgrade.php") {
<meta name="language" content="English" /> <meta name="language" content="English" />
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<meta name="Author" content="Andrew Taylor (MW0MWZ)" /> <meta name="Author" content="Andrew Taylor (MW0MWZ)" />
<meta name="Description" content="Pi-Star Update" /> <meta name="Description" content="CDN Update" />
<meta name="KeyWords" content="Pi-Star" /> <meta name="KeyWords" content="CDN" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="pragma" content="no-cache" /> <meta http-equiv="pragma" content="no-cache" />
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon" /> <link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon" />
<meta http-equiv="Expires" content="0" /> <meta http-equiv="Expires" content="0" />
<title>Pi-Star - <?php echo $lang['digital_voice']." ".$lang['dashboard']." - Modem FW ".$lang['update'];?></title> <title>CDN - <?php echo $lang['digital_voice']." ".$lang['dashboard']." - Modem FW ".$lang['update'];?></title>
<link rel="stylesheet" type="text/css" href="../css/pistar-css.php" /> <link rel="stylesheet" type="text/css" href="../css/pistar-css.php" />
<script type="text/javascript" src="/jquery.min.js"></script> <script type="text/javascript" src="/jquery.min.js"></script>
<script type="text/javascript" src="/jquery-timing.min.js"></script> <script type="text/javascript" src="/jquery-timing.min.js"></script>
<script type="text/javascript"> <script type="text/javascript">
function disableSubmitButtons() function disableSubmitButtons() {
{
var inputs = document.getElementsByTagName('input'); var inputs = document.getElementsByTagName('input');
for (var i = 0; i < inputs.length; i++) { for (var i = 0; i < inputs.length; i++) {
if (inputs[i].type === 'button') { if (inputs[i].type === 'button') {
inputs[i].disabled = true; inputs[i].disabled = true;
inputs[i].value = 'Please Wait...'; inputs[i].value = 'Please Wait...';
} }
} }
} }
function submitform() function submitform() {
{ disableSubmitButtons();
disableSubmitButtons(); document.getElementById("up_fw").submit();
document.getElementById("up_fw").submit();
} }
$(function() { $(function() {
@@ -183,26 +122,20 @@ if ($_SERVER["PHP_SELF"] == "/admin/expert/modem_fw_upgrade.php") {
</script> </script>
</head> </head>
<body> <body>
<?php pistar_warnings_render(); ?>
<div class="container"> <div class="container">
<?php include './header-menu.inc'; ?> <?php include './header-menu.inc'; ?>
<div class="contentwide"> <div class="contentwide">
<table width="100%"> <h2>Modem Firmware Upgrade Utility</h2>
<?php if (empty($_POST['modem'])) { ?> <?php if (empty($_POST['modem'])) { ?>
<tr><th>Modem Firmware Upgrade Utility</th></tr> <div class="settings-card">
<tr><td> <p>This tool will attempt to upgrade your selected modem to the latest version available firmware version:<br />
<br /> <?php echo $fw_ver_msg; ?></p>
<h2>Modem Firmware Upgrade Utility</h2> <p>When ready, select your modem type below and click, "Upgrade Modem". Do not interrupt the process or<br />
<p>This tool will attempt to upgrade your selected modem to the latest version available firmware version:<br /> navigate away from the page while the process is running.</p>
<?php echo $fw_ver_msg; ?></p> <p><strong>Please understand what you are doing, as well as the risks associated with flashing your modem.</strong></p>
<p>When ready, select your modem type below and click, "Upgrade Modem". Do not interrupt the process or<br /> <p><em>(IMPORTANT: Please note, we are not firmware developers, and we offer no support for firmware.<br />
navigate away from the page while the process is running.</p> We provide utilities to update the firmware. For firmware support, you will need to utilise other<br />
<p><strong>Please understand what you are doing, as well as the risks associated with flashing your modem.</strong></p> support resources from the firmware developers/maintainers or the web.)</em></p>
<p><em>(IMPORTANT: Please note, we are not firmware developers, and we offer no support for firmware.<br />
We provide utilities to update the firmware. For firmware support, you will need to utilise other<br />
support resources from the firmware developers/maintainers or the web.)</em></p>
</td></tr>
<tr><td>
<?php <?php
$friendlyNames = [ $friendlyNames = [
@@ -224,7 +157,6 @@ if ($_SERVER["PHP_SELF"] == "/admin/expert/modem_fw_upgrade.php") {
'hs_hat_generic_duplex' => 'MMDVM_HS_GENERIC_DUPLEX (14.7456MHz TCXO) GPIO', 'hs_hat_generic_duplex' => 'MMDVM_HS_GENERIC_DUPLEX (14.7456MHz TCXO) GPIO',
'hs_hat_generic_duplex-usb' => 'MMDVM_HS_GENERIC_DUPLEX (14.7456MHz TCXO) USB', 'hs_hat_generic_duplex-usb' => 'MMDVM_HS_GENERIC_DUPLEX (14.7456MHz TCXO) USB',
'hs_hat_nano_hotspot' => 'Nano_hotSPOT by BI7JTA (14.7456MHz TCXO) GPIO', 'hs_hat_nano_hotspot' => 'Nano_hotSPOT by BI7JTA (14.7456MHz TCXO) GPIO',
'hs_hat_nano_hotspot-duplex' => 'Nano_hotSPOT Dual by VR2VYE & BI7JTA (14.7456MHz TCXO) GPIO',
'dvmega_gpio' => 'DV-Mega Raspberry Pi Hat (Single or Dual Band) GPIO', 'dvmega_gpio' => 'DV-Mega Raspberry Pi Hat (Single or Dual Band) GPIO',
'dvmega_usb_uno' => 'DV-Mega Arduino Uno Shield USB (ttyUSB0)', 'dvmega_usb_uno' => 'DV-Mega Arduino Uno Shield USB (ttyUSB0)',
'dvmega_usb_mega' => 'DV-Mega Arduino Mega Shield USB (ttyUSB0)', 'dvmega_usb_mega' => 'DV-Mega Arduino Mega Shield USB (ttyUSB0)',
@@ -249,15 +181,14 @@ if ($_SERVER["PHP_SELF"] == "/admin/expert/modem_fw_upgrade.php") {
// Create the select element // Create the select element
echo '<p><form method="post" id="up_fw">'; echo '<p><form method="post" id="up_fw">';
echo csrf_field_html();
echo '<label for="modem">Select Modem:</label>'; echo '<label for="modem">Select Modem:</label>';
echo '<select id="modem" name="modem">'; echo '<select id="modem" name="modem">';
echo '<option value="" disabled selected>Please choose device type...</option>'; echo '<option value="" disabled selected>Please choose device type...</option>';
// Output each option with user-friendly names // Output each option with user-friendly names
foreach ($options as $option) { foreach ($options as $option) {
$friendlyName = isset($friendlyNames[$option]) ? $friendlyNames[$option] : $option; $friendlyName = isset($friendlyNames[$option]) ? $friendlyNames[$option] : $option;
echo '<option value="' . htmlspecialchars($option) . '">' . htmlspecialchars($friendlyName) . '</option>'; echo '<option value="' . htmlspecialchars($option) . '">' . htmlspecialchars($friendlyName) . '</option>';
} }
echo '</select>'; echo '</select>';
echo '<input type="button" value="Upgrade Modem" onclick="submitform()">'; echo '<input type="button" value="Upgrade Modem" onclick="submitform()">';
echo '</form></p>'; echo '</form></p>';
@@ -265,33 +196,21 @@ if ($_SERVER["PHP_SELF"] == "/admin/expert/modem_fw_upgrade.php") {
echo '<p>Error executing the command.</p>'; echo '<p>Error executing the command.</p>';
} }
?> ?>
</form>
</td></tr>
</table>
</div> </div>
<div class="footer">
Pi-Star / Pi-Star Dashboard, &copy; Andy Taylor (MW0MWZ) 2014-<?php echo date("Y"); ?>.<br />
Modem Flashing Tool &copy; Chip Cuccio (W0CHP) 2020-<?php echo date("Y");?><br />
Need help? Click <a style="color: #ffffff;" href="https://www.facebook.com/groups/pistarusergroup/" target="_new">here for the Support Group</a><br />
Get your copy of Pi-Star from <a style="color: #ffffff;" href="http://www.pistar.uk/downloads/" target="_blank">here</a>.<br />
</div> </div>
</div> </div>
</body> </body>
</html> </html>
<?php } else { ?> <?php } else { ?>
<tr><th>Modem Flash/Upgrade Output</th></tr> <div class="settings-card" style="padding-top:16px;">
<tr><td align="left"><div id="tail">Starting FW Upgrade, please wait...<br /></div></td></tr> <div id="tail">Starting FW Upgrade, please wait...<br /></div>
</table>
</div> </div>
<div class="footer">
Pi-Star / Pi-Star Dashboard, &copy; Andy Taylor (MW0MWZ) 2014-<?php echo date("Y"); ?>.<br />
Modem Flashing Tool &copy; Chip Cuccio (W0CHP) 2020-<?php echo date("Y");?><br />
Need help? Click <a style="color: #ffffff;" href="https://www.facebook.com/groups/pistarusergroup/" target="_new">here for the Support Group</a><br />
Get your copy of Pi-Star from <a style="color: #ffffff;" href="http://www.pistar.uk/downloads/" target="_blank">here</a>.<br />
</div> </div>
</div> </div>
</body> </body>
</html> </html>
<?php } <?php }
} }
?>
+10 -66
View File
@@ -1,23 +1,4 @@
<?php <?php
/**
* ShellInABox iframe wrapper.
*
* Reads the configured shellinabox port from /etc/default/shellinabox
* and embeds the in-browser SSH terminal as an <iframe>. Uses
* setSecurityHeadersAllowDifferentPorts() to relax the default same-
* origin frame restriction so the iframe can target a non-80 port on
* the same host.
*
* No file edits, no privileged calls — pure UI wrapper.
*/
require_once($_SERVER['DOCUMENT_ROOT'].'/config/security_headers.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/banner_warnings.inc');
setSecurityHeadersAllowDifferentPorts();
// Layer 2 of the default-password protection — see config/banner_warnings.inc.
// MUST run BEFORE any output so header('Location: ...') works.
pistar_warnings_enforce_redirect();
// Load the language support // Load the language support
require_once('../config/language.php'); require_once('../config/language.php');
// Load the Pi-Star Release file // Load the Pi-Star Release file
@@ -32,37 +13,6 @@ if (file_exists('/etc/default/shellinabox')) {
$shellPort = exec($getPortCommand); $shellPort = exec($getPortCommand);
} }
// HTTP_HOST is client-controllable (any HTTP client can send any
// `Host:` header). Echoing it raw into HTML attributes — as the
// iframe `src` and the anchor `href` below do — gives a passing
// attacker a reflected XSS surface: a request with a crafted Host
// header would render an iframe pointing at attacker-controlled
// content inside this page.
//
// Two-layer defence:
// 1. Strip everything that isn't a hostname character. The set
// `[a-zA-Z0-9.\-\[\]:]` covers DNS names, IPv4 dotted-quad,
// IPv6 bracketed literals, and the optional `:port` suffix.
// Anything else (CRLF, `<`, `"`, `'`, semicolons, spaces,
// parens, slashes) is dropped — the regex is what closes
// the XSS / response-splitting class.
// 2. htmlspecialchars on top, so even if the regex were
// relaxed in the future the value still can't break out
// of the `"…"` HTML attribute it lands in.
$shellHost = preg_replace(
'/[^a-zA-Z0-9.\-\[\]:]/',
'',
isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : ''
);
if ($shellHost === '') {
// Pathological case: HTTP_HOST contained nothing usable. Fall
// back to the LAN IP that nginx listens on rather than emitting
// a broken iframe `src=":port"`.
$shellHost = 'localhost';
}
$shellHostHtml = htmlspecialchars($shellHost, ENT_QUOTES, 'UTF-8');
$shellPortHtml = htmlspecialchars((string)(isset($shellPort) ? $shellPort : ''), ENT_QUOTES, 'UTF-8');
// Sanity Check that this file has been opened correctly // Sanity Check that this file has been opened correctly
if ($_SERVER["PHP_SELF"] == "/admin/expert/ssh_access.php") { if ($_SERVER["PHP_SELF"] == "/admin/expert/ssh_access.php") {
?> ?>
@@ -75,40 +25,33 @@ if ($_SERVER["PHP_SELF"] == "/admin/expert/ssh_access.php") {
<meta name="language" content="English" /> <meta name="language" content="English" />
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<meta name="Author" content="Andrew Taylor (MW0MWZ)" /> <meta name="Author" content="Andrew Taylor (MW0MWZ)" />
<meta name="Description" content="Pi-Star Update" /> <meta name="Description" content="CDN Update" />
<meta name="KeyWords" content="Pi-Star" /> <meta name="KeyWords" content="CDN" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="pragma" content="no-cache" /> <meta http-equiv="pragma" content="no-cache" />
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon"> <link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon">
<meta http-equiv="Expires" content="0" /> <meta http-equiv="Expires" content="0" />
<title>Pi-Star - <?php echo $lang['digital_voice']." ".$lang['dashboard']." - SSH";?></title> <title>CDN - <?php echo $lang['digital_voice']." ".$lang['dashboard']." - SSH";?></title>
<link rel="stylesheet" type="text/css" href="../css/pistar-css.php" /> <link rel="stylesheet" type="text/css" href="../css/pistar-css.php" />
<script type="text/javascript" src="/jquery.min.js"></script> <script type="text/javascript" src="/jquery.min.js"></script>
<script type="text/javascript" src="/jquery-timing.min.js"></script> <script type="text/javascript" src="/jquery-timing.min.js"></script>
</head> </head>
<body> <body>
<?php pistar_warnings_render(); ?>
<div class="container"> <div class="container">
<?php include './header-menu.inc'; ?> <?php include './header-menu.inc'; ?>
<div class="contentwide"> <div class="contentwide">
<table width="100%"> <h2>SSH - CDN</h2>
<tr><th>SSH - Pi-Star</th></tr> <div class="settings-card" style="padding-top:16px;">
<tr><td align="left"><div id="tail"> <div id="tail">
<?php if (isset($shellPort)) { <?php if (isset($shellPort)) {
echo "<iframe src=\"http://" . $shellHostHtml . ":" . $shellPortHtml . "\" style=\"border:0px #ffffff none; background:#ffffff; color:#00ff00;\" name=\"Pi-Star_SSH\" scrolling=\"no\" frameborder=\"0\" marginheight=\"0px\" marginwidth=\"0px\" height=\"100%\" width=\"100%\"></iframe>"; echo "<iframe src=\"http://".$_SERVER['HTTP_HOST'].":".$shellPort."\" style=\"border:0px #ffffff none; background:#ffffff; color:#00ff00;\" name=\"CDN_SSH\" scrolling=\"no\" frameborder=\"0\" marginheight=\"0px\" marginwidth=\"0px\" height=\"100%\" width=\"100%\"></iframe>";
} }
else { else {
echo "SSH Feature not yet installed"; echo "SSH Feature not yet installed";
} ?> } ?>
</div></td></tr>
</table>
<?php if (isset($shellPort)) { echo "<a href=\"//" . $shellHostHtml . ":" . $shellPortHtml . "\">Click here for fullscreen SSH client</a><br />\n"; } ?>
</div> </div>
<div class="footer"> <?php if (isset($shellPort)) { echo "<div class=\"field-actions\"><a href=\"//".$_SERVER['HTTP_HOST'].":".$shellPort."\">Open fullscreen SSH client</a></div>\n"; } ?>
Pi-Star web config, &copy; Andy Taylor (MW0MWZ) 2014-<?php echo date("Y"); ?>.<br /> </div>
Need help? Click <a style="color: #ffffff;" href="https://www.facebook.com/groups/pistarusergroup/" target="_new">here for the Support Group</a><br />
Get your copy of Pi-Star from <a style="color: #ffffff;" href="http://www.pistar.uk/downloads/" target="_blank">here</a>.<br />
<br />
</div> </div>
</div> </div>
</body> </body>
@@ -116,3 +59,4 @@ if ($_SERVER["PHP_SELF"] == "/admin/expert/ssh_access.php") {
<?php <?php
} }
?>
+10 -128
View File
@@ -1,28 +1,4 @@
<?php <?php
/**
* Pi-Star firmware/software upgrade runner.
*
* Triggers `sudo /usr/local/sbin/pistar-upgrade` (a longer-running
* sibling of /admin/update.php's pistar-update). Output streams to
* /var/log/pi-star/pi-star_upgrade.log and is tailed via AJAX.
* Requires an explicit POST confirmation field before kicking off.
*/
require_once($_SERVER['DOCUMENT_ROOT'].'/config/security_headers.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/csrf.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/banner_warnings.inc');
setSecurityHeaders();
// CSRF protection — see config/csrf.php for the full rationale.
// Critical here: line 29 below kicks off `sudo pistar-upgrade`
// the moment a `confirm_update` POST lands. csrf_verify() MUST
// run before that, so a hostile cross-site POST can never start
// a long-running privileged upgrade on the device.
csrf_verify();
// Layer 2 of the default-password protection — see config/banner_warnings.inc.
// MUST run BEFORE any output so header('Location: ...') works.
pistar_warnings_enforce_redirect();
// Load the language support // Load the language support
require_once('../config/language.php'); require_once('../config/language.php');
// Load the Pi-Star Release file // Load the Pi-Star Release file
@@ -35,55 +11,12 @@ require_once('../config/version.php');
// Sanity Check that this file has been opened correctly // Sanity Check that this file has been opened correctly
if ($_SERVER["PHP_SELF"] == "/admin/expert/upgrade.php") { if ($_SERVER["PHP_SELF"] == "/admin/expert/upgrade.php") {
// Only proceed with upgrade if user has confirmed via POST submission // Web-based upgrades have been disabled.
if (!isset($_GET['ajax']) && !empty($_POST) && isset($_POST['confirm_update'])) {
// truncate creates+clears in one synchronous call — see
// admin/update.php for the full rationale (the prior touch + echo
// redirect pair was racy under `&` and the `>` ran as www-data,
// defeating the sudo on the truncation step).
system('sudo truncate -s 0 /var/log/pi-star/pi-star_upgrade.log');
system('sudo /usr/local/sbin/pistar-upgrade > /dev/null 2>&1 &');
}
// Sanity Check Passed.
header('Cache-Control: no-cache');
// session_start() is no longer called here — csrf_verify() at
// the top already started the session via csrf_session_start().
// The existing $_SESSION['update_offset'] log-tail logic below
// works unchanged against the already-active session.
// Initialize session offset only if upgrade has been confirmed
if (!isset($_GET['ajax']) && !empty($_POST) && isset($_POST['confirm_update'])) {
//unset($_SESSION['update_offset']);
if (file_exists('/var/log/pi-star/pi-star_upgrade.log')) {
$_SESSION['update_offset'] = filesize('/var/log/pi-star/pi-star_upgrade.log');
} else {
$_SESSION['update_offset'] = 0;
}
}
if (isset($_GET['ajax'])) { if (isset($_GET['ajax'])) {
//session_start(); exit();
if (!file_exists('/var/log/pi-star/pi-star_upgrade.log')) {
exit();
}
$handle = fopen('/var/log/pi-star/pi-star_upgrade.log', 'rb');
if (isset($_SESSION['update_offset'])) {
fseek($handle, 0, SEEK_END);
if ($_SESSION['update_offset'] > ftell($handle)) //log rotated/truncated
$_SESSION['update_offset'] = 0; //continue at beginning of the new log
$data = stream_get_contents($handle, -1, $_SESSION['update_offset']);
$_SESSION['update_offset'] += strlen($data);
echo nl2br($data);
}
else {
fseek($handle, 0, SEEK_END);
$_SESSION['update_offset'] = ftell($handle);
}
exit();
} }
header('Cache-Control: no-cache');
?> ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
@@ -94,79 +27,28 @@ if ($_SERVER["PHP_SELF"] == "/admin/expert/upgrade.php") {
<meta name="language" content="English" /> <meta name="language" content="English" />
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<meta name="Author" content="Andrew Taylor (MW0MWZ)" /> <meta name="Author" content="Andrew Taylor (MW0MWZ)" />
<meta name="Description" content="Pi-Star Update" /> <meta name="Description" content="CDN Update" />
<meta name="KeyWords" content="Pi-Star" /> <meta name="KeyWords" content="CDN" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="pragma" content="no-cache" /> <meta http-equiv="pragma" content="no-cache" />
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon" /> <link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon" />
<meta http-equiv="Expires" content="0" /> <meta http-equiv="Expires" content="0" />
<title>Pi-Star - <?php echo $lang['digital_voice']." ".$lang['dashboard']." - ".$lang['update'];?></title> <title>CDN - <?php echo $lang['digital_voice']." ".$lang['dashboard']." - ".$lang['update'];?></title>
<link rel="stylesheet" type="text/css" href="../css/pistar-css.php" /> <link rel="stylesheet" type="text/css" href="../css/pistar-css.php" />
<?php if (!empty($_POST) && isset($_POST['confirm_update'])) { ?>
<script type="text/javascript" src="/jquery.min.js"></script>
<script type="text/javascript" src="/jquery-timing.min.js"></script>
<script type="text/javascript">
$(function() {
$.repeat(1000, function() {
$.get('/admin/expert/upgrade.php?ajax', function(data) {
if (data.length < 1) return;
var objDiv = document.getElementById("tail");
var isScrolledToBottom = objDiv.scrollHeight - objDiv.clientHeight <= objDiv.scrollTop + 1;
$('#tail').append(data);
if (isScrolledToBottom)
objDiv.scrollTop = objDiv.scrollHeight;
});
});
});
</script>
<?php } ?>
</head> </head>
<body> <body>
<?php pistar_warnings_render(); ?>
<div class="container"> <div class="container">
<?php include './header-menu.inc'; ?> <?php include './header-menu.inc'; ?>
<div class="contentwide"> <div class="contentwide">
<?php if (!empty($_POST) && isset($_POST['confirm_update'])) { ?> <h2>Upgrade Disabled</h2>
<table role="presentation" width="100%"> <div class="settings-card">
<tr><th>Upgrade Running</th></tr> <div class="field-row"><div class="field-note" style="flex:1 1 100%;">Web-based upgrades have been disabled on this system.</div></div>
<tr><td align="left"><div id="tail">Starting upgrade, please wait...<br /></div></td></tr>
</table>
<?php } else { ?>
<form action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>" method="post">
<?php csrf_field(); ?>
<table width="100%">
<tr>
<th>Upgrade</th>
</tr>
<tr>
<td align="center" style="padding: 20px;">
<p style="margin-bottom: 20px;">
<strong>This will upgrade your Pi-Star installation to the latest version.</strong><br />
<br />
The upgrade process may take several minutes to complete.<br />
Please do not interrupt the process or power off your system during the upgrade.
</p>
<button style="border: none; background: none; cursor: pointer;" type="submit" name="confirm_update" value="1">
<img src="/images/download.png" border="0" alt="Start Upgrade" /><br />
<strong>Start Upgrade</strong>
</button>
</td>
</tr>
</table>
</form>
<?php } ?>
</div> </div>
<footer aria-label="footer">
<div class="footer">
Pi-Star web config, &copy; Andy Taylor (MW0MWZ) 2014-<?php echo date("Y"); ?>.<br />
Need help? Click <a style="color: #ffffff;" href="https://www.facebook.com/groups/pistarusergroup/" target="_new">here for the Support Group</a><br />
Get your copy of Pi-Star from <a style="color: #ffffff;" href="http://www.pistar.uk/downloads/" target="_blank">here</a>.<br />
<br />
</div> </div>
</footer>
</div> </div>
</body> </body>
</html> </html>
<?php <?php
} }
?>
-1
View File
@@ -1 +0,0 @@
../images
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

+330 -1
View File
@@ -1 +1,330 @@
../index.php <?php
require_once('config/version.php');
require_once('config/ircddblocal.php');
require_once('config/language.php');
$configs = array();
if ($configfile = fopen($gatewayConfigPath,'r')) {
while ($line = fgets($configfile)) {
list($key,$value) = preg_split('/=/',$line);
$value = trim(str_replace('"','',$value));
if ($key != 'ircddbPassword' && strlen($value) > 0)
$configs[$key] = $value;
}
}
$progname = basename($_SERVER['SCRIPT_FILENAME'],".php");
$rev=$version;
//$MYCALL=strtoupper($callsign);
$MYCALL=strtoupper($configs['gatewayCallsign']);
// Check if the config file exists
if (file_exists('/etc/pistar-css.ini')) {
$piStarCssFile = '/etc/pistar-css.ini';
if (fopen($piStarCssFile,'r')) { $piStarCss = parse_ini_file($piStarCssFile, true); }
if ($piStarCss['BannerH1']['Enabled']) {
$piStarCssBannerH1 = $piStarCss['BannerH1']['Text'];
}
if ($piStarCss['BannerExtText']['Enabled']) {
$piStarCssBannerExtTxt = $piStarCss['BannerExtText']['Text'];
}
}
//Load the Pi-Star Release file
$pistarReleaseConfig = '/etc/pistar-release';
$configPistarRelease = array();
$configPistarRelease = parse_ini_file($pistarReleaseConfig, true);
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" lang="en">
<head>
<meta name="robots" content="index" />
<meta name="robots" content="follow" />
<meta name="language" content="English" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<?php echo "<meta name=\"generator\" content=\"$progname $rev\" />\n"; ?>
<meta name="Author" content="Hans-J. Barthen (DL5DI), Kim Huebel (DG9VH) and Andy Taylor (MW0MWZ)" />
<meta name="Description" content="CDN Dashboard" />
<meta name="KeyWords" content="MW0MWZ,MMDVMHost,ircDDBGateway,D-Star,ircDDB,CDN,Blackwood,Wales,DL5DI,DG9VH" />
<meta http-equiv="cache-control" content="max-age=0" />
<meta http-equiv="cache-control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="expires" content="0" />
<meta http-equiv="pragma" content="no-cache" />
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon" />
<link rel="stylesheet" type="text/css" href="css/nice-select.min.css?ver=<?php echo $configPistarRelease['Pi-Star']['Version']; ?>" />
<title><?php echo "$MYCALL"." - ".$lang['digital_voice']." ".$lang['dashboard'];?></title>
<?php include_once "config/browserdetect.php"; ?>
<script type="text/javascript" src="/jquery.min.js"></script>
<script type="text/javascript" src="/functions.js"></script>
<script type="text/javascript">
$.ajaxSetup({ cache: false });
</script>
</head>
<body>
<div class="container">
<div class="header">
<div style="font-size: 8px; text-align: left; padding-left: 8px; float: left;">Hostname: <?php echo exec('cat /etc/hostname'); ?></div><div style="font-size: 8px; text-align: right; padding-right: 8px;">CDN:<?php echo $configPistarRelease['Pi-Star']['Version']?> / <?php echo $lang['dashboard'].": ".$version; ?></div>
<h1>CDN <?php echo $lang['digital_voice']." ".$lang['dashboard_for']." ".$MYCALL; ?></h1>
<?php if (isset($piStarCssBannerH1)) { echo "<h1>".$piStarCssBannerH1."</h1>\n"; } ?>
<?php if (isset($piStarCssBannerExtTxt)) { echo "<p style=\"text-align: center; color: #ffffff;\">".$piStarCssBannerExtTxt."</p>\n"; }?>
<?php $onAdminPage = ($_SERVER["PHP_SELF"] == "/admin/index.php"); ?>
<p style="padding-right: 5px; text-align: right; color: #ffffff;">
<a href="/" style="color: #ffffff;" class="<?php echo !$onAdminPage ? 'active' : ''; ?>"><?php echo $lang['dashboard'];?></a> |
<a href="/admin/" style="color: #ffffff;" class="<?php echo $onAdminPage ? 'active' : ''; ?>"><?php echo $lang['admin'];?></a> |
<?php if ($onAdminPage) {
echo ' <a href="/admin/live_modem_log.php" style="color: #ffffff;">'.$lang['live_logs'].'</a> |'."\n";
echo ' <a href="/admin/power.php" style="color: #ffffff;">'.$lang['power'].'</a> |'."\n";
} ?>
<a href="/admin/configure.php" style="color: #ffffff;"><?php echo $lang['configuration'];?></a>
</p>
</div>
<?php
// Output some default features
if ($_SERVER["PHP_SELF"] == "/admin/index.php") {
echo '<div class="contentwide">'."\n";
echo '<script type="text/javascript">'."\n";
echo 'function reloadSysInfo(){'."\n";
echo ' $("#sysInfo").load("/dstarrepeater/system.php",function(){ setTimeout(reloadSysInfo,15000) });'."\n";
echo '}'."\n";
echo 'setTimeout(reloadSysInfo,15000);'."\n";
echo '$(window).trigger(\'resize\');'."\n";
echo '</script>'."\n";
echo '<div id="sysInfo">'."\n";
include 'dstarrepeater/system.php'; // Basic System Info
echo '</div>'."\n";
echo '</div>'."\n";
}
// First lets figure out if we are in MMDVMHost mode, or dstarrepeater mode;
if (file_exists('/etc/dstar-radio.mmdvmhost')) {
include 'config/config.php'; // MMDVMDash Config
include_once 'mmdvmhost/tools.php'; // MMDVMDash Tools
function getMMDVMConfigFileContent() {
// loads /etc/mmdvmhost into array for further use
$conf = array();
if ($configs = @fopen('/etc/mmdvmhost', 'r')) {
while ($config = fgets($configs)) {
array_push($conf, trim ( $config, " \t\n\r\0\x0B"));
}
fclose($configs);
}
return $conf;
}
$mmdvmconfigfile = getMMDVMConfigFileContent();
$dashboardRowOpen = true;
echo '<div class="dashboard-row">'."\n"; // Pairs .nav + .content so they stretch to equal height
echo '<div class="nav">'."\n"; // Start the Side Menu
echo '<script type="text/javascript">'."\n";
echo 'function reloadRepeaterInfo(){'."\n";
echo ' $("#repeaterInfo").load("/mmdvmhost/repeaterinfo.php",function(){ setTimeout(reloadRepeaterInfo,1000) });'."\n";
echo '}'."\n";
echo 'setTimeout(reloadRepeaterInfo,1000);'."\n";
echo '$(window).trigger(\'resize\');'."\n";
echo '</script>'."\n";
echo '<div id="repeaterInfo">'."\n";
include 'mmdvmhost/repeaterinfo.php'; // MMDVMDash Repeater Info
echo '</div>'."\n";
echo '</div>'."\n";
echo '<div class="content">'."\n";
$testMMDVModeDSTARnet = getConfigItem("D-Star Network", "Enable", $mmdvmconfigs);
if ( $testMMDVModeDSTARnet == 1 ) { // If D-Star network is enabled, add these extra features.
if ($_SERVER["PHP_SELF"] == "/admin/index.php") { // Admin Only Option
echo '<script type="text/javascript">'."\n";
echo 'function reloadrefLinks(){'."\n";
echo ' $("#refLinks").load("/dstarrepeater/active_reflector_links.php",function(){ setTimeout(reloadrefLinks,15000) });'."\n";
echo '}'."\n";
echo 'setTimeout(reloadrefLinks,15000);'."\n";
echo '$(window).trigger(\'resize\');'."\n";
echo '</script>'."\n";
echo '<div id="refLinks">'."\n";
include 'dstarrepeater/active_reflector_links.php'; // dstarrepeater gateway config
echo '</div>'."\n";
echo '<br />'."\n";
include 'dstarrepeater/link_manager.php'; // D-Star Link Manager
echo "<br />\n";
}
echo '<script type="text/javascript">'."\n";
echo 'function reloadcssConnections(){'."\n";
echo ' $("#cssConnects").load("/dstarrepeater/css_connections.php",function(){ setTimeout(reloadcssConnections,15000) });'."\n";
echo '}'."\n";
echo 'setTimeout(reloadcssConnections,15000);'."\n";
echo '$(window).trigger(\'resize\');'."\n";
echo '</script>'."\n";
echo '<div id="cssConnects">'."\n";
include 'dstarrepeater/css_connections.php'; // dstarrepeater gateway config
echo '</div>'."\n";
}
if ($_SERVER["PHP_SELF"] == "/admin/index.php") { // Admin Only Option
echo '<script type="text/javascript">'."\n";
echo 'function reloadbmConnections(){'."\n";
echo ' $("#bmConnects").load("/mmdvmhost/bm_links.php",function(){ setTimeout(reloadbmConnections,180000) });'."\n";
echo '}'."\n";
echo 'setTimeout(reloadbmConnections,180000);'."\n";
echo '$(window).trigger(\'resize\');'."\n";
echo '</script>'."\n";
echo '<div id="bmConnects">'."\n";
include 'mmdvmhost/bm_links.php'; // BM Links
echo '</div>'."\n";
}
if ($_SERVER["PHP_SELF"] == "/admin/index.php") { // Admin Only Options
include 'mmdvmhost/bm_manager.php'; // BM DMR Link Manager
}
if ($_SERVER["PHP_SELF"] == "/admin/index.php") { // Admin Only Option
echo '<script type="text/javascript">'."\n";
echo 'function reloadtgifConnections(){'."\n";
echo ' $("#tgifConnects").load("/mmdvmhost/tgif_links.php",function(){ setTimeout(reloadtgifConnections,180000) });'."\n";
echo '}'."\n";
echo 'setTimeout(reloadtgifConnections,180000);'."\n";
echo '$(window).trigger(\'resize\');'."\n";
echo '</script>'."\n";
echo '<div id="tgifConnects">'."\n";
include 'mmdvmhost/tgif_links.php'; // TGIF Links
echo '</div>'."\n";
}
if ($_SERVER["PHP_SELF"] == "/admin/index.php") { // Admin Only Options
include 'mmdvmhost/tgif_manager.php'; // TGIF DMR Link Manager
}
$testMMDVModeYSFnet = getConfigItem("System Fusion Network", "Enable", $mmdvmconfigs);
if ( $testMMDVModeYSFnet == 1 ) { // If YSF network is enabled, add these extra features.
if ($_SERVER["PHP_SELF"] == "/admin/index.php") { // Admin Only Option
include 'mmdvmhost/ysf_manager.php'; // YSF Links
}
}
$testMMDVModeP25net = getConfigItem("P25 Network", "Enable", $mmdvmconfigs);
if ( $testMMDVModeP25net == 1 ) { // If P25 network is enabled, add these extra features.
if ($_SERVER["PHP_SELF"] == "/admin/index.php") { // Admin Only Option
include 'mmdvmhost/p25_manager.php'; // P25 Links
}
}
$testMMDVModeNXDNnet = getConfigItem("NXDN Network", "Enable", $mmdvmconfigs);
if ( $testMMDVModeNXDNnet == 1 ) { // If NXDN network is enabled, add these extra features.
if ($_SERVER["PHP_SELF"] == "/admin/index.php") { // Admin Only Option
include 'mmdvmhost/nxdn_manager.php'; // NXDN Links
}
}
$testMMDVModeM17net = getConfigItem("M17 Network", "Enable", $mmdvmconfigs);
if ( $testMMDVModeM17net == 1 ) { // If NXDN network is enabled, add these extra features.
if ($_SERVER["PHP_SELF"] == "/admin/index.php") { // Admin Only Option
include 'mmdvmhost/m17_manager.php'; // M17 Links
}
}
echo '<script type="text/javascript">'."\n";
echo 'function reloadLocalTx(){'."\n";
echo ' $("#localTxs").load("/mmdvmhost/localtx.php",function(){ setTimeout(reloadLocalTx,1500) });'."\n";
echo '}'."\n";
echo 'setTimeout(reloadLocalTx,1500);'."\n";
echo 'function reloadLastHerd(){'."\n";
echo ' $("#lastHerd").load("/mmdvmhost/lh.php",function(){ setTimeout(reloadLastHerd,1500) });'."\n";
echo '}'."\n";
echo 'setTimeout(reloadLastHerd,1500);'."\n";
echo '$(window).trigger(\'resize\');'."\n";
echo '</script>'."\n";
echo '<div id="lastHerd">'."\n";
include 'mmdvmhost/lh.php'; // MMDVMDash Last Herd
echo '</div>'."\n";
echo "<br />\n";
echo '<div id="localTxs">'."\n";
include 'mmdvmhost/localtx.php'; // MMDVMDash Local Trasmissions
echo '</div>'."\n";
// If POCSAG is enabled, show the information pannel
$testMMDVModePOCSAG = getConfigItem("POCSAG Network", "Enable", $mmdvmconfigfile);
if ( $testMMDVModePOCSAG == 1 ) {
echo '<script type="text/javascript">'."\n";
echo 'function reloadPages(){'."\n";
echo ' $("#Pages").load("/mmdvmhost/pages.php",function(){ setTimeout(reloadPages,5000) });'."\n";
echo '}'."\n";
echo 'setTimeout(reloadPages,5000);'."\n";
echo '$(window).trigger(\'resize\');'."\n";
echo '</script>'."\n";
echo "<br />\n";
echo '<div id="Pages">'."\n";
include 'mmdvmhost/pages.php'; // POCSAG Messages
echo '</div>'."\n";
}
} elseif (file_exists('/etc/dstar-radio.dstarrepeater')) {
echo '<div class="contentwide">'."\n";
include 'dstarrepeater/gateway_software_config.php'; // dstarrepeater gateway config
echo '<script type="text/javascript">'."\n";
echo 'function reloadrefLinks(){'."\n";
echo ' $("#refLinks").load("/dstarrepeater/active_reflector_links.php",function(){ setTimeout(reloadrefLinks,15000) });'."\n";
echo '}'."\n";
echo 'setTimeout(reloadrefLinks,15000);'."\n";
echo '$(window).trigger(\'resize\');'."\n";
echo '</script>'."\n";
echo '<br />'."\n";
echo '<div id="refLinks">'."\n";
include 'dstarrepeater/active_reflector_links.php'; // dstarrepeater gateway config
echo '</div>'."\n";
echo '<br />'."\n";
if ($_SERVER["PHP_SELF"] == "/admin/index.php") { // Admin Only Options
include 'dstarrepeater/link_manager.php'; // D-Star Link Manager
echo "<br />\n";
}
echo '<script type="text/javascript">'."\n";
echo 'function reloadcssConnections(){'."\n";
echo ' $("#cssConnects").load("/dstarrepeater/css_connections.php",function(){ setTimeout(reloadcssConnections,15000) });'."\n";
echo '}'."\n";
echo 'setTimeout(reloadcssConnections,15000);'."\n";
echo '$(window).trigger(\'resize\');'."\n";
echo '</script>'."\n";
echo '<div id="cssConnects">'."\n";
include 'dstarrepeater/css_connections.php'; // dstarrepeater gateway config
echo '</div>'."\n";
echo '<script type="text/javascript">'."\n";
echo 'function reloadLocalTx(){'."\n";
echo ' $("#localTx").load("/dstarrepeater/local_tx.php",function(){ setTimeout(reloadLocalTx,3000) });'."\n";
echo '}'."\n";
echo 'setTimeout(reloadLocalTx,3000);'."\n";
echo 'function reloadLastHerd(){'."\n";
echo ' $("#lh").load("/dstarrepeater/last_herd.php",function(){ setTimeout(reloadLastHerd,3000) });'."\n";
echo '}'."\n";
echo 'setTimeout(reloadLastHerd,3000);'."\n";
echo '$(window).trigger(\'resize\');'."\n";
echo '</script>'."\n";
echo '<div id="lh">'."\n";
include 'dstarrepeater/last_herd.php'; //dstarrepeater Last Herd
echo '</div>'."\n";
echo "<br />\n";
echo '<div id="localTx">'."\n";
include 'dstarrepeater/local_tx.php'; //dstarrepeater Local Transmissions
echo '</div>'."\n";
echo '<br />'."\n";
} else {
echo '<div class="contentwide">'."\n";
//We dont know what mode we are in - fail...
echo "<H1>未定义工作模式...</H1>\n";
echo '<script type="text/javascript">setTimeout(function() { window.location="/admin/configure.php";},10000);</script>'."\n";
}
?>
</div>
<?php if (isset($dashboardRowOpen)) { echo "</div>\n"; } ?>
</div>
</body>
<script type="text/javascript" src="/nice-select.min.js"></script>
<script type="text/javascript">
var selectize = document.querySelectorAll('select')
var options = {searchable: true};
selectize.forEach(function(select){
if( select.length > 30 && null === select.onchange ) {
select.classList.add("small", "selectize");
tabletd = select.closest('.field-row') || select.closest('td');
tabletd.style.cssText = 'overflow-x:unset';
NiceSelect.bind(select, options);
}
});
</script>
</html>
-1
View File
@@ -1 +0,0 @@
../lang/
+158
View File
@@ -0,0 +1,158 @@
<?php
//
// Catalan ES Language Pack
// Rafa EA3BIL, Xavi EA3W
// 08-Mar-2018. Some updates done after linguistic expert review.
// 15-Oct-2018 . Ortographical fixes by C31FR - Franc
//
$lang = array (
// Banner texts
"digital_voice" => "Veu Digital",
"configuration" => "Configuració",
"dashboard_for" => "Tauler de control de",
// Banner links
"dashboard" => "Tauler de Control",
"admin" => "Administrar",
"power" => "Reiniciar/Apagar",
"update" => "Actualitzar",
"backup_restore" => "Fer/Restaurar còpia seguretat",
"factory_reset" => "Restaurar estat de fàbrica",
"live_logs" => "Informes/Logs",
// Config page section headdings
"hardware_info" => "Informació del maquinari",
"control_software" => "Control de programari",
"mmdvmhost_config" => "Configuració de MMDVMHost",
"general_config" => "Configuració General",
"dmr_config" => "Configuració de DMR",
"dstar_config" => "Configuració de DSTAR",
"ysf_config" => "Configuració de Yaesu C4FM fusion",
"p25_config" => "Configuració de P25",
"nxdn_config" => "Configuració de NXDN",
"m17_config" => "M17 Configuration",
"pocsag_config" => "Configuració de POCSAG",
"mobilegps_config" => "Configuració GPS mòbil",
"wifi_config" => "Configuració WIFI",
"fw_config" => "Configuració del Tallafocs",
"remote_access_pw" => "Mot clau per acccés remot",
// Config Page - Section General
"setting" => "Configuració",
"value" => "Valor",
"apply" => "Aplicar",
// Config Page - Gateway Hardware Information
"hostname" => "Nom de l'amfitrió",
"kernel" => "Nucli",
"platform" => "Plataforma",
"cpu_load" => "Càrrega CPU",
"cpu_temp" => "Temperatura CPU",
// Config Page - Control Software
"controller_software" => "Controlador programari",
"controller_mode" => "Controlador de Mode",
// Config Page - MMDVMHost Configuration
"dmr_mode" => "Mode DMR",
"d-star_mode" => "Mode D-Star",
"ysf_mode" => "Mode YSF",
"p25_mode" => "Mode P25",
"nxdn_mode" => "Mode NXDN",
"m17_mode" => "M17 Mode",
"mmdvm_display" => "MMDVM Tipus de pantalla",
"mode_hangtime" => "Mode temps de suspensió",
// Config Page - General Configuration
"node_call" => "Indicatiu del Node",
"dmr_id" => "CCS7/DMR ID",
"radio_freq" => "Freqüència RX/TX",
"lattitude" => "Latitud",
"longitude" => "Longitud",
"town" => "Ciutat",
"country" => "País",
"url" => "URL",
"radio_type" => "Ràdio/Tipus de mòdem",
"node_type" => "Tipus de Node",
"timezone" => "Zona horària",
"dash_lang" => "Idioma del Tauler de Control",
// Config Page - DMR Configuration
"dmr_master" => "Màster DMR (MMDVMHost)",
"bm_master" => "Màster BrandMeister",
"bm_network" => "Xarxa BrandMeister",
"dmr_plus_master" => "Màster DMR+",
"dmr_plus_network" => "Xarxa DMR+",
"xlx_master" => "Màster XLX",
"xlx_enable" => "Habilitar màster XLX",
"dmr_cc" => "Codi de color DMR",
"dmr_embeddedlconly" => "DMR EmbeddedLCOnly",
"dmr_dumptadata" => "DMR DumpTAData",
// Config Page - D-Star Configuration
"dstar_rpt1" => "RPT1 indicatiu de crida",
"dstar_rpt2" => "RPT2 indicatiu de crida",
"dstar_irc_password" => "mot clau d'ircDDBGateway",
"dstar_default_ref" => "Reflector predeterminat",
"aprs_host" => "Servidor d'APRS",
"dstar_irc_lang" => "Idioma d'ircDDBGateway",
"dstar_irc_time" => "Interval de Balissa",
// Config Page - YSF Configuration
"ysf_startup_host" => "YSF Arrencant Host",
// Config Page - P25 Configuration
"p25_startup_host" => "P25 Arrencant Host",
"p25_nac" => "P25 NAC",
// Config Page - NXDN Configuration
"nxdn_startup_host" => "NXDN Arrencant Host",
"nxdn_ran" => "NXDN RAN",
// Config Page - M17 Configuration
"m17_startup_host" => "M17 Startup Host",
"m17_can" => "M17 CAN",
// Config Page - MobileGPS Configuration
"mobilegps_enable" => "Activació del GPS mòbil",
"mobilegps_port" => "Port del GPS",
"mobilegps_speed" => "Velocitat del port del GPS",
// Config Page - Firewall Configuration
"fw_dash" => "Tauler d'accés",
"fw_irc" => "ircDDBGateway Remot",
"fw_ssh" => "Accés per SSH",
// Config Page - Password
"user" => "Nom d'usuari",
"password" => "contrasenya",
"set_password" => "Establir contrasenya",
// Dashboard Front Page - Repeater Info Pannel
"modes_enabled" => "Modes habilitats",
"net_status" => "Estat de la xarxa",
"internet" => "Internet",
"radio_info" => "Informació de Ràdio",
"dstar_repeater" => "Repetidor D-Star",
"dstar_net" => "Xarxa D-Star",
"dmr_repeater" => "Repetidor DMR",
"dmr_master" => "Màster DMR",
"ysf_net" => "Xarxa YSF",
"p25_radio" => "Ràdio P25",
"p25_net" => "Xarxa P25",
"nxdn_radio" => "Ràdio NXDN",
"nxdn_net" => "Xarxa NXDN",
"m17_radio" => "M17 Radio",
"m17_net" => "M17 Network",
// Dashboard Front Page - Calls
"time" => "Hora",
"mode" => "Mode",
"callsign" => "Indicatiu",
"target" => "Destí",
"src" => "SRC",
"dur" => "DUR",
"loss" => "Pèrdua",
"ber" => "BER",
// POCSAG Specific
"pocsag_list" => "Activitat de passarel·la DAPNET",
"pocsag_timeslot" => "Time Slot",
"pocsag_msg" => "Missatge",
// Dashboard - Extra Info
"group" => "Grup",
"logoff" => "Finalitzar",
"info" => "Informació",
"utot" => "UTOT",
"gtot" => "GTOT",
// Dashboard Front Page / Admin - Section Headders
"last_heard_list" => "Últimes 20 trucades rebudes",
"local_tx_list" => "Últimes 20 trucades rebudes via ràdio",
"active_starnet_groups" => "Grups actius Starnet",
"active_starnet_members" => "Membres actius grup Starnet",
"d-star_link_manager" => "Gestor d'enllaços D-Star",
"d-star_link_status" => "Informació d'enllaços D-Star",
"service_status" => "Estat del servei"
);
?>
+157
View File
@@ -0,0 +1,157 @@
<?php
// Chinese CN (simplified Chinese characters) Language Pack
// Translated by Le PengBD7KLE, Email: bd7kle@gmail.com
// Yuan WangBG3MDO, Email: bg3mdo@gmail.com
// Maintained by Yuan WangBG3MDO, Email: bg3mdo@gmail.com
// Last update on 03/08/2017
$lang = array (
// Banner texts
"digital_voice" => "数字语音",
"configuration" => "配置",
"dashboard_for" => "仪表盘 -",
// Banner links
"dashboard" => "仪表盘",
"admin" => "管理",
"power" => "电源",
"update" => "更新",
"backup_restore" => "备份/恢复",
"factory_reset" => "恢复出厂设置",
"live_logs" => "日志",
// Config page section headdings
"hardware_info" => "网关硬件信息",
"control_software" => "控制软件",
"mmdvmhost_config" => "MMDVMHost 配置",
"general_config" => "常规配置",
"dmr_config" => "DMR 配置",
"dstar_config" => "D-Star 配置",
"ysf_config" => "Yaesu System Fusion 配置",
"p25_config" => "P25 配置",
"nxdn_config" => "NXDN 配置",
"m17_config" => "M17 Configuration",
"pocsag_config" => "POCSAG 配置",
"mobilegps_config" => "Mobile GPS Configuration",
"fw_config" => "防火墙配置",
"remote_access_pw" => "远程访问密码",
// Config Page - Section General
"setting" => "设置",
"value" => "设置值",
"apply" => "应用设置",
// Config Page - Gateway Hardware Information
"hostname" => "主机名",
"kernel" => "内核",
"platform" => "平台",
"cpu_load" => "CPU 负荷",
"cpu_temp" => "CPU 温度",
// Config Page - Control Software
"controller_software" => "控制器软件",
"controller_mode" => "控制器模式",
// Config Page - MMDVMHost Configuration
"dmr_mode" => "DMR 模式",
"d-star_mode" => "D-Star 模式",
"ysf_mode" => "YSF 模式",
"p25_mode" => "P25 模式",
"nxdn_mode" => "NXDN 模式",
"m17_mode" => "M17 Mode",
"mmdvm_display" => "MMDVM 显示屏类型",
"mode_hangtime" => "模式停留时间",
// Config Page - General Configuration
"node_call" => "节点呼号",
"dmr_id" => "CCS7/DMR ID",
"radio_freq" => "电台频率",
"lattitude" => "纬度",
"longitude" => "经度",
"town" => "城市",
"country" => "国家",
"url" => "URL",
"radio_type" => "电台/调制解调器类型",
"node_type" => "节点类型",
"timezone" => "时区",
"dash_lang" => "仪表盘语言",
// Config Page - DMR Configuration
"dmr_master" => "DMR 主机 (MMDVMHost)",
"bm_master" => "BrandMeister 主机 ",
"bm_network" => "BrandMeister 网络",
"dmr_plus_master" => "DMR+ 主机 ",
"dmr_plus_network" => "DMR+ 网络",
"xlx_master" => "XLX 主机 ",
"xlx_enable" => "XLX 主机启用",
"dmr_cc" => "DMR 彩色码",
"dmr_embeddedlconly" => "DMR EmbeddedLCOnly",
"dmr_dumptadata" => "DMR DumpTAData",
// Config Page - D-Star Configuration
"dstar_rpt1" => "RPT1 呼号",
"dstar_rpt2" => "RPT2 呼号",
"dstar_irc_password" => "ircDDBGateway 密码",
"dstar_default_ref" => "默认反射器",
"aprs_host" => "APRS 服务器",
"dstar_irc_lang" => "ircDDBGateway 语言",
"dstar_irc_time" => "时间通告",
// Config Page - YSF Configuration
"ysf_startup_host" => "YSF 默认服务器",
// Config Page - P25 Configuration
"p25_startup_host" => "P25 默认服务器",
"p25_nac" => "P25 NAC",
// Config Page - NXDN Configuration
"nxdn_startup_host" => "NXDN 默认服务器",
"nxdn_ran" => "NXDN RAN",
// Config Page - M17 Configuration
"m17_startup_host" => "M17 Startup Host",
"m17_can" => "M17 CAN",
// Config Page - MobileGPS Configuration
"mobilegps_enable" => "MobileGPS Enable",
"mobilegps_port" => "GPS Port",
"mobilegps_speed" => "GPS Port Speed",
// Config Page - Firewall Configuration
"fw_dash" => "仪表盘访问",
"fw_irc" => "ircDDBGateway 远程",
"fw_ssh" => "SSH访问",
// Config Page - Password
"user" => "用户名",
"password" => "密码",
"set_password" => "设置密码",
// Dashboard Front Page - Repeater Info Pannel
"modes_enabled" => "启动的模式",
"net_status" => "网络状态",
"internet" => "互联网",
"radio_info" => "电台信息",
"dstar_repeater" => "D-Star 中继",
"dstar_net" => "D-Star 网络",
"dmr_repeater" => "DMR 中继",
"dmr_master" => "DMR 主机",
"ysf_net" => "YSF 网络",
"p25_radio" => "P25 电台",
"p25_net" => "P25 网络",
"nxdn_radio" => "NXDN 电台",
"nxdn_net" => "NXDN 网络",
"m17_radio" => "M17 Radio",
"m17_net" => "M17 Network",
// Dashboard Front Page - Calls
"time" => "时间",
"mode" => "模式",
"callsign" => "呼号",
"target" => "目标",
"src" => "", // Short version of "Source"
"dur" => "时长", // Short version of "Duration"
"loss" => "丢失",
"ber" => "误码率", // Short version of "Bit Error Rate"
// POCSAG Specific
"pocsag_list" => "DAPNET Gateway Activity",
"pocsag_timeslot" => "Time Slot",
"pocsag_msg" => "Message",
// Dashboard - Extra Info
"group" => "",
"logoff" => "注销",
"info" => "信息",
"utot" => "用户超时", // Short for User Timeout
"gtot" => "组超时", // Short for Group Timeout
// Dashboard Front Page / Admin - Section Headders
"last_heard_list" => "网关上最后 20 个呼叫",
"local_tx_list" => "最后 20 个本地呼叫",
"active_starnet_groups" => "激活的 Starnet 组",
"active_starnet_members" => "激活的 Starnet 组成员",
"d-star_link_manager" => "D-Star 连接管理器",
"d-star_link_status" => "D-Star 连接信息",
"service_status" => "服务状态"
);
?>
+157
View File
@@ -0,0 +1,157 @@
<?php
// Chinese HK (Traditional Chinese characters) Language Pack
// Translated by Le PengBD7KLE, Email: bd7kle@gmail.com
// Yuan WangBG3MDO, Email: bg3mdo@gmail.com
// Maintained by Yuan WangBG3MDO, Email: bg3mdo@gmail.com
// Last update on 03/08/2017
$lang = array (
// Banner texts
"digital_voice" => "數字語音",
"configuration" => "設定",
"dashboard_for" => "儀錶盤 -",
// Banner links
"dashboard" => "儀錶盤",
"admin" => "管理",
"power" => "電源",
"update" => "更新",
"backup_restore" => "備份/恢復",
"factory_reset" => "恢復出廠設定",
"live_logs" => "日誌",
// Config page section headdings
"hardware_info" => "網關硬體信息",
"control_software" => "控制軟體",
"mmdvmhost_config" => "MMDVMHost 設定",
"general_config" => "常規設定",
"dmr_config" => "DMR 設定",
"dstar_config" => "D-Star 設定",
"ysf_config" => "Yaesu System Fusion 設定",
"p25_config" => "P25 設定",
"nxdn_config" => "NXDN 設定",
"m17_config" => "M17 Configuration",
"pocsag_config" => "POCSAG 設定",
"mobilegps_config" => "Mobile GPS Configuration",
"fw_config" => "防火墻設定",
"remote_access_pw" => "遠程訪問密碼",
// Config Page - Section General
"setting" => "設定",
"value" => "設定值",
"apply" => "應用設定",
// Config Page - Gateway Hardware Information
"hostname" => "主機名",
"kernel" => "內核",
"platform" => "平臺",
"cpu_load" => "CPU 負荷",
"cpu_temp" => "CPU 溫度",
// Config Page - Control Software
"controller_software" => "控制器軟體",
"controller_mode" => "控制器模式",
// Config Page - MMDVMHost Configuration
"dmr_mode" => "DMR 模式",
"d-star_mode" => "D-Star 模式",
"ysf_mode" => "YSF 模式",
"p25_mode" => "P25 模式",
"nxdn_mode" => "NXDN 模式",
"m17_mode" => "M17 Mode",
"mmdvm_display" => "MMDVM 顯示器類型",
"mode_hangtime" => "模式停留時間",
// Config Page - General Configuration
"node_call" => "節點呼號",
"dmr_id" => "CCS7/DMR ID",
"radio_freq" => "電臺頻率",
"lattitude" => "緯度",
"longitude" => "經度",
"town" => "區域",
"country" => "國家",
"url" => "URL",
"radio_type" => "電臺/調製解調器類型",
"node_type" => "節點類型",
"timezone" => "時區",
"dash_lang" => "儀錶盤語言",
// Config Page - DMR Configuration
"dmr_master" => "DMR 主機 (MMDVMHost)",
"bm_master" => "BrandMeister 主機 ",
"bm_network" => "BrandMeister 網絡",
"dmr_plus_master" => "DMR+ 主機 ",
"dmr_plus_network" => "DMR+ 網絡",
"xlx_master" => "XLX 主機 ",
"xlx_enable" => "XLX 主機啟用",
"dmr_cc" => "DMR 彩色碼",
"dmr_embeddedlconly" => "DMR EmbeddedLCOnly",
"dmr_dumptadata" => "DMR DumpTAData",
// Config Page - D-Star Configuration
"dstar_rpt1" => "RPT1 呼號",
"dstar_rpt2" => "RPT2 呼號",
"dstar_irc_password" => "ircDDBGateway 密碼",
"dstar_default_ref" => "默認反射器",
"aprs_host" => "APRS 服務器",
"dstar_irc_lang" => "ircDDBGateway 語言",
"dstar_irc_time" => "時間通告",
// Config Page - YSF Configuration
"ysf_startup_host" => "YSF 默認服務器",
// Config Page - P25 Configuration
"p25_startup_host" => "P25 默認服務器",
"p25_nac" => "P25 NAC",
// Config Page - NXDN Configuration
"nxdn_startup_host" => "NXDN 默認服務器",
"nxdn_ran" => "NXDN RAN",
// Config Page - M17 Configuration
"m17_startup_host" => "M17 Startup Host",
"m17_can" => "M17 CAN",
// Config Page - MobileGPS Configuration
"mobilegps_enable" => "MobileGPS Enable",
"mobilegps_port" => "GPS Port",
"mobilegps_speed" => "GPS Port Speed",
// Config Page - Firewall Configuration
"fw_dash" => "儀表盤訪問",
"fw_irc" => "ircDDBGateway 遠程",
"fw_ssh" => "SSH訪問",
// Config Page - Password
"user" => "用戶名",
"password" => "密碼",
"set_password" => "密碼設定",
// Dashboard Front Page - Repeater Info Pannel
"modes_enabled" => "啟用的模式",
"net_status" => "網絡狀態",
"internet" => "互聯網",
"radio_info" => "電臺信息",
"dstar_repeater" => "D-Star 中繼",
"dstar_net" => "D-Star 網絡",
"dmr_repeater" => "DMR 中繼",
"dmr_master" => "DMR 主機",
"ysf_net" => "YSF 網絡",
"p25_radio" => "P25 電臺",
"p25_net" => "P25 網絡",
"nxdn_radio" => "NXDN 電臺",
"nxdn_net" => "NXDN 網絡",
"m17_radio" => "M17 Radio",
"m17_net" => "M17 Network",
// Dashboard Front Page - Calls
"time" => "時間",
"mode" => "模式",
"callsign" => "呼號",
"target" => "目標",
"src" => "", // Short version of "Source"
"dur" => "時長", // Short version of "Duration"
"loss" => "丟失",
"ber" => "誤碼率", // Short version of "Bit Error Rate"
// POCSAG Specific
"pocsag_list" => "DAPNET Gateway Activity",
"pocsag_timeslot" => "Time Slot",
"pocsag_msg" => "Message",
// Dashboard - Extra Info
"group" => "",
"logoff" => "註銷",
"info" => "信息",
"utot" => "用户超時", // Short for User Timeout
"gtot" => "組超時", // Short for Group Timeout
// Dashboard Front Page / Admin - Section Headders
"last_heard_list" => "網關上最後 20 個呼叫",
"local_tx_list" => "最後 20 個本地呼叫",
"active_starnet_groups" => "激活 Starnet 組",
"active_starnet_members" => "激活 Starnet 組成員",
"d-star_link_manager" => "D-Star 連接管理器",
"d-star_link_status" => "D-Star 連接信息",
"service_status" => "服務狀態"
);
?>
+155
View File
@@ -0,0 +1,155 @@
<?php
// Chinese TW (Traditional Chinese characters) Language Pack
// Translated by Yngwie ChouBU2EA, Email: bu2ea.tw@gmail.com
// Last update on 02/13/2018
$lang = array (
// Banner texts
"digital_voice" => "數位語音",
"configuration" => "組態設定",
"dashboard_for" => "控制台 -",
// Banner links
"dashboard" => "控制台",
"admin" => "管理",
"power" => "電源",
"update" => "更新",
"backup_restore" => "備份/還原",
"factory_reset" => "恢復出廠設定",
"live_logs" => "日誌",
// Config page section headdings
"hardware_info" => "硬體資訊",
"control_software" => "控制軟體",
"mmdvmhost_config" => "MMDVMHost 設定",
"general_config" => "一般設定",
"dmr_config" => "DMR 設定",
"dstar_config" => "D-Star 設定",
"ysf_config" => "Yaesu System Fusion 設定",
"p25_config" => "P25 設定",
"nxdn_config" => "NXDN 設定",
"m17_config" => "M17 Configuration",
"pocsag_config" => "POCSAG 設定",
"mobilegps_config" => "Mobile GPS Configuration",
"fw_config" => "防火牆設定",
"remote_access_pw" => "遠端遙控密碼",
// Config Page - Section General
"setting" => "設定",
"value" => "設定值",
"apply" => "變更設定",
// Config Page - Gateway Hardware Information
"hostname" => "主機名稱",
"kernel" => "核心",
"platform" => "平台",
"cpu_load" => "CPU 負載",
"cpu_temp" => "CPU 溫度",
// Config Page - Control Software
"controller_software" => "控制器軟體",
"controller_mode" => "控制器模式",
// Config Page - MMDVMHost Configuration
"dmr_mode" => "DMR 模式",
"d-star_mode" => "D-Star 模式",
"ysf_mode" => "YSF 模式",
"p25_mode" => "P25 模式",
"nxdn_mode" => "NXDN 模式",
"m17_mode" => "M17 Mode",
"mmdvm_display" => "MMDVM 螢幕類型",
"mode_hangtime" => "模式保持時間",
// Config Page - General Configuration
"node_call" => "節點呼號",
"dmr_id" => "CCS7/DMR ID",
"radio_freq" => "電台頻率",
"lattitude" => "緯度",
"longitude" => "經度",
"town" => "城市",
"country" => "國家",
"url" => "URL",
"radio_type" => "Modem 類型",
"node_type" => "節點類型",
"timezone" => "時區",
"dash_lang" => "介面語言",
// Config Page - DMR Configuration
"dmr_master" => "DMR 伺服器 (MMDVMHost)",
"bm_master" => "BrandMeister 伺服器",
"bm_network" => "BrandMeister 網路",
"dmr_plus_master" => "DMR+ 伺服器",
"dmr_plus_network" => "DMR+ 網路",
"xlx_master" => "XLX 伺服器",
"xlx_enable" => "啟用 XLX 伺服器",
"dmr_cc" => "DMR 色碼",
"dmr_embeddedlconly" => "僅允許指定 ID 連接",
"dmr_dumptadata" => "DMR DumpTAData",
// Config Page - D-Star Configuration
"dstar_rpt1" => "RPT1 呼號",
"dstar_rpt2" => "RPT2 呼號",
"dstar_irc_password" => "ircDDBGateway 密碼",
"dstar_default_ref" => "預設反射器",
"aprs_host" => "APRS 伺服器",
"dstar_irc_lang" => "ircDDBGateway 語言",
"dstar_irc_time" => "時間提示",
// Config Page - YSF Configuration
"ysf_startup_host" => "YSF 預設伺服器",
// Config Page - P25 Configuration
"p25_startup_host" => "P25 預設伺服器",
"p25_nac" => "P25 NAC",
// Config Page - NXDN Configuration
"nxdn_startup_host" => "NXDN 預設伺服器",
"nxdn_ran" => "NXDN RAN",
// Config Page - M17 Configuration
"m17_startup_host" => "M17 Startup Host",
"m17_can" => "M17 CAN",
// Config Page - MobileGPS Configuration
"mobilegps_enable" => "MobileGPS Enable",
"mobilegps_port" => "GPS Port",
"mobilegps_speed" => "GPS Port Speed",
// Config Page - Firewall Configuration
"fw_dash" => "控制台存取",
"fw_irc" => "ircDDBGateway 遠端遙控",
"fw_ssh" => "SSH 存取",
// Config Page - Password
"user" => "用戶名",
"password" => "密碼",
"set_password" => "密碼設定",
// Dashboard Front Page - Repeater Info Pannel
"modes_enabled" => "已啟用模式",
"net_status" => "網路狀態",
"internet" => "網際網路",
"radio_info" => "RF 電台資訊",
"dstar_repeater" => "D-Star 中繼",
"dstar_net" => "D-Star 網路",
"dmr_repeater" => "DMR 中繼",
"dmr_master" => "DMR 伺服器",
"ysf_net" => "YSF 網路",
"p25_radio" => "P25 電台",
"p25_net" => "P25 網路",
"nxdn_radio" => "NXDN 電台",
"nxdn_net" => "NXDN 網路",
"m17_radio" => "M17 Radio",
"m17_net" => "M17 Network",
// Dashboard Front Page - Calls
"time" => "時間",
"mode" => "模式",
"callsign" => "呼號",
"target" => "目標",
"src" => "來源", // Short version of "Source"
"dur" => "歷時", // Short version of "Duration"
"loss" => "丟失率",
"ber" => "誤碼率", // Short version of "Bit Error Rate"
// POCSAG Specific
"pocsag_list" => "DAPNET Gateway Activity",
"pocsag_timeslot" => "Time Slot",
"pocsag_msg" => "Message",
// Dashboard - Extra Info
"group" => "群組",
"logoff" => "登出",
"info" => "資訊",
"utot" => "用户逾時", // Short for User Timeout
"gtot" => "群組逾時", // Short for Group Timeout
// Dashboard Front Page / Admin - Section Headders
"last_heard_list" => "最近 20 個通訊",
"local_tx_list" => "最近 20 個本地 RF 通訊",
"active_starnet_groups" => "啟用 Starnet 群組",
"active_starnet_members" => "啟用 Starnet 成員",
"d-star_link_manager" => "D-Star 連線管理",
"d-star_link_status" => "D-Star 連線狀態",
"service_status" => "服務狀態"
);
?>
+156
View File
@@ -0,0 +1,156 @@
<?php
//
// Dutch NL Language Pack for Pi-Star translated by Rob van Rheenen P0DIB
// Updated: 04-Aug-2017
//
$lang = array (
// Banner texts
"digital_voice" => "Digital Voice",
"configuration" => "Configuratie",
"dashboard_for" => "Dashboard voor",
// Banner links
"dashboard" => "Dashboard",
"admin" => "Admin",
"power" => "Power",
"update" => "Update",
"backup_restore" => "Backup/Restore",
"factory_reset" => "Reset all",
"live_logs" => "Actuele Logs",
// Config page section headdings
"hardware_info" => "Gateway hardware informatie",
"control_software" => "Controller Host software",
"mmdvmhost_config" => "MMDVMHost configuratie",
"general_config" => "Algemene configuratie",
"dmr_config" => "DMR configuratie",
"dstar_config" => "D-Star configuratie",
"ysf_config" => "Yaesu System Fusion configuratie",
"p25_config" => "P25 configuratie",
"nxdn_config" => "NXDN configuratie",
"m17_config" => "M17 Configuration",
"pocsag_config" => "POCSAG configuratie",
"mobilegps_config" => "Mobile GPS Configuration",
"wifi_config" => "Wireless configuratie",
"fw_config" => "Firewall configuratie",
"remote_access_pw" => "Remote Access paswoord",
// Config Page - Section General
"setting" => "Instelling",
"value" => "Waarde",
"apply" => "Toepassen",
// Config Page - Gateway Hardware Information
"hostname" => "Hostnaam",
"kernel" => "Kernel",
"platform" => "Platform",
"cpu_load" => "CPU Load",
"cpu_temp" => "CPU Temp",
// Config Page - Control Software
"controller_software" => "Controller software",
"controller_mode" => "Controller modus",
// Config Page - MMDVMHost Configuration
"dmr_mode" => "DMR modus",
"d-star_mode" => "D-Star modus",
"ysf_mode" => "YSF modus",
"p25_mode" => "P25 modus",
"nxdn_mode" => "NXDN modus",
"m17_mode" => "M17 Mode",
"mmdvm_display" => "MMDVM display type",
"mode_hangtime" => "Mode hangtijd",
// Config Page - General Configuration
"node_call" => "Node roepletters",
"dmr_id" => "CCS7/DMR ID",
"radio_freq" => "Radio frequentie",
"lattitude" => "Breedtegraad",
"longitude" => "Lengtegraad",
"town" => "Plaats",
"country" => "Land",
"url" => "URL",
"radio_type" => "Radio/Modem type",
"node_type" => "Node type",
"timezone" => "Systeem tijdzone",
"dash_lang" => "Dashboard taal",
// Config Page - DMR Configuration
"dmr_master" => "DMR Master (MMDVMHost)",
"bm_master" => "BrandMeister Master",
"bm_network" => "BrandMeister netwerk",
"dmr_plus_master" => "DMR+ Master",
"dmr_plus_network" => "DMR+ netwerk",
"xlx_master" => "XLX Master",
"xlx_enable" => "XLX Master AAN",
"dmr_cc" => "DMR kleur code (CC)",
"dmr_embeddedlconly" => "DMR EmbeddedLCOnly",
"dmr_dumptadata" => "DMR DumpTAData",
// Config Page - D-Star Configuration
"dstar_rpt1" => "RPT1 roepletters",
"dstar_rpt2" => "RPT2 roepletters",
"dstar_irc_password" => "ircDDBGateway paswoord",
"dstar_default_ref" => "Standaard reflector",
"aprs_host" => "APRS Host",
"dstar_irc_lang" => "ircDDBGateway taal",
"dstar_irc_time" => "Tijd uitzenden",
// Config Page - YSF Configuration
"ysf_startup_host" => "YSF standaard Host",
// Config Page - P25 Configuration
"p25_startup_host" => "P25 standaard Host",
"p25_nac" => "P25 NAC",
// Config Page - NXDN Configuration
"nxdn_startup_host" => "NXDN standaard Host",
"nxdn_ran" => "NXDN RAN",
// Config Page - M17 Configuration
"m17_startup_host" => "M17 Startup Host",
"m17_can" => "M17 CAN",
// Config Page - MobileGPS Configuration
"mobilegps_enable" => "MobileGPS Enable",
"mobilegps_port" => "GPS Port",
"mobilegps_speed" => "GPS Port Speed",
// Config Page - Firewall Configuration
"fw_dash" => "Dashboard toegang",
"fw_irc" => "ircDDBGateway op afstand",
"fw_ssh" => "SSH toegang",
// Config Page - Password
"user" => "Gebruikersnaam",
"password" => "Paswoord",
"set_password" => "Voer paswoord in",
// Dashboard Front Page - Repeater Info Pannel
"modes_enabled" => "Actieve modi",
"net_status" => "Netwerk status",
"internet" => "Internet",
"radio_info" => "Radio info",
"dstar_repeater" => "D-Star repeater",
"dstar_net" => "D-Star netwerk",
"dmr_repeater" => "DMR repeater",
"dmr_master" => "DMR Master",
"ysf_net" => "YSF netwerk",
"p25_radio" => "P25 Radio",
"p25_net" => "P25 netwerk",
"nxdn_radio" => "NXDN Radio",
"nxdn_net" => "NXDN netwerk",
"m17_radio" => "M17 Radio",
"m17_net" => "M17 Network",
// Dashboard Front Page - Calls
"time" => "Tijd",
"mode" => "Mode",
"callsign" => "Roepletters",
"target" => "Doel",
"src" => "Bron", // Short version of "Source"
"dur" => "Duur", // Short version of "Duration"
"loss" => "Verlies",
"ber" => "BER", // Short version of "Bit Error Rate"
// POCSAG Specific
"pocsag_list" => "DAPNET Gateway Activity",
"pocsag_timeslot" => "Time Slot",
"pocsag_msg" => "Message",
// Dashboard - Extra Info
"group" => "Groep",
"logoff" => "Uitloggen",
"info" => "Informatie",
"utot" => "UTOT", // Short for User Timeout
"gtot" => "GTOT", // Short for Group Timeout
// Dashboard Front Page / Admin - Section Headders
"last_heard_list" => "Laatste 20 oproepen gehoord (RX) via deze Gateway",
"local_tx_list" => "Laatste 20 oproepen verzonden (TX) via deze Gateway",
"active_starnet_groups" => "Actieve Starnet groepen",
"active_starnet_members" => "Actieve Starnet groepsleden",
"d-star_link_manager" => "D-Star Link Manager",
"d-star_link_status" => "D-Star Link Informatie",
"service_status" => "Service Status"
);
?>
+157
View File
@@ -0,0 +1,157 @@
<?php
//
// English UK Language Pack
// Andy Taylor (MW0MWZ)
// Updated: 02-Aug-2017
//
$lang = array (
// Banner texts
"digital_voice" => "Digital Voice",
"configuration" => "Configuration",
"dashboard_for" => "Dashboard for",
// Banner links
"dashboard" => "Dashboard",
"admin" => "Admin",
"power" => "Power",
"update" => "Update",
"backup_restore" => "Backup/Restore",
"factory_reset" => "Factory Reset",
"live_logs" => "Live Logs",
// Config page section headdings
"hardware_info" => "Gateway Hardware Information",
"control_software" => "Control Software",
"mmdvmhost_config" => "MMDVMHost Configuration",
"general_config" => "General Configuration",
"dmr_config" => "DMR Configuration",
"dstar_config" => "D-Star Configuration",
"ysf_config" => "Yaesu System Fusion Configuration",
"p25_config" => "P25 Configuration",
"nxdn_config" => "NXDN Configuration",
"m17_config" => "M17 Configuration",
"pocsag_config" => "POCSAG Configuration",
"mobilegps_config" => "Mobile GPS Configuration",
"wifi_config" => "Wireless Configuration",
"fw_config" => "Firewall Configuration",
"remote_access_pw" => "Remote Access Password",
// Config Page - Section General
"setting" => "Setting",
"value" => "Value",
"apply" => "Apply Changes",
// Config Page - Gateway Hardware Information
"hostname" => "Hostname",
"kernel" => "Kernel",
"platform" => "Platform",
"cpu_load" => "CPU Load",
"cpu_temp" => "CPU Temp",
// Config Page - Control Software
"controller_software" => "Controller Software",
"controller_mode" => "Controller Mode",
// Config Page - MMDVMHost Configuration
"dmr_mode" => "DMR Mode",
"d-star_mode" => "D-Star Mode",
"ysf_mode" => "YSF Mode",
"p25_mode" => "P25 Mode",
"nxdn_mode" => "NXDN Mode",
"m17_mode" => "M17 Mode",
"mmdvm_display" => "MMDVM Display Type",
"mode_hangtime" => "Mode Hangtime",
// Config Page - General Configuration
"node_call" => "Node Callsign",
"dmr_id" => "CCS7/DMR ID",
"radio_freq" => "Radio Frequency",
"lattitude" => "Latitude",
"longitude" => "Longitude",
"town" => "Town",
"country" => "Country",
"url" => "URL",
"radio_type" => "Radio/Modem Type",
"node_type" => "Node Type",
"timezone" => "System Time Zone",
"dash_lang" => "Dashboard Language",
// Config Page - DMR Configuration
"dmr_master" => "DMR Master (MMDVMHost)",
"bm_master" => "BrandMeister Master",
"bm_network" => "BrandMeister Network",
"dmr_plus_master" => "DMR+ Master",
"dmr_plus_network" => "DMR+ Network",
"xlx_master" => "XLX Master",
"xlx_enable" => "XLX Master Enable",
"dmr_cc" => "DMR Colour Code",
"dmr_embeddedlconly" => "DMR EmbeddedLCOnly",
"dmr_dumptadata" => "DMR DumpTAData",
// Config Page - D-Star Configuration
"dstar_rpt1" => "RPT1 Callsign",
"dstar_rpt2" => "RPT2 Callsign",
"dstar_irc_password" => "Remote Password",
"dstar_default_ref" => "Default Reflector",
"aprs_host" => "APRS Host",
"dstar_irc_lang" => "ircDDBGateway Language",
"dstar_irc_time" => "Time Announcements",
// Config Page - YSF Configuration
"ysf_startup_host" => "YSF Startup Host",
// Config Page - P25 Configuration
"p25_startup_host" => "P25 Startup Host",
"p25_nac" => "P25 NAC",
// Config Page - NXDN Configuration
"nxdn_startup_host" => "NXDN Startup Host",
"nxdn_ran" => "NXDN RAN",
// Config Page - M17 Configuration
"m17_startup_host" => "M17 Startup Host",
"m17_can" => "M17 CAN",
// Config Page - MobileGPS Configuration
"mobilegps_enable" => "MobileGPS Enable",
"mobilegps_port" => "GPS Port",
"mobilegps_speed" => "GPS Port Speed",
// Config Page - Firewall Configuration
"fw_dash" => "Dashboard Access",
"fw_irc" => "ircDDBGateway Remote",
"fw_ssh" => "SSH Access",
// Config Page - Password
"user" => "User Name",
"password" => "Password",
"set_password" => "Set Password",
// Dashboard Front Page - Repeater Info Pannel
"modes_enabled" => "Modes Enabled",
"net_status" => "Network Status",
"internet" => "Internet",
"radio_info" => "Radio Info",
"dstar_repeater" => "D-Star Repeater",
"dstar_net" => "D-Star Network",
"dmr_repeater" => "DMR Repeater",
"dmr_master" => "DMR Master",
"ysf_net" => "YSF Network",
"p25_radio" => "P25 Radio",
"p25_net" => "P25 Network",
"nxdn_radio" => "NXDN Radio",
"nxdn_net" => "NXDN Network",
"m17_radio" => "M17 Radio",
"m17_net" => "M17 Network",
// Dashboard Front Page - Calls
"time" => "Time",
"mode" => "Mode",
"callsign" => "Callsign",
"target" => "Target",
"src" => "Src", // Short version of "Source"
"dur" => "Dur", // Short version of "Duration"
"loss" => "Loss",
"ber" => "BER", // Short version of "Bit Error Rate"
// POCSAG Specific
"pocsag_list" => "DAPNET Gateway Activity",
"pocsag_timeslot" => "Time Slot",
"pocsag_msg" => "Message",
// Dashboard - Extra Info
"group" => "Group",
"logoff" => "LogOff",
"info" => "Information",
"utot" => "UTOT", // Short for User Timeout
"gtot" => "GTOT", // Short for Group Timeout
// Dashboard Front Page / Admin - Section Headders
"last_heard_list" => "Gateway Activity",
"local_tx_list" => "Local RF Activity",
"active_starnet_groups" => "Active Starnet Groups",
"active_starnet_members" => "Active Starnet Group Members",
"d-star_link_manager" => "D-Star Link Manager",
"d-star_link_status" => "D-Star Link Information",
"service_status" => "Service Status"
);
?>
+157
View File
@@ -0,0 +1,157 @@
<?php
//
// English US Language Pack
// Andy Taylor (MW0MWZ)
// Updated: 02-Aug-2017
//
$lang = array (
// Banner texts
"digital_voice" => "Digital Voice",
"configuration" => "Configuration",
"dashboard_for" => "Dashboard for",
// Banner links
"dashboard" => "Dashboard",
"admin" => "Admin",
"power" => "Power",
"update" => "Update",
"backup_restore" => "Backup/Restore",
"factory_reset" => "Factory Reset",
"live_logs" => "Live Logs",
// Config page section headdings
"hardware_info" => "Gateway Hardware Information",
"control_software" => "Control Software",
"mmdvmhost_config" => "MMDVMHost Configuration",
"general_config" => "General Configuration",
"dmr_config" => "DMR Configuration",
"dstar_config" => "D-Star Configuration",
"ysf_config" => "Yaesu System Fusion Configuration",
"p25_config" => "P25 Configuration",
"nxdn_config" => "NXDN Configuration",
"m17_config" => "M17 Configuration",
"pocsag_config" => "POCSAG Configuration",
"mobilegps_config" => "Mobile GPS Configuration",
"wifi_config" => "Wireless Configuration",
"fw_config" => "Firewall Configuration",
"remote_access_pw" => "Remote Access Password",
// Config Page - Section General
"setting" => "Setting",
"value" => "Value",
"apply" => "Apply Changes",
// Config Page - Gateway Hardware Information
"hostname" => "Hostname",
"kernel" => "Kernel",
"platform" => "Platform",
"cpu_load" => "CPU Load",
"cpu_temp" => "CPU Temp",
// Config Page - Control Software
"controller_software" => "Controller Software",
"controller_mode" => "Controller Mode",
// Config Page - MMDVMHost Configuration
"dmr_mode" => "DMR Mode",
"d-star_mode" => "D-Star Mode",
"ysf_mode" => "YSF Mode",
"p25_mode" => "P25 Mode",
"nxdn_mode" => "NXDN Mode",
"m17_mode" => "M17 Mode",
"mmdvm_display" => "MMDVM Display Type",
"mode_hangtime" => "Mode Hangtime",
// Config Page - General Configuration
"node_call" => "Node Callsign",
"dmr_id" => "CCS7/DMR ID",
"radio_freq" => "Radio Frequency",
"lattitude" => "Latitude",
"longitude" => "Longitude",
"town" => "Town",
"country" => "Country",
"url" => "URL",
"radio_type" => "Radio/Modem Type",
"node_type" => "Node Type",
"timezone" => "System Time Zone",
"dash_lang" => "Dashboard Language",
// Config Page - DMR Configuration
"dmr_master" => "DMR Master (MMDVMHost)",
"bm_master" => "BrandMeister Master",
"bm_network" => "BrandMeister Network",
"dmr_plus_master" => "DMR+ Master",
"dmr_plus_network" => "DMR+ Network",
"xlx_master" => "XLX Master",
"xlx_enable" => "XLX Master Enable",
"dmr_cc" => "DMR Color Code",
"dmr_embeddedlconly" => "DMR EmbeddedLCOnly",
"dmr_dumptadata" => "DMR DumpTAData",
// Config Page - D-Star Configuration
"dstar_rpt1" => "RPT1 Callsign",
"dstar_rpt2" => "RPT2 Callsign",
"dstar_irc_password" => "Remote Password",
"dstar_default_ref" => "Default Reflector",
"aprs_host" => "APRS Host",
"dstar_irc_lang" => "ircDDBGateway Language",
"dstar_irc_time" => "Time Announcements",
// Config Page - YSF Configuration
"ysf_startup_host" => "YSF Startup Host",
// Config Page - P25 Configuration
"p25_startup_host" => "P25 Startup Host",
"p25_nac" => "P25 NAC",
// Config Page - NXDN Configuration
"nxdn_startup_host" => "NXDN Startup Host",
"nxdn_ran" => "NXDN RAN",
// Config Page - M17 Configuration
"m17_startup_host" => "M17 Startup Host",
"m17_can" => "M17 CAN",
// Config Page - MobileGPS Configuration
"mobilegps_enable" => "MobileGPS Enable",
"mobilegps_port" => "GPS Port",
"mobilegps_speed" => "GPS Port Speed",
// Config Page - Firewall Configuration
"fw_dash" => "Dashboard Access",
"fw_irc" => "ircDDBGateway Remote",
"fw_ssh" => "SSH Access",
// Config Page - Password
"user" => "User Name",
"password" => "Password",
"set_password" => "Set Password",
// Dashboard Front Page - Repeater Info Pannel
"modes_enabled" => "Modes Enabled",
"net_status" => "Network Status",
"internet" => "Internet",
"radio_info" => "Radio Info",
"dstar_repeater" => "D-Star Repeater",
"dstar_net" => "D-Star Network",
"dmr_repeater" => "DMR Repeater",
"dmr_master" => "DMR Master",
"ysf_net" => "YSF Network",
"p25_radio" => "P25 Radio",
"p25_net" => "P25 Network",
"nxdn_radio" => "NXDN Radio",
"nxdn_net" => "NXDN Network",
"m17_radio" => "M17 Radio",
"m17_net" => "M17 Network",
// Dashboard Front Page - Calls
"time" => "Time",
"mode" => "Mode",
"callsign" => "Callsign",
"target" => "Target",
"src" => "Src", // Short version of "Source"
"dur" => "Dur", // Short version of "Duration"
"loss" => "Loss",
"ber" => "BER", // Short version of "Bit Error Rate"
// POCSAG Specific
"pocsag_list" => "DAPNET Gateway Activity",
"pocsag_timeslot" => "Time Slot",
"pocsag_msg" => "Message",
// Dashboard - Extra Info
"group" => "Group",
"logoff" => "LogOff",
"info" => "Information",
"utot" => "UTOT", // Short for User Timeout
"gtot" => "GTOT", // Short for Group Timeout
// Dashboard Front Page / Admin - Section Headders
"last_heard_list" => "Gateway Activity",
"local_tx_list" => "Local RF Activity",
"active_starnet_groups" => "Active Starnet Groups",
"active_starnet_members" => "Active Starnet Group Members",
"d-star_link_manager" => "D-Star Link Manager",
"d-star_link_status" => "D-Star Link Information",
"service_status" => "Service Status"
);
?>
+157
View File
@@ -0,0 +1,157 @@
<?php
//
// Frehcn FR Language Pack
// Pierre Philippe (F4MZI)
// Updated: 01-Nov-2017
//
$lang = array (
// Banner texts
"digital_voice" => "Relais numérique",
"configuration" => "Configuration",
"dashboard_for" => "Console pour",
// Banner links
"dashboard" => "Console",
"admin" => "Administration",
"power" => "Arr&ecirc;t/Red&eacute;marrage",
"update" => "Mise &agrave; jour",
"backup_restore" => "Sauvegarde/Restauration",
"factory_reset" => "R&eacute;initialisation Usine",
"live_logs" => "Surveillance des Logs",
// Config page section headdings
"hardware_info" => "Informations mat&eacute;rielles de la passerelle",
"control_software" => "Contr&ocirc;le logiciel",
"mmdvmhost_config" => "Configuration de MMDVMHost",
"general_config" => "Configuration g&eacute;n&eacute;rale",
"dmr_config" => "Configuration DMR",
"dstar_config" => "Configuration D-STAR",
"ysf_config" => "Configuration Yaesu System Fusion",
"p25_config" => "Configuration P25",
"nxdn_config" => "Configuration NXDN",
"m17_config" => "M17 Configuration",
"pocsag_config" => "Configuration POCSAG",
"mobilegps_config" => "Mobile GPS Configuration",
"wifi_config" => "Configuration R&eacute;seau WIFI",
"fw_config" => "Configuration du Firewall",
"remote_access_pw" => "Mot de passe acc&egrave;s distant",
// Config Page - Section General
"setting" => "Param&egrave;tres",
"value" => "Valeur",
"apply" => "Appliquer les modifications",
// Config Page - Gateway Hardware Information
"hostname" => "Nom d'h&ocirc;te",
"kernel" => "Kernel",
"platform" => "Plateforme",
"cpu_load" => "Charge CPU",
"cpu_temp" => "Temp&eacute;rature CPU",
// Config Page - Control Software
"controller_software" => "Logiciel controleur",
"controller_mode" => "Mode controleur",
// Config Page - MMDVMHost Configuration
"dmr_mode" => "Mode DMR",
"d-star_mode" => "Mode D-Star",
"ysf_mode" => "Mode YSF",
"p25_mode" => "Mode P25",
"nxdn_mode" => "Mode NXDN",
"m17_mode" => "M17 Mode",
"mmdvm_display" => "Afficheur MMDVM",
"mode_hangtime" => "Mode Hangtime",
// Config Page - General Configuration
"node_call" => "Indicatif du Node",
"dmr_id" => "Id CCS7/DMR",
"radio_freq" => "Fr&eacute;quence radio",
"lattitude" => "Latitude",
"longitude" => "Longitude",
"town" => "Ville",
"country" => "Pays",
"url" => "URL",
"radio_type" => "Mod&egrave;le Radio/Modem",
"node_type" => "Type de Node",
"timezone" => "Fuseau horaire",
"dash_lang" => "Langage de la console",
// Config Page - DMR Configuration
"dmr_master" => "Master DMR(MMDVMHost)",
"bm_master" => "Master BrandMeister",
"bm_network" => "R&eacute;seau BrandMeister",
"dmr_plus_master" => "Master DMR+",
"dmr_plus_network" => "R&eacute;seau DMR+",
"xlx_master" => "Master XLX",
"xlx_enable" => "Master XLX actif",
"dmr_cc" => "Code Couleur DMR",
"dmr_embeddedlconly" => "DMR LC int&eacute;gr&eacute; uniquement",
"dmr_dumptadata" => "DMR DumpTAData",
// Config Page - D-Star Configuration
"dstar_rpt1" => "Indicatif RPT1",
"dstar_rpt2" => "Indicatif RPT2",
"dstar_irc_password" => "Mot de passe ircDDBGateway",
"dstar_default_ref" => "Reflecteur par d&eacute;faut",
"aprs_host" => "H&ocirc;te APRS",
"dstar_irc_lang" => "Langage ircDDBGateway",
"dstar_irc_time" => "Annonces horaires",
// Config Page - YSF Configuration
"ysf_startup_host" => "Room YSF au d&eacute;marrage",
// Config Page - P25 Configuration
"p25_startup_host" => "H&ocirc;te de d&eacute;marrage P25",
"p25_nac" => "P25 NAC",
// Config Page - NXDN Configuration
"nxdn_startup_host" => "H&ocirc;te de d&eacute;marrage NXDN",
"nxdn_ran" => "NXDN RAN",
// Config Page - M17 Configuration
"m17_startup_host" => "M17 Startup Host",
"m17_can" => "M17 CAN",
// Config Page - MobileGPS Configuration
"mobilegps_enable" => "MobileGPS Enable",
"mobilegps_port" => "GPS Port",
"mobilegps_speed" => "GPS Port Speed",
// Config Page - Firewall Configuration
"fw_dash" => "Acc&egrave;s Console",
"fw_irc" => "Commande &agrave; distance ircDDBGateway",
"fw_ssh" => "Acc&egrave;s SSH",
// Config Page - Password
"user" => "Nom d'utilisateur",
"password" => "Mot de passe",
"set_password" => "D&eacute;finir le mot de passe",
// Dashboard Front Page - Repeater Info Pannel
"modes_enabled" => "Modes actifs",
"net_status" => "&Eacute;tat du r&eacute;seau",
"internet" => "Internet",
"radio_info" => "Info Radio",
"dstar_repeater" => "Relais D-Star",
"dstar_net" => "R&eacute;seau D-Star",
"dmr_repeater" => "Relais DMR",
"dmr_master" => "Master DMR",
"ysf_net" => "R&eacute;seau YSF",
"p25_radio" => "Radio P25",
"p25_net" => "R&eacute;seau P25",
"nxdn_radio" => "Radio NXDN",
"nxdn_net" => "R&eacute;seau NXDN",
"m17_radio" => "M17 Radio",
"m17_net" => "M17 Network",
// Dashboard Front Page - Calls
"time" => "Heure",
"mode" => "Mode",
"callsign" => "Indicatif ",
"target" => "Cible",
"src" => "Source ", // Short version of "Source"
"dur" => "Dur&eacute;e ", // Short version of "Duration"
"loss" => "Pertes",
"ber" => "BER", // Short version of "Bit Error Rate"
// POCSAG Specific
"pocsag_list" => "DAPNET Gateway Activity",
"pocsag_timeslot" => "Time Slot",
"pocsag_msg" => "Message",
// Dashboard - Extra Info
"group" => "Groupe",
"logoff" => "D&eacute;connexion",
"info" => "Information",
"utot" => "UTOT", // Short for User Timeout
"gtot" => "GTOT", // Short for Group Timeout
// Dashboard Front Page / Admin - Section Headders
"last_heard_list" => "Activit&eacute; de la passerelle",
"local_tx_list" => "Activit&eacute; locale de la voie radio",
"active_starnet_groups" => "Active Starnet Groups",
"active_starnet_members" => "Active Starnet Group Members",
"d-star_link_manager" => "Administrateur du lien D-Star",
"d-star_link_status" => "Informations du lien D-Star",
"service_status" => "&Eacute;tat du service"
);
?>
+157
View File
@@ -0,0 +1,157 @@
<?php
//
// German Language Pack
// by Klaus, DL5RFK
// 08-Aug-2017
//
$lang = array (
// Banner texts
"digital_voice" => "Digital Voice",
"configuration" => "Konfiguration",
"dashboard_for" => "Tableau f&uuml;r",
// Banner links
"dashboard" => "Tableau",
"admin" => "Admin",
"power" => "Strom",
"update" => "Aktualisieren",
"backup_restore" => "Datensicherung/Wiederherstellung",
"factory_reset" => "Werkseinstellung",
"live_logs" => "Protokoll",
// Config page section headdings
"hardware_info" => "Gateway Hardware Information",
"control_software" => "Kontrollsoftware",
"mmdvmhost_config" => "MMDVMHost Konfiguration",
"general_config" => "Basis Konfiguration",
"dmr_config" => "DMR Konfiguration",
"dstar_config" => "D-Star Konfiguration",
"ysf_config" => "Yaesu System Fusion Konfiguration",
"p25_config" => "P25 Konfiguration",
"nxdn_config" => "NXDN Konfiguration",
"m17_config" => "M17 Configuration",
"pocsag_config" => "POCSAG Konfiguration",
"mobilegps_config" => "Mobile GPS Configuration",
"wifi_config" => "Wlan Konfiguration",
"fw_config" => "Firewall Konfiguration",
"remote_access_pw" => "Fernzugriff",
// Config Page - Section General
"setting" => "Einstellung",
"value" => "Wert",
"apply" => "Speichern",
// Config Page - Gateway Hardware Information
"hostname" => "Rechnername",
"kernel" => "Kernel",
"platform" => "Plattform",
"cpu_load" => "CPU Last",
"cpu_temp" => "CPU Temp",
// Config Page - Control Software
"controller_software" => "Kontroller Software",
"controller_mode" => "Kontroller Mode",
// Config Page - MMDVMHost Configuration
"dmr_mode" => "DMR Modus",
"d-star_mode" => "D-Star Modus",
"ysf_mode" => "YSF Modus",
"p25_mode" => "P25 Modus",
"nxdn_mode" => "NXDN Modus",
"m17_mode" => "M17 Mode",
"mmdvm_display" => "MMDVM Display Typ",
"mode_hangtime" => "Mode Nachlaufzeit",
// Config Page - General Configuration
"node_call" => "Node Rufzeichen",
"dmr_id" => "CCS7/DMR ID",
"radio_freq" => "Radio Frequenz",
"lattitude" => "Breitengrad",
"longitude" => "L&auml;ngengrad",
"town" => "Stadt",
"country" => "Land",
"url" => "URL",
"radio_type" => "Radio/Modem Typ",
"node_type" => "Node Typ",
"timezone" => "Systemzeit Zone",
"dash_lang" => "Tableau Sprache",
// Config Page - DMR Configuration
"dmr_master" => "DMR Master (MMDVMHost)",
"bm_master" => "BrandMeister Master",
"bm_network" => "BrandMeister Netzwerk",
"dmr_plus_master" => "DMR+ Master",
"dmr_plus_network" => "DMR+ Netzwerk",
"xlx_master" => "XLX Master",
"xlx_enable" => "XLX Master Aktiv",
"dmr_cc" => "DMR Color Code",
"dmr_embeddedlconly" => "DMR EmbeddedLCOnly",
"dmr_dumptadata" => "DMR DumpTAData",
// Config Page - D-Star Configuration
"dstar_rpt1" => "RPT1 Rufzeichen",
"dstar_rpt2" => "RPT2 Rufzeichen",
"dstar_irc_password" => "ircDDBGateway Passwort",
"dstar_default_ref" => "Standard Reflektor",
"aprs_host" => "APRS Host",
"dstar_irc_lang" => "ircDDBGateway Sprache",
"dstar_irc_time" => "Zeit Ansagen",
// Config Page - YSF Configuration
"ysf_startup_host" => "YSF Startup Host",
// Config Page - P25 Configuration
"p25_startup_host" => "P25 Startup Host",
"p25_nac" => "P25 NAC",
// Config Page - NXDN Configuration
"nxdn_startup_host" => "NXDN Startup Host",
"nxdn_ran" => "NXDN RAN",
// Config Page - M17 Configuration
"m17_startup_host" => "M17 Startup Host",
"m17_can" => "M17 CAN",
// Config Page - MobileGPS Configuration
"mobilegps_enable" => "MobileGPS Enable",
"mobilegps_port" => "GPS Port",
"mobilegps_speed" => "GPS Port Speed",
// Config Page - Firewall Configuration
"fw_dash" => "Tableau Zugriff",
"fw_irc" => "ircDDBGateway Remote",
"fw_ssh" => "SSH Zugriff",
// Config Page - Password
"user" => "Benutzername",
"password" => "Passwort",
"set_password" => "Passwort setzen",
// Dashboard Front Page - Repeater Info Pannel
"modes_enabled" => "Aktive Modi",
"net_status" => "Netzwerk Status",
"internet" => "Internet",
"radio_info" => "Radio Info",
"dstar_repeater" => "D-Star Relais",
"dstar_net" => "D-Star Netzwerk",
"dmr_repeater" => "DMR Relais",
"dmr_master" => "DMR Master",
"ysf_net" => "YSF Netzwerk",
"p25_radio" => "P25 Radio",
"p25_net" => "P25 Netzwerk",
"nxdn_radio" => "NXDN Radio",
"nxdn_net" => "NXDN Netzwerk",
"m17_radio" => "M17 Radio",
"m17_net" => "M17 Network",
// Dashboard Front Page - Calls
"time" => "Zeit",
"mode" => "Mode",
"callsign" => "Rufzeichen",
"target" => "Ziel",
"src" => "Quelle", // Short version of "Source"
"dur" => "Dauer", // Short version of "Duration"
"loss" => "Verlust",
"ber" => "BER", // Short version of "Bit Error Rate"
// POCSAG Specific
"pocsag_list" => "DAPNET Gateway Activity",
"pocsag_timeslot" => "Time Slot",
"pocsag_msg" => "Message",
// Dashboard - Extra Info
"group" => "Gruppe",
"logoff" => "LogOff",
"info" => "Information",
"utot" => "UTOT", // Short for User Timeout
"gtot" => "GTOT", // Short for Group Timeout
// Dashboard Front Page / Admin - Section Headders
"last_heard_list" => "Letzten 20 Rufzeichen, die geh&ouml;rt wurden",
"local_tx_list" => "Letzten 20 Rufzeichen, die dieses Gateway nutzten",
"active_starnet_groups" => "Aktive Starnet Gruppen",
"active_starnet_members" => "Aktive Starnet Gruppen Mitglieder",
"d-star_link_manager" => "D-Star Link Manager",
"d-star_link_status" => "D-Star Link Information",
"service_status" => "Service Status"
);
?>
+157
View File
@@ -0,0 +1,157 @@
<?php
//
// Greek GR Language Pack
// Translated to Greek by Demetre SV1UY, version 9
// Updated: 06-Aug-2017
//
$lang = array (
// Banner texts
"digital_voice" => "Ψηφιακή Τηλεφωνία",
"configuration" => "Ρυθμίσεις",
"dashboard_for" => "Πίνακας Ελέγχου για τον",
// Banner links
"dashboard" => "Πίνακας Ελέγχου",
"admin" => "Διαχείριση",
"power" => "Απενεργοποίηση",
"update" => "Ενημέρωση",
"backup_restore" => "Εφεδρικό Αντίγραφο/Επαναφορά",
"factory_reset" => "Εργοστασιακές Ρυθμίσεις",
"live_logs" => "Αρχείο Καταγραφής",
// Config page section headdings
"hardware_info" => "Πληροφορίες Υλικού Πύλης",
"control_software" => "Πρόγραμμα Ελέγχου",
"mmdvmhost_config" => "Ρυθμίσεις του MMDVMHost",
"general_config" => "Γενικές Ρυθμίσεις",
"dmr_config" => "Ρυθμίσεις DMR",
"dstar_config" => "Ρυθμίσεις D-Star",
"ysf_config" => "Ρυθμίσεις Fusion",
"p25_config" => "Ρυθμίσεις P25",
"nxdn_config" => "Ρυθμίσεις NXDN",
"m17_config" => "M17 Configuration",
"pocsag_config" => "Ρυθμίσεις POCSAG",
"mobilegps_config" => "Mobile GPS Configuration",
"wifi_config" => "Ρυθμίσεις WiFi",
"fw_config" => "Ρυθμίσεις Τείχους Προστασίας",
"remote_access_pw" => "Κωδικός Πρόσβασης",
// Config Page - Section General
"setting" => "Ρυθμίσεις",
"value" => "Τιμή",
"apply" => "Εφαρμογή",
// Config Page - Gateway Hardware Information
"hostname" => "Ονομα Συσκευής",
"kernel" => "Πυρήνας",
"platform" => "Πλατφόρμα",
"cpu_load" => "Φόρτος CPU",
"cpu_temp" => "Θερμοκρασία CPU",
// Config Page - Control Software
"controller_software" => "Πρόγραμμα Ελεγκτή",
"controller_mode" => "Κατάσταση Ελεγκτή",
// Config Page - MMDVMHost Configuration
"dmr_mode" => "Λειτουργία DMR",
"d-star_mode" => "Λειτουργία D-Star",
"ysf_mode" => "Λειτουργία YSF",
"p25_mode" => "Λειτουργία P25",
"nxdn_mode" => "Λειτουργία NXDN",
"m17_mode" => "M17 Mode",
"mmdvm_display" => "Τύπος Οθόνης MMDVM",
"mode_hangtime" => "Χρόνος Επαναφοράς Σάρωσης",
// Config Page - General Configuration
"node_call" => "Διακριτικό Κόμβου",
"dmr_id" => "Ταυτότητα CCS7/DMR",
"radio_freq" => "Συχνότητα",
"lattitude" => "Γεωγραφικό Πλάτος",
"longitude" => "Γεωγραφικό Μήκος",
"town" => "Πόλη",
"country" => "Χώρα",
"url" => "URL",
"radio_type" => "Τύπος Modem",
"node_type" => "Τύπος Κόμβου",
"timezone" => "Ζώνη Ωρας",
"dash_lang" => "Γλώσσα Πίνακα Ελέγχου",
// Config Page - DMR Configuration
"dmr_master" => "Εξυπηρετητής DMR (MMDVMHost)",
"bm_master" => "Εξυπηρετητής BrandMeister",
"bm_network" => "Δίκτυο BrandMeister",
"dmr_plus_master" => "Εξυπηρετητής DMR+",
"dmr_plus_network" => "Δίκτυο DMR+",
"xlx_master" => "Εξυπηρετητής XLX",
"xlx_enable" => "Ενεργοποίηση Εξυπηρετητή XLX",
"dmr_cc" => "Κωδικός Χρώματος DMR",
"dmr_embeddedlconly" => "DMR EmbeddedLCOnly",
"dmr_dumptadata" => "DMR DumpTAData",
// Config Page - D-Star Configuration
"dstar_rpt1" => "Διακριτικό RPT1",
"dstar_rpt2" => "Διακριτικό RPT2",
"dstar_irc_password" => "Κωδικός ircDDBGateway",
"dstar_default_ref" => "Reflector Προεπιλογής",
"aprs_host" => "Κεντρικός Υπολ. APRS",
"dstar_irc_lang" => "Γλώσσα ircDDBGateway",
"dstar_irc_time" => "Αναγγελίες Ωρας",
// Config Page - YSF Configuration
"ysf_startup_host" => "Εξυπηρετητής Εκκίνησης YSF",
// Config Page - P25 Configuration
"p25_startup_host" => "Εξυπηρετητής Εκκίνησης P25",
"p25_nac" => "P25 NAC",
// Config Page - NXDN Configuration
"nxdn_startup_host" => "Εξυπηρετητής Εκκίνησης NXDN",
"nxdn_ran" => "NXDN RAN",
// Config Page - M17 Configuration
"m17_startup_host" => "M17 Startup Host",
"m17_can" => "M17 CAN",
// Config Page - MobileGPS Configuration
"mobilegps_enable" => "MobileGPS Enable",
"mobilegps_port" => "GPS Port",
"mobilegps_speed" => "GPS Port Speed",
// Config Page - Firewall Configuration
"fw_dash" => "Πρόσβαση στον Πίνακα Ελέγχου",
"fw_irc" => "Απομακρυσμένος ircDDBGateway",
"fw_ssh" => "Πρόσβαση SSH",
// Config Page - Password
"user" => "Ονομα Χρήστη",
"password" => "Κωδικός Πρόσβασης",
"set_password" => "Αλλαγή Κωδικού Πρόσβασης",
// Dashboard Front Page - Repeater Info Pannel
"modes_enabled" => "Ενεργές Λειτουργίες",
"net_status" => "Κατάσταση Δικτύου",
"internet" => "Διαδίκτυο",
"radio_info" => "Πληροφ. Ασυρμάτου",
"dstar_repeater" => "Αναμεταδότης D-Star",
"dstar_net" => "Δίκτυο D-Star",
"dmr_repeater" => "Αναμεταδότης DMR",
"dmr_master" => "Εξυπηρετητής DMR",
"ysf_net" => "Δίκτυο YSF",
"p25_radio" => "Ασύρματος P25",
"p25_net" => "Δίκτυο P25",
"nxdn_radio" => "Ασύρματος NXDN",
"nxdn_net" => "Δίκτυο NXDN",
"m17_radio" => "M17 Radio",
"m17_net" => "M17 Network",
// Dashboard Front Page - Calls
"time" => "Ωρα",
"mode" => "Λειτουργία",
"callsign" => "Διακριτικό",
"target" => "Προορισμός",
"src" => "Πηγή", // Short version of "Source"
"dur" => "Διάρκεια", // Short version of "Duration"
"loss" => "Απώλεια",
"ber" => "Σφάλματα", // Short version of "Bit Error Rate"
// POCSAG Specific
"pocsag_list" => "DAPNET Gateway Activity",
"pocsag_timeslot" => "Time Slot",
"pocsag_msg" => "Message",
// Dashboard - Extra Info
"group" => "Ομάδα",
"logoff" => "Αποσύνδεση",
"info" => "Πληροφορίες",
"utot" => "UTOT", // Short for User Timeout
"gtot" => "GTOT", // Short for Group Timeout
// Dashboard Front Page / Admin - Section Headders
"last_heard_list" => "Τελευταία 20 διακριτικά που ακούστηκαν μέσω αυτής της Πύλης",
"local_tx_list" => "Τελευταία 20 διακριτικά που είχαν πρόσβαση σε αυτή την Πύλη",
"active_starnet_groups" => "Ενεργές Ομάδες Starnet",
"active_starnet_members" => "Ενεργά Μέλη Ομάδων Starnet",
"d-star_link_manager" => "Διαχειριστής Ζεύζης D-Star",
"d-star_link_status" => "Πληροφορίες Ζεύξης D-Star",
"service_status" => "Κατάσταση Υπηρεσίας"
);
?>
+157
View File
@@ -0,0 +1,157 @@
<?php
//
// Hungarian HU Language Pack
// Karoly Bencsik (HA8BKG)
// Updated: 07-Mar-2024
//
$lang = array (
// Banner texts
"digital_voice" => "Digitális",
"configuration" => "Konfiguráció",
"dashboard_for" => "Irányítópult",
// Banner links
"dashboard" => "Irányítópult",
"admin" => "Admin",
"power" => "Tápellátás",
"update" => "Frissítés",
"backup_restore" => "Mentés/Visszaállítás",
"factory_reset" => "Gyári beállítás",
"live_logs" => "Élő forgalom",
// Config page section headdings
"hardware_info" => "Hardver információ",
"control_software" => "Vezérlő szoftver",
"mmdvmhost_config" => "MMDVMHost konfiguráció",
"general_config" => "Általános konfiguráció",
"dmr_config" => "DMR konfiguráció",
"dstar_config" => "D-Star konfiguráció",
"ysf_config" => "Yaesu System Fusion konfiguráció",
"p25_config" => "P25 konfiguráció",
"nxdn_config" => "NXDN konfiguráció",
"m17_config" => "M17 konfiguráció",
"pocsag_config" => "POCSAG konfiguráció",
"mobilegps_config" => "Mobil GPS konfiguráció",
"wifi_config" => "Wi-fi konfiguráció",
"fw_config" => "Tűzfal konfiguráció",
"remote_access_pw" => "Távoli hozzáférés jelszó",
// Config Page - Section General
"setting" => "Beállítás",
"value" => "Érték",
"apply" => "Alkalmaz",
// Config Page - Gateway Hardware Information
"hostname" => "Kiszolgáló neve",
"kernel" => "Kernel",
"platform" => "Rendszer",
"cpu_load" => "CPU Terhelés",
"cpu_temp" => "CPU Hőmérséklet",
// Config Page - Control Software
"controller_software" => "Vezérlő Szoftver",
"controller_mode" => "Mód",
// Config Page - MMDVMHost Configuration
"dmr_mode" => "DMR Mód",
"d-star_mode" => "D-Star Mód",
"ysf_mode" => "YSF Mód",
"p25_mode" => "P25 Mód",
"nxdn_mode" => "NXDN Mód",
"m17_mode" => "M17 Mód",
"mmdvm_display" => "MMDVM kijelző típusa",
"mode_hangtime" => "Mód tartási ideje",
// Config Page - General Configuration
"node_call" => "Hívójel",
"dmr_id" => "CCS7/DMR ID",
"radio_freq" => "Frekvencia",
"lattitude" => "Szélességi kör",
"longitude" => "Hosszúsági kör",
"town" => "Város",
"country" => "Ország",
"url" => "URL",
"radio_type" => "Rádió/Modem típusa",
"node_type" => "Hozzáférési pont típusa",
"timezone" => "Időzóna",
"dash_lang" => "Irányítópult nyelve",
// Config Page - DMR Configuration
"dmr_master" => "DMR Szerver (MMDVMHost)",
"bm_master" => "BrandMeister Szerver",
"bm_network" => "BrandMeister Hálózat",
"dmr_plus_master" => "DMR+ Szerver",
"dmr_plus_network" => "DMR+ Hálózat",
"xlx_master" => "XLX Szerver",
"xlx_enable" => "XLX Szerver engedélyezése",
"dmr_cc" => "DMR Color kód",
"dmr_embeddedlconly" => "DMR GPS/Talker Alias kikapcsolása",
"dmr_dumptadata" => "DMR GPS/Talker Alias mentése logba",
// Config Page - D-Star Configuration
"dstar_rpt1" => "RPT1 Hívójel",
"dstar_rpt2" => "RPT2 Hívójel",
"dstar_irc_password" => "Jelszó",
"dstar_default_ref" => "Alapértelmezett reflektor",
"aprs_host" => "APRS",
"dstar_irc_lang" => "ircDDBGateway Nyelv",
"dstar_irc_time" => "Idő hirdetmények",
// Config Page - YSF Configuration
"ysf_startup_host" => "YSF kiszolgáló",
// Config Page - P25 Configuration
"p25_startup_host" => "P25 kiszolgáló",
"p25_nac" => "P25 NAC",
// Config Page - NXDN Configuration
"nxdn_startup_host" => "NXDN kiszolgáló",
"nxdn_ran" => "NXDN RAN",
// Config Page - M17 Configuration
"m17_startup_host" => "M17 kiszolgáló",
"m17_can" => "M17 CAN",
// Config Page - MobileGPS Configuration
"mobilegps_enable" => "Mobil GPS",
"mobilegps_port" => "GPS Port",
"mobilegps_speed" => "GPS Port Sebessége",
// Config Page - Firewall Configuration
"fw_dash" => "Irányítópult hozzáférés",
"fw_irc" => "ircDDBGateway távoli hozzáférés",
"fw_ssh" => "SSH hozzáférés",
// Config Page - Password
"user" => "Felhasználónév",
"password" => "Jelszó",
"set_password" => "Beállítás",
// Dashboard Front Page - Repeater Info Pannel
"modes_enabled" => "Bekapcsolt módok",
"net_status" => "Hálózat státusza",
"internet" => "Internet",
"radio_info" => "Rádió infó",
"dstar_repeater" => "D-Star átjátszó",
"dstar_net" => "D-Star hálózat",
"dmr_repeater" => "DMR átjátszó",
"dmr_master" => "DMR hálózat",
"ysf_net" => "YSF hálózat",
"p25_radio" => "P25 rádió",
"p25_net" => "P25 hálózat",
"nxdn_radio" => "NXDN rádió",
"nxdn_net" => "NXDN hálózat",
"m17_radio" => "M17 rádió",
"m17_net" => "M17 hálózat",
// Dashboard Front Page - Calls
"time" => "Idő",
"mode" => "Mód",
"callsign" => "Hívójel",
"target" => "Cél",
"src" => "Forrás", // Short version of "Source"
"dur" => "Hossz", // Short version of "Duration"
"loss" => "Veszteség",
"ber" => "BER", // Short version of "Bit Error Rate"
// POCSAG Specific
"pocsag_list" => "DAPNET aktivitás",
"pocsag_timeslot" => "Időrés",
"pocsag_msg" => "Üzenet",
// Dashboard - Extra Info
"group" => "Csoport",
"logoff" => "LogOff",
"info" => "Információ",
"utot" => "FIT", // Short for User Timeout
"gtot" => "CSIT", // Short for Group Timeout
// Dashboard Front Page / Admin - Section Headders
"last_heard_list" => "Átjáró aktivitás",
"local_tx_list" => "Helyi RF aktivitás",
"active_starnet_groups" => "Aktív Starnet csoport",
"active_starnet_members" => "Aktív Starnet csoport résztvevők",
"d-star_link_manager" => "D-Star Link Manager",
"d-star_link_status" => "D-Star Link Információ",
"service_status" => "Szolgáltatás állapota"
);
?>
+1
View File
@@ -0,0 +1 @@
+157
View File
@@ -0,0 +1,157 @@
<?php
//
// Italian IT Language Pack
// Luca Gheza
// Updated: 26-Aug-2017
//
$lang = array (
// Banner texts
"digital_voice" => "Modi Digitali",
"configuration" => "Configurazioni",
"dashboard_for" => "Cruscotto",
// Banner links
"dashboard" => "Cruscotto",
"admin" => "Admin",
"power" => "Power",
"update" => "Aggiornamento",
"backup_restore" => "Backup/Restore",
"factory_reset" => "Ripristino",
"live_logs" => "Live Logs",
// Config page section headdings
"hardware_info" => "Informazioni Hardware del Gateway",
"control_software" => "Software di controllo",
"mmdvmhost_config" => "MMDVMHost Config",
"general_config" => "Configurazione Generale",
"dmr_config" => "DMR Config",
"dstar_config" => "D-Star Configuration",
"ysf_config" => "Yaesu System Fusion Config",
"p25_config" => "P25 Config",
"nxdn_config" => "NXDN Config",
"m17_config" => "M17 Configuration",
"pocsag_config" => "POCSAG Config",
"mobilegps_config" => "Mobile GPS Configuration",
"wifi_config" => "Wireless Config",
"fw_config" => "Firewall Config",
"remote_access_pw" => "Password Accesso Remoto",
// Config Page - Section General
"setting" => "Settaggi",
"value" => "Valori",
"apply" => "Applicare le modifiche",
// Config Page - Gateway Hardware Information
"hostname" => "Hostname",
"kernel" => "Kernel",
"platform" => "Platform",
"cpu_load" => "CPU Load",
"cpu_temp" => "CPU Temp",
// Config Page - Control Software
"controller_software" => "Controller Software",
"controller_mode" => "Controller Mode",
// Config Page - MMDVMHost Configuration
"dmr_mode" => "DMR Mode",
"d-star_mode" => "D-Star Mode",
"ysf_mode" => "YSF Mode",
"p25_mode" => "P25 Mode",
"nxdn_mode" => "NXDN Mode",
"m17_mode" => "M17 Mode",
"mmdvm_display" => "MMDVM Display Type",
"mode_hangtime" => "Tempo Ritardo",
// Config Page - General Configuration
"node_call" => "Nominativo",
"dmr_id" => "CCS7/DMR ID",
"radio_freq" => "Frequenza Radio",
"lattitude" => "Latitudine",
"longitude" => "Longitudine",
"town" => "Città",
"country" => "Stato",
"url" => "URL",
"radio_type" => "Radio/Modem Modello",
"node_type" => "Tipo di Nodo",
"timezone" => "Fuso Orario",
"dash_lang" => "Linguaggio Cruscotto",
// Config Page - DMR Configuration
"dmr_master" => "DMR Master (MMDVMHost)",
"bm_master" => "BrandMeister Master",
"bm_network" => "BrandMeister Network",
"dmr_plus_master" => "DMR+ Master",
"dmr_plus_network" => "DMR+ Network",
"xlx_master" => "XLX Master",
"xlx_enable" => "XLX Master Attivo",
"dmr_cc" => "DMR Colour Code",
"dmr_embeddedlconly" => "DMR EmbeddedLCOnly",
"dmr_dumptadata" => "DMR DumpTAData",
// Config Page - D-Star Configuration
"dstar_rpt1" => "Nominativo RPT1",
"dstar_rpt2" => "Nominativo RPT2",
"dstar_irc_password" => "ircDDBGateway Password",
"dstar_default_ref" => "Default Reflector",
"aprs_host" => "APRS Host",
"dstar_irc_lang" => "ircDDBGateway Linguaggio",
"dstar_irc_time" => "Annuncio (Vocale) Orario",
// Config Page - YSF Configuration
"ysf_startup_host" => "Room YSF Di Partenza",
// Config Page - P25 Configuration
"p25_startup_host" => "Room P25 Di Partenza",
"p25_nac" => "P25 NAC",
// Config Page - NXDN Configuration
"nxdn_startup_host" => "Room NXDN Di Partenza",
"nxdn_ran" => "NXDN RAN",
// Config Page - M17 Configuration
"m17_startup_host" => "M17 Startup Host",
"m17_can" => "M17 CAN",
// Config Page - MobileGPS Configuration
"mobilegps_enable" => "MobileGPS Enable",
"mobilegps_port" => "GPS Port",
"mobilegps_speed" => "GPS Port Speed",
// Config Page - Firewall Configuration
"fw_dash" => "Accesso Cruscotto",
"fw_irc" => "ircDDBGateway Remote",
"fw_ssh" => "Accesso SSH",
// Config Page - Password
"user" => "Nome Utente",
"password" => "Password",
"set_password" => "Imposta Password",
// Dashboard Front Page - Repeater Info Pannel
"modes_enabled" => "Modi Attivi",
"net_status" => "Stato Rete",
"internet" => "Internet",
"radio_info" => "Info Radio",
"dstar_repeater" => "D-Star Repeater",
"dstar_net" => "D-Star Network",
"dmr_repeater" => "DMR Repeater",
"dmr_master" => "DMR Master",
"ysf_net" => "YSF Network",
"p25_radio" => "P25 Radio",
"p25_net" => "P25 Network",
"nxdn_radio" => "NXDN Radio",
"nxdn_net" => "NXDN Network",
"m17_radio" => "M17 Radio",
"m17_net" => "M17 Network",
// Dashboard Front Page - Calls
"time" => "Ora",
"mode" => "Modo",
"callsign" => "Nominativo",
"target" => "Target",
"src" => "Src", // Short version of "Source"
"dur" => "Dur", // Short version of "Duration"
"loss" => "Loss",
"ber" => "BER", // Short version of "Bit Error Rate"
// POCSAG Specific
"pocsag_list" => "DAPNET Gateway Activity",
"pocsag_timeslot" => "Time Slot",
"pocsag_msg" => "Message",
// Dashboard - Extra Info
"group" => "Gruppo",
"logoff" => "LogOff",
"info" => "Informazioni",
"utot" => "UTOT", // Short for User Timeout
"gtot" => "GTOT", // Short for Group Timeout
// Dashboard Front Page / Admin - Section Headders
"last_heard_list" => "Le Ultime 20 Trasmissioni Di Questo Nodo",
"local_tx_list" => "Gli Ultimi 20 Accessi RF Su Questo Nodo",
"active_starnet_groups" => "Gruppi Attivi Starnet",
"active_starnet_members" => "Menbri Attivi Nel Gruppo Starnet",
"d-star_link_manager" => "Gestione D-Star Link",
"d-star_link_status" => "Info D-Star Link",
"service_status" => "Stato Servizi"
);
?>
+159
View File
@@ -0,0 +1,159 @@
<?php
//
// Japanese JP Language Pack
// Toshiro Okano (JF1CXH)
// Updated: 09-Sep-2017
// Masaru Mitani (JE4SMQ)
// Updated: 10-Jan-2021
//
$lang = array (
// Banner texts
"digital_voice" => "デジタルボイス",
"configuration" => "設定",
"dashboard_for" => "ダッシュボード for",
// Banner links
"dashboard" => "ダッシュボード",
"admin" => "制御",
"power" => "電源",
"update" => "更新",
"backup_restore" => "バックアップ/復旧",
"factory_reset" => "工場出荷に戻る",
"live_logs" => "ログ",
// Config page section headdings
"hardware_info" => "ゲートウェイハード情報",
"control_software" => "制御ソフトウェア",
"mmdvmhost_config" => "MMDVMHost設定",
"general_config" => "全般設定",
"dmr_config" => "DMR設定",
"dstar_config" => "D-Star設定",
"ysf_config" => "YSF設定",
"p25_config" => "P25設定",
"nxdn_config" => "NXDN設定",
"m17_config" => "M17 Configuration",
"pocsag_config" => "POCSAG設定",
"mobilegps_config" => "外部GPS設定",
"wifi_config" => "無線LAN設定",
"fw_config" => "ファイアウォール設定",
"remote_access_pw" => "遠隔ログインパスワード",
// Config Page - Section General
"setting" => "設定項目",
"value" => "設定内容",
"apply" => "更新",
// Config Page - Gateway Hardware Information
"hostname" => "ホスト名",
"kernel" => "カーネル",
"platform" => "マイコン",
"cpu_load" => "CPU負荷",
"cpu_temp" => "CPU温度",
// Config Page - Control Software
"controller_software" => "制御ソフト",
"controller_mode" => "制御モード",
// Config Page - MMDVMHost Configuration
"dmr_mode" => "DMRモード",
"d-star_mode" => "D-Starモード",
"ysf_mode" => "YSFモード",
"p25_mode" => "P25モード",
"nxdn_mode" => "NXDNモード",
"m17_mode" => "M17 Mode",
"mmdvm_display" => "MMDVMディスプレイタイプ",
"mode_hangtime" => "モード保持時間",
// Config Page - General Configuration
"node_call" => "ノード コールサイン",
"dmr_id" => "CCS7/DMR ID",
"radio_freq" => "周波数",
"lattitude" => "緯度",
"longitude" => "経度",
"town" => "市町村",
"country" => "",
"url" => "URL",
"radio_type" => "Radio/Modem タイプ",
"node_type" => "ノードタイプ",
"timezone" => "タイムゾーン",
"dash_lang" => "言語",
// Config Page - DMR Configuration
"dmr_master" => "DMRマスター(MMDVMHost)",
"bm_master" => "BrandMeisterマスター",
"bm_network" => "BrandMeisterネットワーク",
"dmr_plus_master" => "DMR+マスター",
"dmr_plus_network" => "DMR+ネットワーク",
"xlx_master" => "XLXマスター",
"xlx_enable" => "XLXマスター有効",
"dmr_cc" => "DMRカラーコード",
"dmr_embeddedlconly" => "DMR EmbeddedLCOnly",
"dmr_dumptadata" => "DMR DumpTAData",
// Config Page - D-Star Configuration
"dstar_rpt1" => "RPT1コールサイン",
"dstar_rpt2" => "RPT2コールサイン",
"dstar_irc_password" => "ircDDBGatewayパスワード",
"dstar_default_ref" => "初期接続リフレクター",
"aprs_host" => "APRSホスト",
"dstar_irc_lang" => "ircDDBGateway言語",
"dstar_irc_time" => "時刻アナウンス",
// Config Page - YSF Configuration
"ysf_startup_host" => "YSF初期接続ホスト",
// Config Page - P25 Configuration
"p25_startup_host" => "P25初期接続ホスト",
"p25_nac" => "P25 NAC",
// Config Page - NXDN Configuration
"nxdn_startup_host" => "NXDN初期接続ホスト",
"nxdn_ran" => "NXDN RAN",
// Config Page - M17 Configuration
"m17_startup_host" => "M17 Startup Host",
"m17_can" => "M17 CAN",
// Config Page - MobileGPS Configuration
"mobilegps_enable" => "外部GPS有効",
"mobilegps_port" => "GPSポート",
"mobilegps_speed" => "GPSポート速度",
// Config Page - Firewall Configuration
"fw_dash" => "ダッシュボードアクセス",
"fw_irc" => "ircDDBGatewayリモート",
"fw_ssh" => "SSHアクセス",
// Config Page - Password
"user" => "ユーザー名",
"password" => "パスワード",
"set_password" => "パスワード保存",
// Dashboard Front Page - Repeater Info Pannel
"modes_enabled" => "使用可能モード",
"net_status" => "ネットワーク状況",
"internet" => "インターネット",
"radio_info" => "無線モデム情報",
"dstar_repeater" => "D-Starレピーター",
"dstar_net" => "D-Starネットワーク",
"dmr_repeater" => "DMRレピーター",
"dmr_master" => "DMRマスター",
"ysf_net" => "YSFネットワーク",
"p25_radio" => "P25 Radio",
"p25_net" => "P25ネットワーク",
"nxdn_radio" => "NXDN Radio",
"nxdn_net" => "NXDNネットワーク",
"m17_radio" => "M17 Radio",
"m17_net" => "M17 Network",
// Dashboard Front Page - Calls
"time" => "時刻",
"mode" => "モード",
"callsign" => "コールサイン",
"target" => "ターゲット",
"src" => "ソース", // Short version of "Source"
"dur" => "時間", // Short version of "Duration"
"loss" => "ロス",
"ber" => "BER", // Short version of "Bit Error Rate"
// POCSAG Specific
"pocsag_list" => "DAPNETゲートウェイ状態",
"pocsag_timeslot" => "タイムスロット",
"pocsag_msg" => "メッセージ",
// Dashboard - Extra Info
"group" => "グループ",
"logoff" => "ログオフ",
"info" => "情報",
"utot" => "UTOT", // Short for User Timeout
"gtot" => "GTOT", // Short for Group Timeout
// Dashboard Front Page / Admin - Section Headders
"last_heard_list" => "受信リスト(最新20局)",
"local_tx_list" => "送信リスト(最新20局)",
"active_starnet_groups" => "Active Starnet Groups",
"active_starnet_members" => "Active Starnet Group Members",
"d-star_link_manager" => "D-Star接続マネージャー",
"d-star_link_status" => "D-Star接続情報",
"service_status" => "サービス状態"
);
?>
+157
View File
@@ -0,0 +1,157 @@
<?php
//
// Norwegian NO Language Pack
// Translated to Norwegian by LB4GH, Torkell Opedal Wullum, lb4gh@lb4gh.no
// Updated: 02-Aug-2017
//
$lang = array (
// Banner texts
"digital_voice" => "Digital Voice",
"configuration" => "Konfigurasjon",
"dashboard_for" => "Skrivebord for",
// Banner links
"dashboard" => "Skrivebord",
"admin" => "Admin",
"power" => "Strøm",
"update" => "Oppdater",
"backup_restore" => "Backup/Restore",
"factory_reset" => "Tilbakestill",
"live_logs" => "Live Logg",
// Config page section headdings
"hardware_info" => "Gateway Hardware Informasjon",
"control_software" => "Kontroller Programvare",
"mmdvmhost_config" => "MMDVMHost Konfigurasjon",
"general_config" => "Generel Konfigurasjon",
"dmr_config" => "DMR Konfigurasjon",
"dstar_config" => "D-Star Konfigurasjon",
"ysf_config" => "Yaesu System Fusion Konfigurasjon",
"p25_config" => "P25 Konfigurasjon",
"nxdn_config" => "NXDN Konfigurasjon",
"m17_config" => "M17 Configuration",
"pocsag_config" => "POCSAG Konfigurasjon",
"mobilegps_config" => "Mobile GPS Configuration",
"wifi_config" => "Trådløs Konfigurasjon",
"fw_config" => "Firewall Konfigurasjon",
"remote_access_pw" => "Fjerntilgangs passord",
// Config Page - Section General
"setting" => "Innstillinger",
"value" => "Verdi",
"apply" => "Bruk endringer",
// Config Page - Gateway Hardware Information
"hostname" => "Vertsnavn",
"kernel" => "Kernel",
"platform" => "Platform",
"cpu_load" => "CPU-belastning",
"cpu_temp" => "CPU Temp",
// Config Page - Control Software
"controller_software" => "Kontroller Programvare",
"controller_mode" => "Kontroller Mode",
// Config Page - MMDVMHost Configuration
"dmr_mode" => "DMR Mode",
"d-star_mode" => "D-Star Mode",
"ysf_mode" => "YSF Mode",
"p25_mode" => "P25 Mode",
"nxdn_mode" => "NXDN Mode",
"m17_mode" => "M17 Mode",
"mmdvm_display" => "MMDVM Skjerm Type",
"mode_hangtime" => "Mode Hangtime",
// Config Page - General Configuration
"node_call" => "Node Kallesignal",
"dmr_id" => "CCS7/DMR ID",
"radio_freq" => "Radio Frekvens",
"lattitude" => "Breddegrad",
"longitude" => "lengdegrad",
"town" => "By",
"country" => "Land",
"url" => "URL",
"radio_type" => "Radio/Modem Type",
"node_type" => "Node Type",
"timezone" => "Tidssone",
"dash_lang" => "Skrivebord Språk",
// Config Page - DMR Configuration
"dmr_master" => "DMR Master (MMDVMHost)",
"bm_master" => "BrandMeister Master",
"bm_network" => "BrandMeister Nettverk",
"dmr_plus_master" => "DMR+ Master",
"dmr_plus_network" => "DMR+ Nettverk",
"xlx_master" => "XLX Master",
"xlx_enable" => "XLX Master Aktivert",
"dmr_cc" => "DMR Farge Kode",
"dmr_embeddedlconly" => "DMR EmbeddedLCOnly",
"dmr_dumptadata" => "DMR DumpTAData",
// Config Page - D-Star Configuration
"dstar_rpt1" => "RPT1 Kallesignal",
"dstar_rpt2" => "RPT2 Kallesignal",
"dstar_irc_password" => "ircDDBGateway Passord",
"dstar_default_ref" => "Standard Reflektor",
"aprs_host" => "APRS Vert",
"dstar_irc_lang" => "ircDDBGateway Språk",
"dstar_irc_time" => "Tids annonsering",
// Config Page - YSF Configuration
"ysf_startup_host" => "YSF Oppstarts Vert",
// Config Page - P25 Configuration
"p25_startup_host" => "P25 Oppstarts Vert",
"p25_nac" => "P25 NAC",
// Config Page - NXDN Configuration
"nxdn_startup_host" => "NXDN Oppstarts Vert",
"nxdn_ran" => "NXDN RAN",
// Config Page - M17 Configuration
"m17_startup_host" => "M17 Startup Host",
"m17_can" => "M17 CAN",
// Config Page - MobileGPS Configuration
"mobilegps_enable" => "MobileGPS Enable",
"mobilegps_port" => "GPS Port",
"mobilegps_speed" => "GPS Port Speed",
// Config Page - Firewall Configuration
"fw_dash" => "Skrivebord Adgang",
"fw_irc" => "ircDDBGateway Remote",
"fw_ssh" => "SSH Adgang",
// Config Page - Password
"user" => "Bruker navn",
"password" => "Passord",
"set_password" => "Set Passord",
// Dashboard Front Page - Repeater Info Pannel
"modes_enabled" => "Modes Aktivert",
"net_status" => "Nettverk Status",
"internet" => "Internett",
"radio_info" => "Radio Info",
"dstar_repeater" => "D-Star Repeater",
"dstar_net" => "D-Star Nettverk",
"dmr_repeater" => "DMR Repeater",
"dmr_master" => "DMR Master",
"ysf_net" => "YSF Nettverk",
"p25_radio" => "P25 Radio",
"p25_net" => "P25 Nettverk",
"nxdn_radio" => "NXDN Radio",
"nxdn_net" => "NXDN Nettverk",
"m17_radio" => "M17 Radio",
"m17_net" => "M17 Network",
// Dashboard Front Page - Calls
"time" => "Tid",
"mode" => "Mode",
"callsign" => "Kallesignal",
"target" => "Destinasjon",
"src" => "Src", // Short version of "Source"
"dur" => "Var", // Short version of "Duration"
"loss" => "Loss",
"ber" => "BER", // Short version of "Bit Error Rate"
// POCSAG Specific
"pocsag_list" => "DAPNET Gateway Activity",
"pocsag_timeslot" => "Time Slot",
"pocsag_msg" => "Message",
// Dashboard - Extra Info
"group" => "Gruppe",
"logoff" => "Logg Av",
"info" => "Informasjon",
"utot" => "UTOT", // Short for User Timeout
"gtot" => "GTOT", // Short for Group Timeout
// Dashboard Front Page / Admin - Section Headders
"last_heard_list" => "De siste 20 anropene hørt via denne Gatewayen",
"local_tx_list" => "De siste 20 anropene som åpnet denne Gatewayen",
"active_starnet_groups" => "Aktive Starnet Grupper",
"active_starnet_members" => "Aktive Starnet Gruppe medlemer",
"d-star_link_manager" => "D-Star Link Manager",
"d-star_link_status" => "D-Star Link Informasjon",
"service_status" => "Service Status"
);
?>
+157
View File
@@ -0,0 +1,157 @@
<?php
//
// Polish PL Language Pack
// Translated 02-Sep-2022 from English Language by Pawel SP8MDH
// Edited: 04-Sep-2022
//
$lang = array (
// Banner texts
"digital_voice" => "Tryb cyfrowy",
"configuration" => "Konfiguracja",
"dashboard_for" => "Panel użytkownika",
// Banner links
"dashboard" => "Panel",
"admin" => "Zarządzanie",
"power" => "Zasilanie",
"update" => "Aktualizacja",
"backup_restore" => "Kopia/Przywracanie",
"factory_reset" => "Resetowanie",
"live_logs" => "Logi na żywo",
// Config page section headdings
"hardware_info" => "Informacje sprzętowe bramki",
"control_software" => "Oprogramowanie sterujące",
"mmdvmhost_config" => "Konfiguracja MMDVMHost",
"general_config" => "Konfiguracja główna",
"dmr_config" => "Konfiguracja DMR",
"dstar_config" => "Konfiguracja D-Star",
"ysf_config" => "Konfiguracja Yaesu System Fusion",
"p25_config" => "Konfiguracja P25",
"nxdn_config" => "Konfiguracja NXDN",
"m17_config" => "M17 Configuration",
"pocsag_config" => "Konfiguracja POCSAG",
"mobilegps_config" => "Konfiguracja przenośnego GPS",
"wifi_config" => "Konfiguracja WiFi",
"fw_config" => "Konfiguracja zapory sieciowej",
"remote_access_pw" => "Hasło dostępu zdalnego",
// Config Page - Section General
"setting" => "Ustawienie",
"value" => "Wartość",
"apply" => "Zatwierdź zmiany",
// Config Page - Gateway Hardware Information
"hostname" => "Nazwa hosta",
"kernel" => "Kernel",
"platform" => "Platforma",
"cpu_load" => "Obciążenie CPU",
"cpu_temp" => "Temperatura CPU",
// Config Page - Control Software
"controller_software" => "Oprogramowanie kontrolera",
"controller_mode" => "Tryb kontrolera",
// Config Page - MMDVMHost Configuration
"dmr_mode" => "Tryb DMR",
"d-star_mode" => "Tryb D-Star",
"ysf_mode" => "Tryb YSF",
"p25_mode" => "Tryb P25",
"nxdn_mode" => "Tryb NXDN",
"m17_mode" => "M17 Mode",
"mmdvm_display" => "Typ wyświetlacza MMDVM",
"mode_hangtime" => "Tryb czasu oczekiwania",
// Config Page - General Configuration
"node_call" => "Znak krótkofalarski bramki",
"dmr_id" => "CCS7/DMR ID",
"radio_freq" => "Częstotliwość radia",
"lattitude" => "Szerokość geograficzna",
"longitude" => "Długość geograficzna",
"town" => "Miejscowość",
"country" => "Państwo",
"url" => "URL",
"radio_type" => "Typ radia/modemu",
"node_type" => "Rodzaj bramki",
"timezone" => "Strefa czasowa",
"dash_lang" => "Język panelu",
// Config Page - DMR Configuration
"dmr_master" => "DMR Master (MMDVMHost)",
"bm_master" => "BrandMeister Master",
"bm_network" => "Sieć BrandMeister",
"dmr_plus_master" => "DMR+ Master",
"dmr_plus_network" => "Sieć DMR+",
"xlx_master" => "XLX Master",
"xlx_enable" => "Włączony XLX Master",
"dmr_cc" => "DMR Colour Code",
"dmr_embeddedlconly" => "DMR EmbeddedLCOnly",
"dmr_dumptadata" => "DMR DumpTAData",
// Config Page - D-Star Configuration
"dstar_rpt1" => "Znak RPT1",
"dstar_rpt2" => "Znak RPT2",
"dstar_irc_password" => "Hasło dostępu zdalnego",
"dstar_default_ref" => "Domyślny reflektor",
"aprs_host" => "Host APRS",
"dstar_irc_lang" => "Język ircDDBGateway",
"dstar_irc_time" => "Czas zapowiedzi",
// Config Page - YSF Configuration
"ysf_startup_host" => "Startowy host YSF",
// Config Page - P25 Configuration
"p25_startup_host" => "Startowy host P25",
"p25_nac" => "P25 NAC",
// Config Page - NXDN Configuration
"nxdn_startup_host" => "Startowy host NXDN",
"nxdn_ran" => "NXDN RAN",
// Config Page - M17 Configuration
"m17_startup_host" => "M17 Startup Host",
"m17_can" => "M17 CAN",
// Config Page - MobileGPS Configuration
"mobilegps_enable" => "Włączanie przenośnego GPS",
"mobilegps_port" => "Port GPS",
"mobilegps_speed" => "Prędkość portu GPS",
// Config Page - Firewall Configuration
"fw_dash" => "Dostęp do panelu",
"fw_irc" => "Zdalny ircDDBGateway",
"fw_ssh" => "Dostęp SSH",
// Config Page - Password
"user" => "Nazwa użytkownika",
"password" => "Hasło",
"set_password" => "Ustaw hasło",
// Dashboard Front Page - Repeater Info Pannel
"modes_enabled" => "Włączone tryby",
"net_status" => "Status sieci",
"internet" => "Internet",
"radio_info" => "Radio Info",
"dstar_repeater" => "Przemiennik D-Star",
"dstar_net" => "Sieć D-Star",
"dmr_repeater" => "Przemiennik DMR",
"dmr_master" => "DMR Master",
"ysf_net" => "Sieć YSF",
"p25_radio" => "Radio P25",
"p25_net" => "Sieć P25",
"nxdn_radio" => "Radio NXDN",
"nxdn_net" => "Sieć NXDN",
"m17_radio" => "M17 Radio",
"m17_net" => "M17 Network",
// Dashboard Front Page - Calls
"time" => "Czas",
"mode" => "Tryb",
"callsign" => "Znak",
"target" => "Cel",
"src" => "Źródło",
"dur" => "Okres",
"loss" => "Straty",
"ber" => "BER", // Short version of "Bit Error Rate"
// POCSAG Specific
"pocsag_list" => "Aktywność bramy DAPNET",
"pocsag_timeslot" => "Szczelina czasowa",
"pocsag_msg" => "Wiadomość",
// Dashboard - Extra Info
"group" => "Grupa",
"logoff" => "Wyloguj",
"info" => "Informacja",
"utot" => "UTOT", // Short for User Timeout
"gtot" => "GTOT", // Short for Group Timeout
// Dashboard Front Page / Admin - Section Headders
"last_heard_list" => "Aktywność bramki",
"local_tx_list" => "Atywność radiowa lokalna",
"active_starnet_groups" => "Aktywne grupy Starnet",
"active_starnet_members" => "Aktywni członkowie grupy Starnet",
"d-star_link_manager" => "Zarządzanie linkami D-Star",
"d-star_link_status" => "Status linków D-Star",
"service_status" => "Status serwisu"
);
?>
+157
View File
@@ -0,0 +1,157 @@
<?php
//
// Portuguese BR Language Pack
// Rubens H. Zolotujin (PU2LRZ)
// Updated: 30-Aug-2017
//
$lang = array (
// Banner texts
"digital_voice" => "Digital Voz",
"configuration" => "Configuração",
"dashboard_for" => "Painel de Controle de",
// Banner links
"dashboard" => "Painel",
"admin" => "Admin",
"power" => "Ação",
"update" => "Atualizar",
"backup_restore" => "Backup/Restaurar",
"factory_reset" => "Reset",
"live_logs" => "Logs online",
// Config page section headdings
"hardware_info" => "Informações Hardware do Gateway",
"control_software" => "Controle Software",
"mmdvmhost_config" => "MMDVMHost Configuração",
"general_config" => "Configurações Gerais",
"dmr_config" => "Configuração do DMR",
"dstar_config" => "Configuração do D-Star",
"ysf_config" => "Configuração do Sistema Yaesu Fusion",
"p25_config" => "Configuração do P25",
"nxdn_config" => "Configuração do NXDN",
"m17_config" => "M17 Configuration",
"pocsag_config" => "Configuração do POCSAG",
"mobilegps_config" => "Mobile GPS Configuration",
"wifi_config" => "Configuração do Wireless",
"fw_config" => "Configuração do Firewall",
"remote_access_pw" => "Senha do Acesso Remoto",
// Config Page - Section General
"setting" => "Configurações",
"value" => "Valores",
"apply" => "Aplicar",
// Config Page - Gateway Hardware Information
"hostname" => "Nome do Host",
"kernel" => "Kernel",
"platform" => "Plataforma",
"cpu_load" => "Leitura de CPU",
"cpu_temp" => "Temperatura de CPU",
// Config Page - Control Software
"controller_software" => "Tipo de Software",
"controller_mode" => "Modo de Controle",
// Config Page - MMDVMHost Configuration
"dmr_mode" => "Modo DMR",
"d-star_mode" => "Modo D-Star",
"ysf_mode" => "Modo YSF",
"p25_mode" => "Modo P25",
"nxdn_mode" => "Modo NXDN",
"m17_mode" => "M17 Mode",
"mmdvm_display" => "Exibição MMDVM",
"mode_hangtime" => "Modo Tempo de Espera",
// Config Page - General Configuration
"node_call" => "Indicativo do Node",
"dmr_id" => "CCS7/DMR ID",
"radio_freq" => "Frequencia do Rádio",
"lattitude" => "Latitude",
"longitude" => "Longitude",
"town" => "Cidade",
"country" => "País",
"url" => "URL",
"radio_type" => "Tipo de Radio/Modem",
"node_type" => "Tipo de Node",
"timezone" => "Horário do Sistema",
"dash_lang" => "Idioma do Painel de Controle",
// Config Page - DMR Configuration
"dmr_master" => "DMR Master (MMDVMHost)",
"bm_master" => "BrandMeister Master",
"bm_network" => "BrandMeister Rede",
"dmr_plus_master" => "DMR+ Master",
"dmr_plus_network" => "DMR+ Network",
"xlx_master" => "XLX Master",
"xlx_enable" => "XLX Master Enable",
"dmr_cc" => "Código de Cores DMR",
"dmr_embeddedlconly" => "DMR EmbeddedLCOnly",
"dmr_dumptadata" => "DMR DumpTAData",
// Config Page - D-Star Configuration
"dstar_rpt1" => "RPT1 Indicativo",
"dstar_rpt2" => "RPT2 Indicativo",
"dstar_irc_password" => "Senha ircDDBGateway",
"dstar_default_ref" => "Refletor Principal",
"aprs_host" => "Servidor APRS",
"dstar_irc_lang" => "Idioma ircDDBGateway",
"dstar_irc_time" => "Anúncio a cada Hora",
// Config Page - YSF Configuration
"ysf_startup_host" => "Servidor Inicial YSF",
// Config Page - P25 Configuration
"p25_startup_host" => "Servidor Inicial P25",
"p25_nac" => "P25 NAC",
// Config Page - NXDN Configuration
"nxdn_startup_host" => "Servidor Inicial NXDN",
"nxdn_ran" => "NXDN RAN",
// Config Page - M17 Configuration
"m17_startup_host" => "M17 Startup Host",
"m17_can" => "M17 CAN",
// Config Page - MobileGPS Configuration
"mobilegps_enable" => "MobileGPS Enable",
"mobilegps_port" => "GPS Port",
"mobilegps_speed" => "GPS Port Speed",
// Config Page - Firewall Configuration
"fw_dash" => "Acesso ao Painel",
"fw_irc" => "Remoto ircDDBGateway",
"fw_ssh" => "Acesso via SSH",
// Config Page - Password
"user" => "Nome do Usuário",
"password" => "Senha",
"set_password" => "Modificar Senha",
// Dashboard Front Page - Repeater Info Pannel
"modes_enabled" => "Habilitar Modos",
"net_status" => "Status de Rede",
"internet" => "Internet",
"radio_info" => "Info do Rádio",
"dstar_repeater" => "Repetidora D-Star",
"dstar_net" => "Rede D-Star",
"dmr_repeater" => "Repetidora DMR",
"dmr_master" => "DMR Master",
"ysf_net" => "Rede YSF",
"p25_radio" => "Rádio P25",
"p25_net" => "Rede P25",
"nxdn_radio" => "Rádio NDXN",
"nxdn_net" => "Rede NXDN",
"m17_radio" => "M17 Radio",
"m17_net" => "M17 Network",
// Dashboard Front Page - Calls
"time" => "Hora",
"mode" => "Modo",
"callsign" => "Indicativo",
"target" => "Alvo",
"src" => "Src", // Short version of "Source"
"dur" => "Dur", // Short version of "Duration"
"loss" => "Loss",
"ber" => "BER", // Short version of "Bit Error Rate"
// POCSAG Specific
"pocsag_list" => "DAPNET Gateway Activity",
"pocsag_timeslot" => "Time Slot",
"pocsag_msg" => "Message",
// Dashboard - Extra Info
"group" => "Grupo",
"logoff" => "Sair",
"info" => "Informação",
"utot" => "UTOT", // Short for User Timeout
"gtot" => "GTOT", // Short for Group Timeout
// Dashboard Front Page / Admin - Section Headders
"last_heard_list" => "Últimas 20 chamadas via Gateway",
"local_tx_list" => "Últimas 20 chamadas por este Gateway",
"active_starnet_groups" => "Grupo Ativos na Starnet",
"active_starnet_members" => "Grupo de Membros Ativos na Starnet",
"d-star_link_manager" => "Gerenciar Conexão D-Star",
"d-star_link_status" => "Informações do Link D-Star",
"service_status" => "Status do Sistema"
);
?>
+158
View File
@@ -0,0 +1,158 @@
<?php
//
// Portuguese PT Language Pack
// Jorge Santos (CT1JIB)
// Updated: 08-Feb-2018
//
$lang = array (
// Banner texts
"digital_voice" => "Digital Voz",
"configuration" => "Configuração",
"dashboard_for" => "Painel de Controle de",
// Banner links
"dashboard" => "Painel",
"admin" => "Admin",
"power" => "Ação",
"update" => "Atualizar",
"backup_restore" => "Backup/Restauro",
"factory_reset" => "Reset",
"live_logs" => "Logs online",
// Config page section headdings
"hardware_info" => "Informações Hardware do Gateway",
"control_software" => "Controle Software",
"mmdvmhost_config" => "MMDVMHost Configuração",
"general_config" => "Configurações Gerais",
"dmr_config" => "Configuração do DMR",
"dstar_config" => "Configuração do DSTAR",
"ysf_config" => "Configuração do Yaesu System Fusion",
"p25_config" => "Configuração do P25",
"nxdn_config" => "Configuração do NXDN",
"m17_config" => "M17 Configuration",
"pocsag_config" => "Configuração do POCSAG",
"mobilegps_config" => "Mobile GPS Configuration",
"wifi_config" => "Configuração do Wireless",
"fw_config" => "Configuração do Firewall",
"remote_access_pw" => "Senha do Acesso Remoto",
// Config Page - Section General
"setting" => "Configurações",
"value" => "Valores",
"apply" => "Aplicar",
// Config Page - Gateway Hardware Information
"hostname" => "Nome do Host",
"kernel" => "Kernel",
"platform" => "Plataforma",
"cpu_load" => "Carga do CPU",
"cpu_temp" => "Temperatura do CPU",
// Config Page - Control Software
"controller_software" => "Tipo de Software",
"controller_mode" => "Modo de Controle",
// Config Page - MMDVMHost Configuration
"dmr_mode" => "Modo DMR",
"d-star_mode" => "Modo DSTAR",
"ysf_mode" => "Modo YSF",
"p25_mode" => "Modo P25",
"nxdn_mode" => "Modo NXDN",
"m17_mode" => "M17 Mode",
"mmdvm_display" => "Exibição MMDVM",
"mode_hangtime" => "Espera tempo do Modo",
// Config Page - General Configuration
"node_call" => "Indicativo do Nó",
"dmr_id" => "CCS7/DMR ID",
"radio_freq" => "Frequência do Rádio",
"lattitude" => "Latitude",
"longitude" => "Longitude",
"town" => "Cidade",
"country" => "País",
"url" => "URL",
"radio_type" => "Tipo de Radio/Modem",
"node_type" => "Tipo de Nó",
"timezone" => "Horário do Sistema",
"dash_lang" => "Idioma do Painel de Controle",
// Config Page - DMR Configuration
"dmr_master" => "DMR Master (MMDVMHost)",
"bm_master" => "BrandMeister Master",
"bm_network" => "Rede BrandMeister",
"dmr_plus_master" => "DMR+ Master",
"dmr_plus_network" => "Rede DMR+",
"xlx_master" => "XLX Master",
"xlx_enable" => "XLX Master Enable",
"dmr_cc" => "Código de Cores DMR",
"dmr_embeddedlconly" => "DMR EmbeddedLCOnly",
"dmr_dumptadata" => "DMR DumpTAData",
// Config Page - D-Star Configuration
"dstar_rpt1" => "R1 Indicativo",
"dstar_rpt2" => "R2 Indicativo",
"dstar_irc_password" => "Senha ircDDBGateway",
"dstar_default_ref" => "Refletor Principal",
"aprs_host" => "Servidor APRS",
"dstar_irc_lang" => "Idioma ircDDBGateway",
"dstar_irc_time" => "Anúncio a cada hora",
// Config Page - YSF Configuration
"ysf_startup_host" => "Servidor Inicial YSF",
// Config Page - P25 Configuration
"p25_startup_host" => "Servidor Inicial P25",
"p25_nac" => "P25 NAC",
// Config Page - NXDN Configuration
"nxdn_startup_host" => "Servidor Inicial NXDN",
"nxdn_ran" => "NXDN RAN",
// Config Page - M17 Configuration
"m17_startup_host" => "M17 Startup Host",
"m17_can" => "M17 CAN",
// Config Page - MobileGPS Configuration
"mobilegps_enable" => "MobileGPS Enable",
"mobilegps_port" => "GPS Port",
"mobilegps_speed" => "GPS Port Speed",
// Config Page - Firewall Configuration
"fw_dash" => "Acesso ao Painel",
"fw_irc" => "Remoto ircDDBGateway",
"fw_ssh" => "Acesso via SSH",
// Config Page - Password
"user" => "Nome utilizador",
"password" => "Senha",
"set_password" => "Modificar Senha",
// Dashboard Front Page - Repeater Info Pannel
"modes_enabled" => "Habilitar Modos",
"net_status" => "Estado da Rede",
"internet" => "Internet",
"radio_info" => "Info do Rádio",
"dstar_repeater" => "Repetidor DSTAR",
"dstar_net" => "Rede DSTAR",
"dmr_repeater" => "Repetidor DMR",
"dmr_master" => "DMR Master",
"ysf_net" => "Rede YSF",
"p25_radio" => "Rádio P25",
"p25_net" => "Rede P25",
"nxdn_radio" => "Rádio NXDN",
"nxdn_net" => "Rede NXDN",
"m17_radio" => "M17 Radio",
"m17_net" => "M17 Network",
// Dashboard Front Page - Calls
"time" => "Hora",
"mode" => "Modo",
"callsign" => "Indicativo", // versão traduzida de "callsign"
"target" => "Alvo", // versão traduzida de "Target"
"src" => "Origem", // versão traduzida de "Source"
"dur" => "Dur.", // versão abreviada de "Duration"
"loss" => "Perda",
"ber" => "BER", // versão abreviada de "Bit Error Rate"
// POCSAG Specific
"pocsag_list" => "DAPNET Gateway Activity",
"pocsag_timeslot" => "Time Slot",
"pocsag_msg" => "Message",
// Dashboard - Extra Info
"group" => "Grupo",
"logoff" => "Sair",
"info" => "Informação",
"utot" => "UTOT", // abreviatura para Timeout do utilizador
"gtot" => "GTOT", // abreviatura para Timeout do grupo
// Dashboard Front Page / Admin - Section Headders
"last_heard_list" => "Últimas 20 chamadas através deste Gateway",
"local_tx_list" => "Últimas 20 chamadas deste Gateway",
"active_starnet_groups" => "Grupos ativos na Starnet",
"active_starnet_members" => "Grupo de membros ativos na Starnet",
"d-star_link_manager" => "Gerir a ligação DSTAR",
"d-star_link_status" => "Informações do Link DSTAR",
"service_status" => "Estado do Sistema"
);
?>
+157
View File
@@ -0,0 +1,157 @@
<?php
//
// Romanian RO Language Pack
// Isidor Stirbu (YO8TEH-M0JFF)
// Updated: 12-Jun-2020
//
$lang = array (
// Banner texts
"digital_voice" => "Voce Digitala",
"configuration" => "Configuratii",
"dashboard_for" => "Panou Control pentru",
// Banner links
"dashboard" => "Panou Control",
"admin" => "Administrator",
"power" => "Alimentare",
"update" => "Actualizare",
"backup_restore" => "Copie Rezerva/Restabilire",
"factory_reset" => "Resetare Stare Initiala",
"live_logs" => "Jurnal Live",
// Config page section headdings
"hardware_info" => "Informatii Hardware Gateway",
"control_software" => "Control Software",
"mmdvmhost_config" => "Configurare MMDVMHost",
"general_config" => "Configurari Generale",
"dmr_config" => "Configurari DMR",
"dstar_config" => "Configurari D-Star",
"ysf_config" => "Configurari Yaesu System Fusion",
"p25_config" => "Configurari P25",
"nxdn_config" => "Configurari NXDN",
"m17_config" => "M17 Configuration",
"pocsag_config" => "Configurari POCSAG",
"mobilegps_config" => "Configurari Mobile GPS",
"wifi_config" => "Configurari Retea Wireless",
"fw_config" => "Configurari Firewall",
"remote_access_pw" => "Parola Acces Exterior",
// Config Page - Section General
"setting" => "Setari",
"value" => "Valoare",
"apply" => "Salvare Setari",
// Config Page - Gateway Hardware Information
"hostname" => "Denumire Detinator",
"kernel" => "Nucleu",
"platform" => "Platforma",
"cpu_load" => "Incarcare CPU",
"cpu_temp" => "Temeratura CPU",
// Config Page - Control Software
"controller_software" => "Program de Control",
"controller_mode" => "Mod Control",
// Config Page - MMDVMHost Configuration
"dmr_mode" => "Mod DMR",
"d-star_mode" => "Mod D-Star",
"ysf_mode" => "Mod YSF",
"p25_mode" => "Mod P25",
"nxdn_mode" => "Mod NXDN",
"m17_mode" => "M17 Mode",
"mmdvm_display" => "Tip Display MMDVM",
"mode_hangtime" => "Control Hangtime",
// Config Page - General Configuration
"node_call" => "Indicativ Node",
"dmr_id" => "CCS7/ID DMR",
"radio_freq" => "Frecventa Radio",
"lattitude" => "Latitudine",
"longitude" => "Longitudine",
"town" => "Oras",
"country" => "Tara",
"url" => "URL",
"radio_type" => "Model Radio/Modem",
"node_type" => "Model Node",
"timezone" => "Time Zone Sistem",
"dash_lang" => "Limba Panou Control",
// Config Page - DMR Configuration
"dmr_master" => "DMR Master (MMDVMHost)",
"bm_master" => "BrandMeister Master",
"bm_network" => "Reteaua BrandMeister",
"dmr_plus_master" => "DMR+ Master",
"dmr_plus_network" => "Retea DMR+",
"xlx_master" => "XLX Master",
"xlx_enable" => "Activare XLX Master",
"dmr_cc" => "Cod Culoare DMR",
"dmr_embeddedlconly" => "DMR EmbeddedLCOnly",
"dmr_dumptadata" => "DMR DumpTAData",
// Config Page - D-Star Configuration
"dstar_rpt1" => "Indicativ RPT1",
"dstar_rpt2" => "Indicativ RPT2",
"dstar_irc_password" => "parola ircDDBGateway",
"dstar_default_ref" => "Reflector Implicit ",
"aprs_host" => "Detinator APRS",
"dstar_irc_lang" => "Limba ircDDBGateway",
"dstar_irc_time" => "Timp instiintare",
// Config Page - YSF Configuration
"ysf_startup_host" => "lansare Detinator YSF",
// Config Page - P25 Configuration
"p25_startup_host" => "lansare Detinator P25",
"p25_nac" => "P25 NAC",
// Config Page - NXDN Configuration
"nxdn_startup_host" => "lansare Detinator NXDN",
"nxdn_ran" => "Lansare NXDN",
// Config Page - M17 Configuration
"m17_startup_host" => "M17 Startup Host",
"m17_can" => "M17 CAN",
// Config Page - MobileGPS Configuration
"mobilegps_enable" => "Activare Mobile GPS",
"mobilegps_port" => "Port GPS",
"mobilegps_speed" => "Viteza Port GPS",
// Config Page - Firewall Configuration
"fw_dash" => "Acces Panou Control",
"fw_irc" => "Control Exterior ircDDBGateway",
"fw_ssh" => "Acces SSH",
// Config Page - Password
"user" => "Nume Utilizator",
"password" => "Parola",
"set_password" => "Setare Parola",
// Dashboard Front Page - Repeater Info Pannel
"modes_enabled" => "Moduri Activate ",
"net_status" => "Stare Retea",
"internet" => "Internet",
"radio_info" => "Info Radio",
"dstar_repeater" => "Repetor D-Star",
"dstar_net" => "Reteaua D-Star",
"dmr_repeater" => "Repetor DMR",
"dmr_master" => "DMR Master",
"ysf_net" => "Reteaua YSF",
"p25_radio" => "P25 Radio",
"p25_net" => "Reteaua P25",
"nxdn_radio" => "NXDN Radio",
"nxdn_net" => "Reteaua NXDN",
"m17_radio" => "M17 Radio",
"m17_net" => "M17 Network",
// Dashboard Front Page - Calls
"time" => "Ora",
"mode" => "Mod",
"callsign" => "Indicativ",
"target" => "Apel Catre",
"src" => "Src", // Prescurtare "Sursa"
"dur" => "Dur", // prescurtare "Durata"
"loss" => "Pierderi",
"ber" => "BER", // Prescurtare "Rata Eroare Biti"
// POCSAG Specific
"pocsag_list" => "Activitate Gateway DAPNET",
"pocsag_timeslot" => "Time Slot",
"pocsag_msg" => "Mesaj",
// Dashboard - Extra Info
"group" => "Grup",
"logoff" => "Deconectare",
"info" => "Informatii",
"utot" => "UTOT", // Prescurtare User Timeout
"gtot" => "GTOT", // Prescurtare Group Timeout
// Dashboard Front Page / Admin - Section Headders
"last_heard_list" => "Ultimele 20 Apeluri Receptionate prin acest Gateway",
"local_tx_list" => "Ultimele 20 Apeluri care au accesat acest Gateway",
"active_starnet_groups" => "Grupuri Active Starnet",
"active_starnet_members" => "Membrii activi in Grupuri Starnet ",
"d-star_link_manager" => "D-Star Link Manager",
"d-star_link_status" => "Informatii Legatura D-Star",
"service_status" => "Stare Serviciu"
);
?>
+157
View File
@@ -0,0 +1,157 @@
<?php
//
// Slovenian SL Language Pack
// Deni Bacic (S55DB)
// Updated: 20-Jun-2019
//
$lang = array (
// Banner texts
"digital_voice" => "DV",
"configuration" => "Nastavitve",
"dashboard_for" => "Nadzorna plošča",
// Banner links
"dashboard" => "Nadzorna plošča",
"admin" => "Skrbniški način",
"power" => "Vklop/Izklop",
"update" => "Posodobitev",
"backup_restore" => "Varnostno kopiranje",
"factory_reset" => "Tovarniške nastavitve",
"live_logs" => "Dnevnik",
// Config page section headdings
"hardware_info" => "Informacije o strojni opremi prehoda",
"control_software" => "Nadzorna programska oprema",
"mmdvmhost_config" => "Nastavitve MMDVMHost",
"general_config" => "Splošne nastavitve",
"dmr_config" => "Nastavitve DMR",
"dstar_config" => "Nastavitve D-Star",
"ysf_config" => "Nastavitve Yaesu System Fusion",
"p25_config" => "Nastavitve P25",
"nxdn_config" => "Nastavitve NXDN",
"m17_config" => "M17 Configuration",
"pocsag_config" => "Nastavitve POCSAG",
"mobilegps_config" => "Mobile GPS Configuration",
"wifi_config" => "Nastavitve brezžičnega omrežja",
"fw_config" => "Nastavitve požarnega zidu",
"remote_access_pw" => "Geslo za oddaljeni dostop",
// Config Page - Section General
"setting" => "Nastavitev",
"value" => "Vrednost",
"apply" => "Potrdi spremembe",
// Config Page - Gateway Hardware Information
"hostname" => "Ime gostitelja",
"kernel" => "Jedro",
"platform" => "Platforma",
"cpu_load" => "Obremenitev proc.",
"cpu_temp" => "Temperatura proc.",
// Config Page - Control Software
"controller_software" => "Programska oprema krmilnika",
"controller_mode" => "Način krmilnika",
// Config Page - MMDVMHost Configuration
"dmr_mode" => "Način DMR",
"d-star_mode" => "Način D-Star",
"ysf_mode" => "Način YSF",
"p25_mode" => "Način P25",
"nxdn_mode" => "Način NXDN",
"m17_mode" => "M17 Mode",
"mmdvm_display" => "Tip MMDVM zaslona",
"mode_hangtime" => "Obstanek načina",
// Config Page - General Configuration
"node_call" => "Klicni znak vozlišča",
"dmr_id" => "CCS7/DMR ID",
"radio_freq" => "Frekvenca",
"lattitude" => "Zemljepisna širina",
"longitude" => "Zemljepisna dolžina",
"town" => "Kraj",
"country" => "Država",
"url" => "URL",
"radio_type" => "Vrsta sprejemnika/modema",
"node_type" => "Vrsta vozlišča",
"timezone" => "Časovni pas",
"dash_lang" => "Jezik nadzorne plošče",
// Config Page - DMR Configuration
"dmr_master" => "DMR Master (MMDVMHost)",
"bm_master" => "BrandMeister Master",
"bm_network" => "BrandMeister omrežje",
"dmr_plus_master" => "DMR+ Master",
"dmr_plus_network" => "DMR+ omrežje",
"xlx_master" => "XLX Master",
"xlx_enable" => "XLX Master Enable",
"dmr_cc" => "DMR Barvna Koda (CC)",
"dmr_embeddedlconly" => "DMR EmbeddedLCOnly",
"dmr_dumptadata" => "DMR DumpTAData",
// Config Page - D-Star Configuration
"dstar_rpt1" => "Klicni znak RPT1",
"dstar_rpt2" => "Klicni znak RPT2",
"dstar_irc_password" => "Geslo za oddaljeni dostop",
"dstar_default_ref" => "Privzeti reflektor",
"aprs_host" => "Gostitelj APRS",
"dstar_irc_lang" => "Jezik ircDDBGateway",
"dstar_irc_time" => "Napoved časa",
// Config Page - YSF Configuration
"ysf_startup_host" => "YSF zagonski gostitelj",
// Config Page - P25 Configuration
"p25_startup_host" => "P25 zagonski gostitelj",
"p25_nac" => "P25 NAC",
// Config Page - NXDN Configuration
"nxdn_startup_host" => "NXDN zagonski gostitelj",
"nxdn_ran" => "NXDN RAN",
// Config Page - M17 Configuration
"m17_startup_host" => "M17 Startup Host",
"m17_can" => "M17 CAN",
// Config Page - MobileGPS Configuration
"mobilegps_enable" => "MobileGPS Enable",
"mobilegps_port" => "GPS Port",
"mobilegps_speed" => "GPS Port Speed",
// Config Page - Firewall Configuration
"fw_dash" => "Dostop do nadzorne plošče",
"fw_irc" => "Dostop ircDDBGateway",
"fw_ssh" => "Dostop SSH",
// Config Page - Password
"user" => "Uporabniški ime",
"password" => "Geslo",
"set_password" => "Nastavi geslo",
// Dashboard Front Page - Repeater Info Pannel
"modes_enabled" => "Omogočeni načini",
"net_status" => "Stanje omrežja",
"internet" => "Internet",
"radio_info" => "Info o sprejemniku",
"dstar_repeater" => "Repetitor D-Star",
"dstar_net" => "D-Star Omrežje",
"dmr_repeater" => "Repetitor DMR",
"dmr_master" => "Master DMR",
"ysf_net" => "Omrežje YSF",
"p25_radio" => "Radio P25",
"p25_net" => "Omrežje P25",
"nxdn_radio" => "Radio NXDN",
"nxdn_net" => "Omrežje NXDN",
"m17_radio" => "M17 Radio",
"m17_net" => "M17 Network",
// Dashboard Front Page - Calls
"time" => "Čas",
"mode" => "Način",
"callsign" => "Klicni znak",
"target" => "Cilj",
"src" => "Vir", // Short version of "Source"
"dur" => "Dol", // Short version of "Duration"
"loss" => "Izguba",
"ber" => "DNB", // Short version of "Bit Error Rate" - delež napačnih bitov
// POCSAG Specific
"pocsag_list" => "Aktivnost DAPNET prehoda",
"pocsag_timeslot" => "Časovno okno (TS)",
"pocsag_msg" => "Sporočilo",
// Dashboard - Extra Info
"group" => "Skupina",
"logoff" => "Odjava",
"info" => "Informacija",
"utot" => "UTOT", // Short for User Timeout
"gtot" => "GTOT", // Short for Group Timeout
// Dashboard Front Page / Admin - Section Headders
"last_heard_list" => "Aktivnost prehoda",
"local_tx_list" => "Lokalna RF aktivnost",
"active_starnet_groups" => "Aktivne skupine Starnet",
"active_starnet_members" => "Aktivni uporabniki Starnet",
"d-star_link_manager" => "D-Star Link Manager",
"d-star_link_status" => "D-Star Link Informacije",
"service_status" => "Stanje storitev"
);
?>
+157
View File
@@ -0,0 +1,157 @@
<?php
//
// Spanish ES Language Pack
// Blas Cantero, EA7GIB / Francisco Jimenez-Martin, EA1JM
// 27-Sept-2017
//
$lang = array (
// Banner texts
"digital_voice" => "Voz Digital",
"configuration" => "Configuracion",
"dashboard_for" => "Panel de control de",
// Banner links
"dashboard" => "Panel de Control",
"admin" => "Administrar",
"power" => "Reiniciar/Apagar",
"update" => "Actualizar",
"backup_restore" => "Backup/Restaurar copia de seguridad",
"factory_reset" => "Restaurar datos de fabrica",
"live_logs" => "Informes-Logs",
// Config page section headdings
"hardware_info" => "Informacion de hardware",
"control_software" => "Control de software",
"mmdvmhost_config" => "Configuracion de MMDVMHost",
"general_config" => "Configuracion General",
"dmr_config" => "Configuracion de DMR",
"dstar_config" => "Configuracion de DSTAR",
"ysf_config" => "Configuracion de Yaesu C4FM fusion",
"p25_config" => "Configuracion de P25",
"nxdn_config" => "Configuracion de NXDN",
"m17_config" => "M17 Configuration",
"pocsag_config" => "Configuracion de POCSAG",
"mobilegps_config" => "Mobile GPS Configuration",
"wifi_config" => "Configuracion WIFI",
"fw_config" => "Configuracion del cortafuegos",
"remote_access_pw" => "Contraseña accceso Remoto",
// Config Page - Section General
"setting" => "Ajustes",
"value" => "Valor",
"apply" => "Aplicar",
// Config Page - Gateway Hardware Information
"hostname" => "Hostname",
"kernel" => "Kernel",
"platform" => "Plataforma",
"cpu_load" => "Carga de la CPU",
"cpu_temp" => "Temperatura CPU",
// Config Page - Control Software
"controller_software" => "Controlador Software",
"controller_mode" => "Controlador de Modo",
// Config Page - MMDVMHost Configuration
"dmr_mode" => "Modo DMR",
"d-star_mode" => "Modo D-Star",
"ysf_mode" => "Modo YSF",
"p25_mode" => "Modo P25",
"nxdn_mode" => "Modo NXDN",
"m17_mode" => "M17 Mode",
"mmdvm_display" => "MMDVM Tipo Display",
"mode_hangtime" => "Modo tiempo de suspension",
// Config Page - General Configuration
"node_call" => "Nodo indicativo de llamada",
"dmr_id" => "CCS7/DMR ID",
"radio_freq" => "Frecuencia RX/TX",
"lattitude" => "Latitud",
"longitude" => "Longitud",
"town" => "Ciudad",
"country" => "Pais",
"url" => "URL",
"radio_type" => "Radio/Tipo modem",
"node_type" => "Nodo Tipo",
"timezone" => "Zona horaria",
"dash_lang" => "Idioma del Panel de Control",
// Config Page - DMR Configuration
"dmr_master" => "DMR Master (MMDVMHost)",
"bm_master" => "Master de BrandMeister",
"bm_network" => "Red de BrandMeister",
"dmr_plus_master" => "Master de DMR+",
"dmr_plus_network" => "Red de DMR+",
"xlx_master" => "Master de XLX",
"xlx_enable" => "Habilitar Master XLX",
"dmr_cc" => "Codigo de color de DMR",
"dmr_embeddedlconly" => "DMR EmbeddedLCOnly",
"dmr_dumptadata" => "DMR DumpTAData",
// Config Page - D-Star Configuration
"dstar_rpt1" => "RPT1 indicativo de llamada",
"dstar_rpt2" => "RPT2 indicativo de llamada",
"dstar_irc_password" => "contrasena de ircDDBGateway",
"dstar_default_ref" => "Reflector predeterminado",
"aprs_host" => "Servidor de APRS",
"dstar_irc_lang" => "Idioma de ircDDBGateway",
"dstar_irc_time" => "Intervalo de Balizas",
// Config Page - YSF Configuration
"ysf_startup_host" => "Servidor de Inicio YSF",
// Config Page - P25 Configuration
"p25_startup_host" => "Servidor de Inicio P25",
"p25_nac" => "P25 NAC",
// Config Page - NXDN Configuration
"nxdn_startup_host" => "Servidor de Inicio NXDN",
"nxdn_ran" => "NXDN RAN",
// Config Page - M17 Configuration
"m17_startup_host" => "M17 Startup Host",
"m17_can" => "M17 CAN",
// Config Page - MobileGPS Configuration
"mobilegps_enable" => "MobileGPS Enable",
"mobilegps_port" => "GPS Port",
"mobilegps_speed" => "GPS Port Speed",
// Config Page - Firewall Configuration
"fw_dash" => "Tablero de acceso",
"fw_irc" => "ircDDBGateway Remoto",
"fw_ssh" => "Acceso por SSH",
// Config Page - Password
"user" => "Nombre de usuario",
"password" => "Contrasena",
"set_password" => "Establecer contrasena",
// Dashboard Front Page - Repeater Info Pannel
"modes_enabled" => "Modos habilitados",
"net_status" => "Estado de la red",
"internet" => "Internet",
"radio_info" => "Informacion de Radio",
"dstar_repeater" => "Repetidor de D-Star",
"dstar_net" => "Red de D-Star",
"dmr_repeater" => "Repetidor de DMR",
"dmr_master" => "Master de DMR",
"ysf_net" => "Red de YSF",
"p25_radio" => "Radio de P25",
"p25_net" => "Red de P25",
"nxdn_radio" => "Radio de NXDN",
"nxdn_net" => "Red de NXDN",
"m17_radio" => "M17 Radio",
"m17_net" => "M17 Network",
// Dashboard Front Page - Calls
"time" => "Hora",
"mode" => "Modo",
"callsign" => "Indicativo de llamada",
"target" => "Destino",
"src" => "SRC", //version corta de "fuente"
"dur" => "DUR", //version corta de "duracion"
"loss" => "Perdida",
"ber" => "BER", //version corta de "Error de bit"
// POCSAG Specific
"pocsag_list" => "Actividad de DAPNET Gateway",
"pocsag_timeslot" => "Slot de Tiempo",
"pocsag_msg" => "Mensaje",
// Dashboard - Extra Info
"group" => "Grupos",
"logoff" => "Finalizar",
"info" => "Informacion",
"utot" => "UTOT", //tiempo agotado para usuario
"gtot" => "GTOT", // tiempo agotado para grupo
// Dashboard Front Page / Admin - Section Headders
"last_heard_list" => "Ultimas 20 llamadas que accedieron al sistema",
"local_tx_list" => "Ultimas 20 llamadas que accedieron a ese puerto",
"active_starnet_groups" => "Grupos activos Starnet",
"active_starnet_members" => "Miembros activos del grupo Starnet",
"d-star_link_manager" => "Gestor de enlaces D-Star",
"d-star_link_status" => "Informacion de enlaces D-Star",
"service_status" => "Estado del servicio"
);
?>
+157
View File
@@ -0,0 +1,157 @@
<?php
//
// Spanish MX Language Pack
// Joe Condron
// 06-Aug-2017
//
$lang = array (
// Banner texts
"digital_voice" => "Voz Digital",
"configuration" => "Configuracion",
"dashboard_for" => "Tablero para",
// Banner links
"dashboard" => "Tablero",
"admin" => "Admin",
"power" => "Poder",
"update" => "Actualizar",
"backup_restore" => "Copia deseguridad_restaurar",
"factory_reset" => "Restablecimiento de fabrica",
"live_logs" => "Vivo Logs",
// Config page section headdings
"hardware_info" => "Puerta hardware Informacion",
"control_software" => "Control software",
"mmdvmhost_config" => "MMDVMHost Configuracion",
"general_config" => "General Configuracion",
"dmr_config" => "DMR Configuracion",
"dstar_config" => "D-Star Configuracion",
"ysf_config" => "Yaesu Configuracion de sistema fusion",
"p25_config" => "P25 Configuracion",
"nxdn_config" => "NXDN Configuracion",
"m17_config" => "M17 Configuration",
"pocsag_config" => "POCSAG Configuracion",
"mobilegps_config" => "Mobile GPS Configuration",
"wifi_config" => "Configuracion Inalambrico",
"fw_config" => "Configuracion firewall",
"remote_access_pw" => "Remoto Accesso contrasena",
// Config Page - Section General
"setting" => "Ajustes",
"value" => "Valor",
"apply" => "Aplicar",
// Config Page - Gateway Hardware Information
"hostname" => "Hostname",
"kernel" => "Kernel",
"platform" => "Plataforma",
"cpu_load" => "CPU Load",
"cpu_temp" => "CPU Temp",
// Config Page - Control Software
"controller_software" => "Controlador Software",
"controller_mode" => "Controlador Modo",
// Config Page - MMDVMHost Configuration
"dmr_mode" => "DMR Modo",
"d-star_mode" => "D-Star Modo",
"ysf_mode" => "YSF Modo",
"p25_mode" => "P25 Modo",
"nxdn_mode" => "NXDN Modo",
"m17_mode" => "M17 Mode",
"mmdvm_display" => "MMDVM Tipo de muestra",
"mode_hangtime" => "Modo tiempo de suspension",
// Config Page - General Configuration
"node_call" => "Nodo signo de llamada",
"dmr_id" => "CCS7/DMR ID",
"radio_freq" => "Radio Frecuencia",
"lattitude" => "Latitud",
"longitude" => "Longitud",
"town" => "Ciudad",
"country" => "Pais",
"url" => "URL",
"radio_type" => "Radio/Tipo modem",
"node_type" => "Nodo Tipo",
"timezone" => "Sistema zona horaria",
"dash_lang" => "Tablero de ldiomas",
// Config Page - DMR Configuration
"dmr_master" => "DMR Master (MMDVMHost)",
"bm_master" => "BrandMeister Master",
"bm_network" => "BrandMeister red",
"dmr_plus_master" => "DMR+ Master",
"dmr_plus_network" => "DMR+ red",
"xlx_master" => "XLX Master",
"xlx_enable" => "XLX Master habilitar",
"dmr_cc" => "DMR Codigo de color",
"dmr_embeddedlconly" => "DMR EmbeddedLCOnly",
"dmr_dumptadata" => "DMR DumpTAData",
// Config Page - D-Star Configuration
"dstar_rpt1" => "RPT1 signo de llamada",
"dstar_rpt2" => "RPT2 signo de llamada",
"dstar_irc_password" => "ircDDBGateway contrasena",
"dstar_default_ref" => "Reflector predeterminado",
"aprs_host" => "APRS Host",
"dstar_irc_lang" => "ircDDBGateway Language",
"dstar_irc_time" => "Tiempo Anuncios",
// Config Page - YSF Configuration
"ysf_startup_host" => "YSF Lanzamiento Host",
// Config Page - P25 Configuration
"p25_startup_host" => "P25 Lanzamiento Host",
"p25_nac" => "P25 NAC",
// Config Page - NXDN Configuration
"nxdn_startup_host" => "NXDN Lanzamiento Host",
"nxdn_ran" => "NXDN RAN",
// Config Page - M17 Configuration
"m17_startup_host" => "M17 Startup Host",
"m17_can" => "M17 CAN",
// Config Page - MobileGPS Configuration
"mobilegps_enable" => "MobileGPS Enable",
"mobilegps_port" => "GPS Port",
"mobilegps_speed" => "GPS Port Speed",
// Config Page - Firewall Configuration
"fw_dash" => "Tablero de acceso",
"fw_irc" => "ircDDBGateway Remoto",
"fw_ssh" => "SSH Acceso",
// Config Page - Password
"user" => "Usuario Nombre",
"password" => "Contrasena",
"set_password" => "Set contrasena",
// Dashboard Front Page - Repeater Info Pannel
"modes_enabled" => "Modos habilitado",
"net_status" => "Estatus de red",
"internet" => "Internet",
"radio_info" => "Radio Informacion",
"dstar_repeater" => "D-Star Repetidor",
"dstar_net" => "D-Star red",
"dmr_repeater" => "DMR Repetidor",
"dmr_master" => "DMR Master",
"ysf_net" => "YSF red",
"p25_radio" => "P25 Radio",
"p25_net" => "P25 red",
"nxdn_radio" => "NXDN Radio",
"nxdn_net" => "NXDN red",
"m17_radio" => "M17 Radio",
"m17_net" => "M17 Network",
// Dashboard Front Page - Calls
"time" => "Time",
"mode" => "Modo",
"callsign" => "signo de llamada",
"target" => "Objetivo",
"src" => "Src", //version corta de "fuente"
"dur" => "Dur", //version corta de "duracion"
"loss" => "Perdida",
"ber" => "BER", //version corta de "Error de bit"
// POCSAG Specific
"pocsag_list" => "DAPNET Gateway Activity",
"pocsag_timeslot" => "Time Slot",
"pocsag_msg" => "Message",
// Dashboard - Extra Info
"group" => "Grupos",
"logoff" => "Finalizar",
"info" => "Informacion",
"utot" => "UTOT", //tiempo agotado para usuario
"gtot" => "GTOT", // tiempo agotado para grupo
// Dashboard Front Page / Admin - Section Headders
"last_heard_list" => "Ultimos 20 llamadas escuchadas atraves de este puerto",
"local_tx_list" => "Ultimo 20 llamadas que accesaron este puerto",
"active_starnet_groups" => "Activo grupo Starnet Groups",
"active_starnet_members" => "Activo miembros de grupo Starnet",
"d-star_link_manager" => "D-Star gestar de enlaces",
"d-star_link_status" => "D-Star Informacion de enlaces",
"service_status" => "Estado servicio"
);
?>
+158
View File
@@ -0,0 +1,158 @@
<?php
//
// Svenska SE Språkpaket
// Henrik Persson (SA3BPE)
// Skapad: 02-Sep-2022
// Uppdaterad: 02-Sep-2022
//
$lang = array (
// Banner texts
"digital_voice" => "Digital Voice",
"configuration" => "Konfiguration",
"dashboard_for" => "Dashboard för",
// Banner links
"dashboard" => "Dashboard",
"admin" => "Admin",
"power" => "Power",
"update" => "Uppdatera",
"backup_restore" => "Backup/Återställ",
"factory_reset" => "Fabriksåterställning",
"live_logs" => "Liveloggar",
// Config page section headdings
"hardware_info" => "Gateway Hårdvaruinformation",
"control_software" => "Kontroll Mjukvara",
"mmdvmhost_config" => "MMDVMHost Konfiguration",
"general_config" => "Generell Konfiguration",
"dmr_config" => "DMR Konfiguration",
"dstar_config" => "D-Star Konfiguration",
"ysf_config" => "Yaesu System Fusion Konfiguration",
"p25_config" => "P25 Konfiguration",
"nxdn_config" => "NXDN Konfiguration",
"m17_config" => "M17 Configuration",
"pocsag_config" => "POCSAG Konfiguration",
"mobilegps_config" => "Mobil GPS Konfiguration",
"wifi_config" => "WiFi Konfiguration",
"fw_config" => "Brandvägg Konfiguration",
"remote_access_pw" => "Remote Access Password",
// Config Page - Section General
"setting" => "Inställning",
"value" => "Värde",
"apply" => "Spara",
// Config Page - Gateway Hardware Information
"hostname" => "Värdnamn",
"kernel" => "Kärna",
"platform" => "Plattform",
"cpu_load" => "CPU Last",
"cpu_temp" => "CPU Temp",
// Config Page - Control Software
"controller_software" => "Controller Software",
"controller_mode" => "Controller Mode",
// Config Page - MMDVMHost Configuration
"dmr_mode" => "DMR Mode",
"d-star_mode" => "D-Star Mode",
"ysf_mode" => "YSF Mode",
"p25_mode" => "P25 Mode",
"nxdn_mode" => "NXDN Mode",
"m17_mode" => "M17 Mode",
"mmdvm_display" => "MMDVM Displaytyp",
"mode_hangtime" => "Mode Hängtid",
// Config Page - General Configuration
"node_call" => "Node Signal",
"dmr_id" => "CCS7/DMR ID",
"radio_freq" => "Radiofrekvens",
"lattitude" => "Latitud",
"longitude" => "Longitud",
"town" => "Stad",
"country" => "Land",
"url" => "URL",
"radio_type" => "Radio-/Modemtyp",
"node_type" => "Nodetyp",
"timezone" => "Systemets Tidzon",
"dash_lang" => "Dashboard Språk",
// Config Page - DMR Configuration
"dmr_master" => "DMR Master (MMDVMHost)",
"bm_master" => "BrandMeister Master",
"bm_network" => "BrandMeister Network",
"dmr_plus_master" => "DMR+ Master",
"dmr_plus_network" => "DMR+ Network",
"xlx_master" => "XLX Master",
"xlx_enable" => "XLX Master Enable",
"dmr_cc" => "DMR Colour Code",
"dmr_embeddedlconly" => "DMR EmbeddedLCOnly",
"dmr_dumptadata" => "DMR DumpTAData",
// Config Page - D-Star Configuration
"dstar_rpt1" => "RPT1 Signal",
"dstar_rpt2" => "RPT2 Signal",
"dstar_irc_password" => "Lösen ircDDB",
"dstar_default_ref" => "Default Reflektor",
"aprs_host" => "APRS-värd",
"dstar_irc_lang" => "ircDDBGateway Språk",
"dstar_irc_time" => "Tidsannonseringar",
// Config Page - YSF Configuration
"ysf_startup_host" => "YSF Startvärd",
// Config Page - P25 Configuration
"p25_startup_host" => "P25 Startvärd",
"p25_nac" => "P25 NAC",
// Config Page - NXDN Configuration
"nxdn_startup_host" => "NXDN Startvärd",
"nxdn_ran" => "NXDN RAN",
// Config Page - M17 Configuration
"m17_startup_host" => "M17 Startup Host",
"m17_can" => "M17 CAN",
// Config Page - MobileGPS Configuration
"mobilegps_enable" => "MobilGPS På",
"mobilegps_port" => "GPS Port",
"mobilegps_speed" => "GPS Port Hastighet",
// Config Page - Firewall Configuration
"fw_dash" => "Dashboard Access",
"fw_irc" => "ircDDBGateway Remote",
"fw_ssh" => "SSH-access",
// Config Page - Password
"user" => "Användarnamn",
"password" => "Lösenord",
"set_password" => "Byt Lösenord",
// Dashboard Front Page - Repeater Info Pannel
"modes_enabled" => "Moder Aktiverade",
"net_status" => "Nätverkstatus",
"internet" => "Internet",
"radio_info" => "Radio Info",
"dstar_repeater" => "D-Star Repeater",
"dstar_net" => "D-Star Nätverk",
"dmr_repeater" => "DMR Repeater",
"dmr_master" => "DMR Master",
"ysf_net" => "YSF Nätverk",
"p25_radio" => "P25 Radio",
"p25_net" => "P25 Nätverk",
"nxdn_radio" => "NXDN Radio",
"nxdn_net" => "NXDN Nätverk",
"m17_radio" => "M17 Radio",
"m17_net" => "M17 Network",
// Dashboard Front Page - Calls
"time" => "Tid",
"mode" => "Mode",
"callsign" => "Signal",
"target" => "Mål",
"src" => "Src", // Short version of "Source"
"dur" => "Dur", // Short version of "Duration"
"loss" => "Loss",
"ber" => "BER", // Short version of "Bit Error Rate"
// POCSAG Specific
"pocsag_list" => "DAPNET Gatewayaktivitet",
"pocsag_timeslot" => "Tidslucka",
"pocsag_msg" => "Meddelande",
// Dashboard - Extra Info
"group" => "Grupp",
"logoff" => "Logga Av",
"info" => "Information",
"utot" => "UTOT", // Short for User Timeout
"gtot" => "GTOT", // Short for Group Timeout
// Dashboard Front Page / Admin - Section Headders
"last_heard_list" => "Gatewayaktivitet",
"local_tx_list" => "Lokal RF-aktivitet",
"active_starnet_groups" => "Aktiva Starnet Grupper",
"active_starnet_members" => "Aktiva Starnet Gruppmedlemmar",
"d-star_link_manager" => "D-Star Link Manager",
"d-star_link_status" => "D-Star Link Information",
"service_status" => "Service Status"
);
?>
+157
View File
@@ -0,0 +1,157 @@
<?php
//
// Thai TH Language Pack
// Napont Kitiwiriyakul (E24YUJ)
// Updated: 27July2018
//
$lang = array (
// Banner texts
"digital_voice" => "Digital Voice",
"configuration" => "ตั้งค่า",
"dashboard_for" => "แผงควบคุมสำหรับ",
// Banner links
"dashboard" => "แผงควบคุม",
"admin" => "ผู้ดูแล",
"power" => "เพาเวอร์",
"update" => "อัพเดท",
"backup_restore" => "สำรองข้อมูล",
"factory_reset" => "รีเซ็ต",
"live_logs" => "ประวัติการติดต่อ (สด)",
// Config page section headdings
"hardware_info" => "รายละเอียดของอุปกรณ์เกตเวย์",
"control_software" => "ซอฟต์แวร์ควบคุม",
"mmdvmhost_config" => "ตั้งค่า MMDVMHost",
"general_config" => "ตั้งค่าทั่วไป",
"dmr_config" => "ตั้งค่า DMR",
"dstar_config" => "ตั้งค่า D-Star",
"ysf_config" => "ตั้งค่า Yaesu System Fusion",
"p25_config" => "ตั้งค่า P25",
"nxdn_config" => "ตั้งค่า NXDN",
"m17_config" => "M17 Configuration",
"pocsag_config" => "ตั้งค่า POCSAG",
"mobilegps_config" => "Mobile GPS Configuration",
"wifi_config" => "ตั้งค่าวายฟาย",
"fw_config" => "ตั้งค่าระบบป้องกัน",
"remote_access_pw" => "ตั้งรหัสผ่าน",
// Config Page - Section General
"setting" => "ตั้งค่า",
"value" => "ค่า",
"apply" => "ยืนยัน",
// Config Page - Gateway Hardware Information
"hostname" => "ชื่ออุปกรณ์",
"kernel" => "เคอร์เนิล",
"platform" => "อุปกรณ์",
"cpu_load" => "โหลด CPU",
"cpu_temp" => "อุณหภูมิ CPU",
// Config Page - Control Software
"controller_software" => "ชนิดของอุปกรณ์",
"controller_mode" => "โหมดการทำงาน",
// Config Page - MMDVMHost Configuration
"dmr_mode" => "DMR",
"d-star_mode" => "D-Star",
"ysf_mode" => "YSF",
"p25_mode" => "P25",
"nxdn_mode" => "NXDN",
"m17_mode" => "M17 Mode",
"mmdvm_display" => "หน้าจอ MMDVM",
"mode_hangtime" => "Hangtime",
// Config Page - General Configuration
"node_call" => "นามเรียกขานของสถานี",
"dmr_id" => "CCS7/DMR ID",
"radio_freq" => "ความถี่ที่ใช้งาน",
"lattitude" => "ละติจูด",
"longitude" => "ลองจิจูด",
"town" => "เมือง",
"country" => "ประเทศ",
"url" => "URL",
"radio_type" => "ชนิดของอุปกรณ์",
"node_type" => "รูปแบบของสถานี",
"timezone" => "เขตเวลาที่ใช้",
"dash_lang" => "ภาษาสำหรับแผงควบคุม",
// Config Page - DMR Configuration
"dmr_master" => "DMR Master (MMDVMHost)",
"bm_master" => "BrandMeister Master",
"bm_network" => "BrandMeister Network",
"dmr_plus_master" => "DMR+ Master",
"dmr_plus_network" => "DMR+ Network",
"xlx_master" => "XLX Master",
"xlx_enable" => "XLX Master Enable",
"dmr_cc" => "DMR Color Code",
"dmr_embeddedlconly" => "DMR EmbeddedLCOnly",
"dmr_dumptadata" => "DMR DumpTAData",
// Config Page - D-Star Configuration
"dstar_rpt1" => "นามเรียกขาน RPT1",
"dstar_rpt2" => "นามเรียกขาน RPT2",
"dstar_irc_password" => "รหัสผ่านเข้าถึงจากภายนอก",
"dstar_default_ref" => "ค่าเริ่มต้นของ Reflector",
"aprs_host" => "เซิร์ฟเวอร์ APRS",
"dstar_irc_lang" => "ภาษาสำหรับ ircDDBGateway",
"dstar_irc_time" => "ประกาศเวลาทุกชั่วโมง",
// Config Page - YSF Configuration
"ysf_startup_host" => "YSF Startup Host",
// Config Page - P25 Configuration
"p25_startup_host" => "P25 Startup Host",
"p25_nac" => "P25 NAC",
// Config Page - NXDN Configuration
"nxdn_startup_host" => "NXDN Startup Host",
"nxdn_ran" => "NXDN RAN",
// Config Page - M17 Configuration
"m17_startup_host" => "M17 Startup Host",
"m17_can" => "M17 CAN",
// Config Page - MobileGPS Configuration
"mobilegps_enable" => "MobileGPS Enable",
"mobilegps_port" => "GPS Port",
"mobilegps_speed" => "GPS Port Speed",
// Config Page - Firewall Configuration
"fw_dash" => "การเข้าถึงแผงควบคุม",
"fw_irc" => "การเข้าถึง ircDDBGateway",
"fw_ssh" => "การเข้าถึง SSH",
// Config Page - Password
"user" => "ชื่อผู้ใช้",
"password" => "รหัสผ่าน",
"set_password" => "ยืนยัน",
// Dashboard Front Page - Repeater Info Pannel
"modes_enabled" => "โหมดที่เปิดใช้งาน",
"net_status" => "สถานะเครือข่าย",
"internet" => "อินเตอร์เน็ต",
"radio_info" => "ข้อมูลวิทยุ",
"dstar_repeater" => "รีพีทเตอร์ D-Star",
"dstar_net" => "เครือข่าย D-Star",
"dmr_repeater" => "รีพีทเตอร์ DMR",
"dmr_master" => "DMR Master",
"ysf_net" => "เครือข่าย YSF",
"p25_radio" => "วิทยุ P25",
"p25_net" => "เครือข่าย P25",
"nxdn_radio" => "วิทยุ NXDN",
"nxdn_net" => "เครือข่าย NXDN",
"m17_radio" => "M17 Radio",
"m17_net" => "M17 Network",
// Dashboard Front Page - Calls
"time" => "เวลา",
"mode" => "โหมด",
"callsign" => "นามเรียกขาน",
"target" => "เป้าหมาย",
"src" => "สัญญาณจาก", // Short version of "Source"
"dur" => "ระยะเวลา", // Short version of "Duration"
"loss" => "สัญญาณสูญเสีย",
"ber" => "สัญญาณผิดพลาด", // Short version of "Bit Error Rate"
// POCSAG Specific
"pocsag_list" => "DAPNET Gateway Activity",
"pocsag_timeslot" => "Time Slot",
"pocsag_msg" => "Message",
// Dashboard - Extra Info
"group" => "กลุ่ม",
"logoff" => "ออกจากระบบ",
"info" => "ข้อมูล",
"utot" => "ผู้ใช้", // Short for User Timeout
"gtot" => "กลุ่ม", // Short for Group Timeout
// Dashboard Front Page / Admin - Section Headders
"last_heard_list" => "ความเคลื่อนไหวของเกตเวย์",
"local_tx_list" => "ความเคลื่อนไหวของคลื่นที่รับได้",
"active_starnet_groups" => "Active Starnet Groups",
"active_starnet_members" => "Active Starnet Group Members",
"d-star_link_manager" => "จัดการการเชื่อมต่อของ D-Star",
"d-star_link_status" => "ข้อมูลการเชื่อมต่อของ D-Star",
"service_status" => "สถานะของระบบ"
);
?>
+157
View File
@@ -0,0 +1,157 @@
<?php
//
// Turkish TR Language Pack
// Translated by Cem Gurcan TB2TCG
// Updated: 27-JAN-2019
//
$lang = array (
// Banner texts
"digital_voice" => "Dijital Ses",
"configuration" => "Konfigürasyon",
"dashboard_for" => "Panosu",
// Banner links
"dashboard" => "Pano",
"admin" => "Yönetici",
"power" => "Güç",
"update" => "Güncelle",
"backup_restore" => "Yedekle/Geriyükle",
"factory_reset" => "Fabrika Ayarları",
"live_logs" => "Sistem Günlüğü",
// Config page section headdings
"hardware_info" => "Ağ Geçidi Donanım Bilgisi",
"control_software" => "Kontrolcü Yazılımı",
"mmdvmhost_config" => "MMDVMHost Konfigürasyonu",
"general_config" => "Genel Konfigürasyon",
"dmr_config" => "DMR Konfigürasyonu",
"dstar_config" => "D-Star Konfigürasyonu",
"ysf_config" => "Yaesu System Fusion Konfigürasyonu",
"p25_config" => "P25 Konfigürasyonu",
"nxdn_config" => "NXDN Konfigürasyonu",
"m17_config" => "M17 Configuration",
"pocsag_config" => "POCSAG Konfigürasyonu",
"mobilegps_config" => "Mobile GPS Configuration",
"wifi_config" => "Kablosuz Konfigürasyonu",
"fw_config" => "Güv.Duv. Konfigürasyonu",
"remote_access_pw" => "Uzak Erişim Parolası",
// Config Page - Section General
"setting" => "Ayar",
"value" => "Değer",
"apply" => "Değişiklikleri Uygula",
// Config Page - Gateway Hardware Information
"hostname" => "Sistem Adı",
"kernel" => "Çekirdek Sürümü",
"platform" => "Altyapı",
"cpu_load" => "CPU Kullanımı",
"cpu_temp" => "CPU Sıcaklığı",
// Config Page - Control Software
"controller_software" => "Kontrolcünün Yazılımı",
"controller_mode" => "Kontrolcünün Modu",
// Config Page - MMDVMHost Configuration
"dmr_mode" => "DMR Modu",
"d-star_mode" => "D-Star Modu",
"ysf_mode" => "YSF Modu",
"p25_mode" => "P25 Modu",
"nxdn_mode" => "NXDN Modu",
"m17_mode" => "M17 Mode",
"mmdvm_display" => "MMDVM Ekran Tipi",
"mode_hangtime" => "Mod Askı Süresi",
// Config Page - General Configuration
"node_call" => "Çağrı İşaretiniz",
"dmr_id" => "CCS7/DMR No",
"radio_freq" => "Radyo Frekansı",
"lattitude" => "Enlem",
"longitude" => "Boylam",
"town" => "Şehir",
"country" => "Ülke",
"url" => "URL",
"radio_type" => "Radyo/Modem Tipi",
"node_type" => "Yayın Tipi",
"timezone" => "Sistem Saati Dilimi",
"dash_lang" => "Pano Dili",
// Config Page - DMR Configuration
"dmr_master" => "DMR Sunucu (MMDVMHost)",
"bm_master" => "BrandMeister Sunucu",
"bm_network" => "BrandMeister Ağı",
"dmr_plus_master" => "DMR+ Sunucu",
"dmr_plus_network" => "DMR+ Ağı",
"xlx_master" => "XLX Sunucu",
"xlx_enable" => "XLX Sunucu Etkinleştirme",
"dmr_cc" => "DMR Renk Kodu",
"dmr_embeddedlconly" => "DMR GömülüLC",
"dmr_dumptadata" => "DMR TAVeriDökümü",
// Config Page - D-Star Configuration
"dstar_rpt1" => "RPT1 Çağrı İşareti",
"dstar_rpt2" => "RPT2 Çağrı İşareti",
"dstar_irc_password" => "Uzak Erişim Parolası",
"dstar_default_ref" => "Varsayılan Yansıtıcı",
"aprs_host" => "APRS Sunucusu",
"dstar_irc_lang" => "ircDDBAğGeçidi Dili",
"dstar_irc_time" => "Bildirimler",
// Config Page - YSF Configuration
"ysf_startup_host" => "YSF İşletme Ağı",
// Config Page - P25 Configuration
"p25_startup_host" => "P25 İşletme Ağı",
"p25_nac" => "P25 NAC",
// Config Page - NXDN Configuration
"nxdn_startup_host" => "NXDN İşletme Ağı",
"nxdn_ran" => "NXDN RAN",
// Config Page - M17 Configuration
"m17_startup_host" => "M17 Startup Host",
"m17_can" => "M17 CAN",
// Config Page - MobileGPS Configuration
"mobilegps_enable" => "MobileGPS Enable",
"mobilegps_port" => "GPS Port",
"mobilegps_speed" => "GPS Port Speed",
// Config Page - Firewall Configuration
"fw_dash" => "Pano Erişimi",
"fw_irc" => "ircDDBAğGeçidi Erişimi",
"fw_ssh" => "SSH Erişimi",
// Config Page - Password
"user" => "Kullanıcı Adı",
"password" => "Parola",
"set_password" => "Parolayı Uygula",
// Dashboard Front Page - Repeater Info Pannel
"modes_enabled" => "Etkin Modlar",
"net_status" => "Ağ Durumu",
"internet" => "İnternet",
"radio_info" => "Radyo Bilgisi",
"dstar_repeater" => "D-Star Röle",
"dstar_net" => "D-Star Ağı",
"dmr_repeater" => "DMR Röle",
"dmr_master" => "DMR Sunucu",
"ysf_net" => "YSF Ağı",
"p25_radio" => "P25 Radyo",
"p25_net" => "P25 Ağı",
"nxdn_radio" => "NXDN Radyo",
"nxdn_net" => "NXDN Ağı",
"m17_radio" => "M17 Radio",
"m17_net" => "M17 Network",
// Dashboard Front Page - Calls
"time" => "Zaman",
"mode" => "Mod",
"callsign" => "Çağrı İşareti",
"target" => "Hedef",
"src" => "Kynk", // Short version of "Source"
"dur" => "Süre", // Short version of "Duration"
"loss" => "Kayıp",
"ber" => "BHO", // Short version of "Bit Error Rate"
// POCSAG Specific
"pocsag_list" => "DAPNET Ağ Geçidi Etkinliği",
"pocsag_timeslot" => "Zaman Aralığı",
"pocsag_msg" => "Mesaj",
// Dashboard - Extra Info
"group" => "Grup",
"logoff" => "Oturum Kapat",
"info" => "Ayrıntılar",
"utot" => "UTOT", // Short for User Timeout
"gtot" => "GTOT", // Short for Group Timeout
// Dashboard Front Page / Admin - Section Headders
"last_heard_list" => "Ağ Geçidi Etkinliği",
"local_tx_list" => "Yerel RF Etkinliği",
"active_starnet_groups" => "Aktif Starnet Grupları",
"active_starnet_members" => "Aktif Starnet Grup Üyeleri",
"d-star_link_manager" => "D-Star Bağlantı Yöneticisi",
"d-star_link_status" => "D-Star Bağlantı Ayrıntıları",
"service_status" => "Servis Durumu"
);
?>
+10 -37
View File
@@ -1,25 +1,4 @@
<?php <?php
/**
* Live modem log viewer.
*
* AJAX-tails today's /var/log/pi-star/MMDVM-* (or DStarRepeater-*)
* log file in real time. Uses a session-stored byte offset so each
* `?ajax` call returns only the new bytes since the previous fetch,
* resetting if the log appears to have been rotated/truncated.
*
* Companion to download_modem_log.php (which sends the whole file
* in one go).
*/
require_once($_SERVER['DOCUMENT_ROOT'].'/config/security_headers.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/config/banner_warnings.inc');
setSecurityHeaders();
// Layer 2 of the default-password protection — see config/banner_warnings.inc.
// MUST run BEFORE any output so header('Location: ...') works. Affects
// both the wrapper-page render and the ?ajax tail endpoint; the latter
// is unreachable in practice unless the parent page already redirected.
pistar_warnings_enforce_redirect();
// Load the language support // Load the language support
require_once('config/language.php'); require_once('config/language.php');
// Load the Pi-Star Release file // Load the Pi-Star Release file
@@ -81,13 +60,13 @@ if ($_SERVER["PHP_SELF"] == "/admin/live_modem_log.php") {
<meta name="language" content="English" /> <meta name="language" content="English" />
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<meta name="Author" content="Andrew Taylor (MW0MWZ)" /> <meta name="Author" content="Andrew Taylor (MW0MWZ)" />
<meta name="Description" content="Pi-Star Update" /> <meta name="Description" content="CDN Update" />
<meta name="KeyWords" content="Pi-Star" /> <meta name="KeyWords" content="CDN" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="pragma" content="no-cache" /> <meta http-equiv="pragma" content="no-cache" />
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon" /> <link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon" />
<meta http-equiv="Expires" content="0" /> <meta http-equiv="Expires" content="0" />
<title>Pi-Star - <?php echo $lang['digital_voice']." ".$lang['dashboard']." - ".$lang['live_logs'];?></title> <title>CDN - <?php echo $lang['digital_voice']." ".$lang['dashboard']." - ".$lang['live_logs'];?></title>
<link rel="stylesheet" type="text/css" href="css/pistar-css.php" /> <link rel="stylesheet" type="text/css" href="css/pistar-css.php" />
<script type="text/javascript" src="/jquery.min.js"></script> <script type="text/javascript" src="/jquery.min.js"></script>
<script type="text/javascript" src="/jquery-timing.min.js"></script> <script type="text/javascript" src="/jquery-timing.min.js"></script>
@@ -107,11 +86,10 @@ if ($_SERVER["PHP_SELF"] == "/admin/live_modem_log.php") {
</script> </script>
</head> </head>
<body> <body>
<?php pistar_warnings_render(); ?>
<div class="container"> <div class="container">
<div class="header"> <div class="header">
<div style="font-size: 8px; text-align: right; padding-right: 8px;">Pi-Star:<?php echo $configPistarRelease['Pi-Star']['Version']?> / <?php echo $lang['dashboard'].": ".$version; ?></div> <div style="font-size: 8px; text-align: right; padding-right: 8px;">CDN:<?php echo $configPistarRelease['Pi-Star']['Version']?> / <?php echo $lang['dashboard'].": ".$version; ?></div>
<h1>Pi-Star <?php echo $lang['digital_voice']." - ".$lang['live_logs'];?></h1> <h1>CDN <?php echo $lang['digital_voice']." - ".$lang['live_logs'];?></h1>
<p style="padding-right: 5px; text-align: right; color: #ffffff;"> <p style="padding-right: 5px; text-align: right; color: #ffffff;">
<a href="/" style="color: #ffffff;"><?php echo $lang['dashboard'];?></a> | <a href="/" style="color: #ffffff;"><?php echo $lang['dashboard'];?></a> |
<a href="/admin/" style="color: #ffffff;"><?php echo $lang['admin'];?></a> | <a href="/admin/" style="color: #ffffff;"><?php echo $lang['admin'];?></a> |
@@ -121,17 +99,11 @@ if ($_SERVER["PHP_SELF"] == "/admin/live_modem_log.php") {
</p> </p>
</div> </div>
<div class="contentwide"> <div class="contentwide">
<table width="100%"> <h2><?php echo $lang['live_logs'];?></h2>
<tr><th><?php echo $lang['live_logs'];?></th></tr> <div class="settings-card" style="padding-top:16px;">
<tr><td align="left"><div id="tail">Starting logging, please wait...<br /></div></td></tr> <div id="tail">Starting logging, please wait...<br /></div>
<tr><th>Download the log: <a href="/admin/download_modem_log.php" style="color: #ffffff;">here</a></th></tr> <div class="field-actions">Download the log: <a href="/admin/download_modem_log.php"><?php echo $lang['live_logs'];?> (.log)</a></div>
</table>
</div> </div>
<div class="footer">
Pi-Star web config, &copy; Andy Taylor (MW0MWZ) 2014-<?php echo date("Y"); ?>.<br />
Need help? Click <a style="color: #ffffff;" href="https://www.facebook.com/groups/pistarusergroup/" target="_new">here for the Support Group</a><br />
Get your copy of Pi-Star from <a style="color: #ffffff;" href="http://www.pistar.uk/downloads/" target="_blank">here</a>.<br />
<br />
</div> </div>
</div> </div>
</body> </body>
@@ -139,3 +111,4 @@ if ($_SERVER["PHP_SELF"] == "/admin/live_modem_log.php") {
<?php <?php
} }
?>
-1
View File
@@ -1 +0,0 @@
../mmdvmhost/
+113
View File
@@ -0,0 +1,113 @@
<?php
include_once $_SERVER['DOCUMENT_ROOT'].'/config/config.php'; // MMDVMDash Config
include_once $_SERVER['DOCUMENT_ROOT'].'/mmdvmhost/tools.php'; // MMDVMDash Tools
include_once $_SERVER['DOCUMENT_ROOT'].'/mmdvmhost/functions.php'; // MMDVMDash Functions
include_once $_SERVER['DOCUMENT_ROOT'].'/config/language.php'; // Translation Code
// Check if DMR is Enabled
$testMMDVModeDMR = getConfigItem("DMR", "Enable", $mmdvmconfigs);
if ( $testMMDVModeDMR == 1 ) {
//setup BM API Key
$bmAPIkeyFile = '/etc/bmapi.key';
if (file_exists($bmAPIkeyFile) && fopen($bmAPIkeyFile,'r')) { $configBMapi = parse_ini_file($bmAPIkeyFile, true);
$bmAPIkey = $configBMapi['key']['apikey'];
// Check the BM API Key
if ( strlen($bmAPIkey) <= 20 ) { unset($bmAPIkey); }
if ( strlen($bmAPIkey) >= 200 ) { $bmAPIkeyV2 = $bmAPIkey; unset($bmAPIkey); }
}
//Load the dmrgateway config file
$dmrGatewayConfigFile = '/etc/dmrgateway';
if (fopen($dmrGatewayConfigFile,'r')) { $configdmrgateway = parse_ini_file($dmrGatewayConfigFile, true); }
// Get the current DMR Master from the config
$dmrMasterHost = getConfigItem("DMR Network", "Address", $mmdvmconfigs);
if ( $dmrMasterHost == '127.0.0.1' ) {
$dmrMasterHost = $configdmrgateway['DMR Network 1']['Address'];
if (isset($configdmrgateway['DMR Network 1']['Id'])) { $dmrID = $configdmrgateway['DMR Network 1']['Id']; }
} elseif (getConfigItem("DMR", "Id", $mmdvmconfigs)) {
$dmrID = getConfigItem("DMR", "Id", $mmdvmconfigs);
} else {
$dmrID = getConfigItem("General", "Id", $mmdvmconfigs);
}
// Store the DMR Master IP, we will need this for the JSON lookup
$dmrMasterHostIP = $dmrMasterHost;
// Make sure the master is a BrandMeister Master
$dmrMasterFile = fopen("/usr/local/etc/DMR_Hosts.txt", "r");
while (!feof($dmrMasterFile)) {
$dmrMasterLine = fgets($dmrMasterFile);
$dmrMasterHostF = preg_split('/\s+/', $dmrMasterLine);
if ((strpos($dmrMasterHostF[0], '#') === FALSE) && ($dmrMasterHostF[0] != '')) {
if ($dmrMasterHost == $dmrMasterHostF[2]) { $dmrMasterHost = str_replace('_', ' ', $dmrMasterHostF[0]); }
}
}
if (substr($dmrMasterHost, 0, 2) == "BM") {
// Use BM API to get information about current TGs
$jsonContext = stream_context_create(array('http'=>array('timeout' => 2, 'header' => 'User-Agent: Pi-Star Dashboard for '.$dmrID) )); // Add Timout and User Agent to include DMRID
if (isset($bmAPIkeyV2)) {
$json = json_decode(@file_get_contents("https://api.brandmeister.network/v2/device/$dmrID/profile", true, $jsonContext));
} else {
$json = json_decode(@file_get_contents("https://api.brandmeister.network/v1.0/repeater/?action=PROFILE&q=$dmrID", true, $jsonContext));
}
// Set some Variable
$bmStaticTGList = "";
$bmDynamicTGList = "";
// Pull the information form JSON
if (isset($json->staticSubscriptions)) { $bmStaticTGListJson = $json->staticSubscriptions;
foreach($bmStaticTGListJson as $staticTG) {
if (getConfigItem("DMR Network", "Slot1", $mmdvmconfigs) && $staticTG->slot == "1") {
$bmStaticTGList .= "TG".$staticTG->talkgroup."(".$staticTG->slot.") ";
}
else if (getConfigItem("DMR Network", "Slot2", $mmdvmconfigs) && $staticTG->slot == "2") {
$bmStaticTGList .= "TG".$staticTG->talkgroup."(".$staticTG->slot.") ";
}
else if (getConfigItem("DMR Network", "Slot1", $mmdvmconfigs) == "0" && getConfigItem("DMR Network", "Slot2", $mmdvmconfigs) && $staticTG->slot == "0") {
$bmStaticTGList .= "TG".$staticTG->talkgroup." ";
}
}
$bmStaticTGList = wordwrap($bmStaticTGList, 15, "<br />\n");
if (preg_match('/TG/', $bmStaticTGList) == false) { $bmStaticTGList = "None"; }
} else { $bmStaticTGList = "None"; }
if (isset($json->dynamicSubscriptions)) { $bmDynamicTGListJson = $json->dynamicSubscriptions;
foreach($bmDynamicTGListJson as $dynamicTG) {
if (getConfigItem("DMR Network", "Slot1", $mmdvmconfigs) && $dynamicTG->slot == "1") {
$bmDynamicTGList .= "TG".$dynamicTG->talkgroup."(".$dynamicTG->slot.") ";
}
else if (getConfigItem("DMR Network", "Slot2", $mmdvmconfigs) && $dynamicTG->slot == "2") {
$bmDynamicTGList .= "TG".$dynamicTG->talkgroup."(".$dynamicTG->slot.") ";
}
else if (getConfigItem("DMR Network", "Slot1", $mmdvmconfigs) == "0" && getConfigItem("DMR Network", "Slot2", $mmdvmconfigs) && $dynamicTG->slot == "0") {
$bmDynamicTGList .= "TG".$dynamicTG->talkgroup." ";
}
}
$bmDynamicTGList = wordwrap($bmDynamicTGList, 15, "<br />\n");
if (preg_match('/TG/', $bmDynamicTGList) == false) { $bmDynamicTGList = "None"; }
} else { $bmDynamicTGList = "None"; }
echo '<b>Active BrandMeister Connections</b>
<table>
<tr>
<th><a class=tooltip href="#">'.$lang['bm_master'].'<span><b>Connected Master</b></span></a></th>
<th><a class=tooltip href="#">Repeater ID<span><b>The ID for this Repeater/Hotspot</b></span></a></th>
<th><a class=tooltip href="#">Static TGs<span><b>Statically linked talkgroups</b></span></a></th>
<th><a class=tooltip href="#">Dynamic TGs<span><b>Dynamically linked talkgroups</b></span></a></th>
</tr>'."\n";
echo ' <tr>'."\n";
echo ' <td>'.$dmrMasterHost.'</td>';
echo '<td>'.$dmrID.'</td>';
echo '<td>'.$bmStaticTGList.'</td>';
echo '<td>'.$bmDynamicTGList.'</td>';
echo '</tr>'."\n";
echo ' </table>'."\n";
echo ' <br />'."\n";
}
}
?>
+137
View File
@@ -0,0 +1,137 @@
<?php
if ($_SERVER["PHP_SELF"] == "/admin/index.php") { // Stop this working outside of the admin page
include_once $_SERVER['DOCUMENT_ROOT'].'/config/config.php'; // MMDVMDash Config
include_once $_SERVER['DOCUMENT_ROOT'].'/mmdvmhost/tools.php'; // MMDVMDash Tools
include_once $_SERVER['DOCUMENT_ROOT'].'/mmdvmhost/functions.php'; // MMDVMDash Functions
include_once $_SERVER['DOCUMENT_ROOT'].'/config/language.php'; // Translation Code
// Check if DMR is Enabled
$testMMDVModeDMR = getConfigItem("DMR", "Enable", $mmdvmconfigs);
if ( $testMMDVModeDMR == 1 ) {
//setup BM API Key
$bmAPIkeyFile = '/etc/bmapi.key';
if (file_exists($bmAPIkeyFile) && fopen($bmAPIkeyFile,'r')) {
$configBMapi = parse_ini_file($bmAPIkeyFile, true);
$bmAPIkey = $configBMapi['key']['apikey'];
// Check the BM API Key
if ( strlen($bmAPIkey) <= 20 ) { unset($bmAPIkey); }
if ( strlen($bmAPIkey) >= 200 ) { $bmAPIkeyV2 = $bmAPIkey; unset($bmAPIkey); }
}
//Load the dmrgateway config file
$dmrGatewayConfigFile = '/etc/dmrgateway';
if (fopen($dmrGatewayConfigFile,'r')) { $configdmrgateway = parse_ini_file($dmrGatewayConfigFile, true); }
// Get the current DMR Master from the config
$dmrMasterHost = getConfigItem("DMR Network", "Address", $mmdvmconfigs);
if ( $dmrMasterHost == '127.0.0.1' ) {
$dmrMasterHost = $configdmrgateway['DMR Network 1']['Address'];
if (isset($configdmrgateway['DMR Network 1']['Id'])) { $dmrID = $configdmrgateway['DMR Network 1']['Id']; }
} elseif (getConfigItem("DMR", "Id", $mmdvmconfigs)) {
$dmrID = getConfigItem("DMR", "Id", $mmdvmconfigs);
} else {
$dmrID = getConfigItem("General", "Id", $mmdvmconfigs);
}
// Store the DMR Master IP, we will need this for the JSON lookup
$dmrMasterHostIP = $dmrMasterHost;
// Make sure the master is a BrandMeister Master
$dmrMasterFile = fopen("/usr/local/etc/DMR_Hosts.txt", "r");
while (!feof($dmrMasterFile)) {
$dmrMasterLine = fgets($dmrMasterFile);
$dmrMasterHostF = preg_split('/\s+/', $dmrMasterLine);
if ((strpos($dmrMasterHostF[0], '#') === FALSE) && ($dmrMasterHostF[0] != '')) {
if ($dmrMasterHost == $dmrMasterHostF[2]) { $dmrMasterHost = str_replace('_', ' ', $dmrMasterHostF[0]); }
}
}
if (substr($dmrMasterHost, 0, 2) == "BM") {
if (isset($bmAPIkeyV2) && !empty($_POST) && (isset($_POST["dropDyn"]) || isset($_POST["dropQso"]) || isset($_POST["tgSubmit"]))) : // Data has been posted for this page
$bmAPIurl = 'https://api.brandmeister.network/v2/device/';
// Are we a repeater
if ( getConfigItem("DMR Network", "Slot1", $mmdvmconfigs) == "0" ) {
unset($_POST["TS"]);
$targetSlot = "0";
} else {
$targetSlot = $_POST["TS"];
}
// Set the API URLs
if (isset($_POST["dropDyn"])) { $bmAPIurl = $bmAPIurl.$dmrID."/action/dropDynamicGroups/".$targetSlot; $method = "GET"; }
if (isset($_POST["dropQso"])) { $bmAPIurl = $bmAPIurl.$dmrID."/action/dropCallRoute/".$targetSlot; $method = "GET"; }
if ( (isset($_POST["tgNr"])) && (isset($_POST["tgSubmit"])) ) { $targetTG = preg_replace("/[^0-9]/", "", $_POST["tgNr"]); }
if ( ($_POST["TGmgr"] == "ADD") && (isset($_POST["tgSubmit"])) ) { $bmAPIurl = $bmAPIurl.$dmrID."/talkgroup/"; $method = "POST"; }
if ( ($_POST["TGmgr"] == "DEL") && (isset($_POST["tgSubmit"])) ) { $bmAPIurl = $bmAPIurl.$dmrID."/talkgroup/".$targetSlot."/".$targetTG; $method = "DELETE"; }
// Build the Data
$postHeaders = array(
'Content-Type: application/json',
'Accept: application/json',
'Authorization: Bearer '.$bmAPIkeyV2,
'User-Agent: Pi-Star Dashboard for '.$dmrID,
);
$opts = array(
'http' => array(
'method' => $method,
//'header' => $postHeaders,
'header' => implode("\r\n", $postHeaders),
'password' => '',
'success' => '',
'timeout' => 2,
),
);
// If we are adding a TG
if ( (!isset($_POST["dropDyn"])) && (!isset($_POST["dropQso"])) && isset($targetTG) && $_POST["TGmgr"] == "ADD" ) {
$postDataTG = array(
'slot' => $targetSlot,
'group' => $targetTG
);
$postData = json_encode($postDataTG);
$postHeaders[] = 'Content-Length: '.strlen($postData);
$opts['http']['content'] = $postData;
}
// Make the request
$context = stream_context_create($opts);
$result = @file_get_contents($bmAPIurl, false, $context);
$feeback=json_decode($result);
// Output to the browser
echo '<b>BrandMeister Manager</b>'."\n";
echo "<table>\n<tr><th>Command Output</th></tr>\n<tr><td>";
if (isset($feeback)) { print "BrandMeister APIv2: OK"; } else { print "BrandMeister APIv2: No Response"; }
echo "</td></tr>\n</table>\n";
echo "<br />\n";
// Clean up...
unset($_POST);
echo '<script type="text/javascript">setTimeout(function() { window.location=window.location;},3000);</script>';
else: // Do this when we are not handling post data
// If there is a BM API Key
if (isset($bmAPIkeyV2)) {
echo '<b>BrandMeister Manager</b>'."\n";
echo '<form action="'.htmlentities($_SERVER['PHP_SELF']).'" method="post">'."\n";
echo '<table role="presentation">'."\n";
echo '<tr>
<th aria-hidden="true" id="lblTG" style="width:25%;"><a class=tooltip href="#">Static Talkgroup<span><b>Enter the Talkgroup number</b></span></a></th>
<th aria-hidden="true" id="lblSlot" style="width:25%;"><a class=tooltip href="#">Slot<span><b>Where to link/unlink</b></span></a></th>
<th aria-hidden="true" id="addRemove" style="width:25%;"><a class=tooltip href="#">Add / Remove<span><b>Add or Remove</b></span></a></th>
<th><a class=tooltip href="#">Action<span><b>Take Action</b></span></a></th>
</tr>'."\n";
echo ' <tr>';
echo '<td><input aria-labelledby="lblTG" type="text" name="tgNr" size="10" maxlength="7" /></td>';
echo '<td role="radiogroup" aria-labelledby="lblTS"><input id="rbTS1" type="radio" name="TS" value="1" /><label for="rbTS1">TS1</label> <input id="rbTS2" type="radio" name="TS" value="2" checked="checked" /><label for="rbTS2">TS2</label></td>';
echo '<td role="radiogroup" aria-labelledby="lblAddRemove"><input id="rbAdd" type="radio" name="TGmgr" value="ADD" checked="checked" /><label for="rbAdd">Add</label> <input id="rbDelete" type="radio" name="TGmgr" value="DEL" /><label for="rbDelete">Delete</label></td>';
echo '<td><input type="submit" value="Modify Static" name="tgSubmit" /></td>';
echo '</tr>'."\n";
echo ' <tr>';
echo '<td colspan="4" style="background: #ffffff;"><input type="submit" value="Drop QSO" name="dropQso" /> <input type="submit" value="Drop All Dynamic" name="dropDyn" /></td>';
echo '</tr>'."\n";
echo ' </table>'."\n";
echo ' <br />'."\n";
}
endif;
}
}
}
File diff suppressed because it is too large Load Diff
View File
+201
View File
@@ -0,0 +1,201 @@
<?php
/**
* Last-heard list (MMDVMHost mode) — last 20 unique transmissions across
* every enabled mode (D-Star / DMR / YSF / P25 / NXDN / M17 / POCSAG).
*
* AJAX-loaded partial; refreshed every 1.5 seconds by /index.php in
* MMDVMHost mode. Renders columns: time (UTC → local), mode, callsign,
* target, source (RF / Net), duration, loss, BER. Callsign links use
* the operator's chosen lookup service (RadioID or QRZ, from
* /etc/pistar-css.ini's [Lookup] Service key) plus aprs.fi for D-Star
* dPRS data.
*
* Data flow: relies on `$lastHeard` populated by mmdvmhost/functions.php
* (which parses /var/log/pi-star/MMDVM-YYYY-MM-DD.log via shell
* pipelines). This file just renders the array — see functions.php
* for the parsing logic and log-line offset comments.
*/
require_once($_SERVER['DOCUMENT_ROOT'] . '/config/security_headers.php');
// AJAX-loaded partial; the parent page (index.php) sets the full
// security headers. setEmbeddableSecurityHeaders() ships the
// non-frame-related security headers without locking frame-
// ancestors, so the partial can be loaded into the parent via
// $.load(). Calling setSecurityHeaders() before this would emit
// X-Frame-Options + frame-ancestors 'self', which makes the
// embeddable variant a no-op (headers_sent() === true) — fixes
// the historical bug where the wrong variant won.
setEmbeddableSecurityHeaders();
include_once $_SERVER['DOCUMENT_ROOT'].'/config/config.php'; // MMDVMDash Config
include_once $_SERVER['DOCUMENT_ROOT'].'/mmdvmhost/tools.php'; // MMDVMDash Tools
include_once $_SERVER['DOCUMENT_ROOT'].'/mmdvmhost/functions.php'; // MMDVMDash Functions
include_once $_SERVER['DOCUMENT_ROOT'].'/config/language.php'; // Translation Code
// Check if the config file exists
if (file_exists('/etc/pistar-css.ini')) {
// Use the values from the file
$piStarCssFile = '/etc/pistar-css.ini';
if (fopen($piStarCssFile,'r')) { $piStarCss = parse_ini_file($piStarCssFile, true); }
// Set the Values from the config file
if (isset($piStarCss['Lookup']['Service'])) { $callsignLookupSvc = $piStarCss['Lookup']['Service']; } // Lookup Service "QRZ" or "RadioID"
else { $callsignLookupSvc = "RadioID"; } // Set the default if its missing // Set the default if its missing
} else {
// Default values
$callsignLookupSvc = "RadioID";
}
// Safety net
if (($callsignLookupSvc != "RadioID") && ($callsignLookupSvc != "QRZ")) { $callsignLookupSvc = "RadioID"; }
// Setup the URL(s)
$idLookupUrl = "https://database.radioid.net/database/view?id=";
if ($callsignLookupSvc == "RadioID") { $callsignLookupUrl = "https://database.radioid.net/database/view?callsign="; }
if ($callsignLookupSvc == "QRZ") { $callsignLookupUrl = "https://www.qrz.com/db/"; }
?>
<b><?php echo $lang['last_heard_list'];?></b>
<table>
<tr>
<th><a class="tooltip" href="#"><?php echo $lang['time'];?> (<?php echo date('T')?>)<span><b>Time in <?php echo date('T')?> time zone</b></span></a></th>
<th><a class="tooltip" href="#"><?php echo $lang['mode'];?><span><b>Transmitted Mode</b></span></a></th>
<th style="min-width:14ch"><a class="tooltip" href="#"><?php echo $lang['callsign'];?><span><b>Callsign</b></span></a></th>
<th><a class="tooltip" href="#"><?php echo $lang['target'];?><span><b>Target, D-Star Reflector, DMR Talk Group etc</b></span></a></th>
<th><a class="tooltip" href="#"><?php echo $lang['src'];?><span><b>Received from source</b></span></a></th>
<th><a class="tooltip" href="#"><?php echo $lang['dur'];?>(s)<span><b>Duration in Seconds</b></span></a></th>
<th><a class="tooltip" href="#"><?php echo $lang['loss'];?><span><b>Packet Loss</b></span></a></th>
<th><a class="tooltip" href="#"><?php echo $lang['ber'];?><span><b>Bit Error Rate</b></span></a></th>
</tr>
<?php
$i = 0;
for ($i = 0; ($i <= 19); $i++) { //Last 20 calls
if (isset($lastHeard[$i])) {
$listElem = $lastHeard[$i];
if ( $listElem[2] ) {
$utc_time = $listElem[0];
$utc_tz = new DateTimeZone('UTC');
$local_tz = new DateTimeZone(date_default_timezone_get ());
$dt = new DateTime($utc_time, $utc_tz);
$dt->setTimeZone($local_tz);
$local_time = $dt->format('H:i:s M jS');
// Every value below comes from log-line parsing in
// mmdvmhost/functions.php, which in turn parses log lines
// produced by RF traffic. A station transmitting on RF
// can put almost any byte sequence into the callsign or
// target field; without escaping it lands in the
// dashboard's HTML refresh every 1.5s. Normalise once
// here so every echo below works on safe values.
//
// $modeHtml — "Slot 1" -> "TS1" cosmetic + escape
// $cs — callsign (HTML-safe text)
// $csUrl — callsign URL-encoded for href= path
// $csSuffix — D-Star station ID after `/` (HTML-safe)
// $tgt — target callsign / talkgroup (HTML-safe)
// $src — source ("RF"/"Net"/etc.; HTML-safe)
// $dur, $loss, $ber — numeric; HTML-safe defensively.
//
// The downstream str_replace ' '->'&nbsp;' has to run
// AFTER htmlspecialchars; the entity reference `&nbsp;`
// is intentional raw HTML output, not data.
$modeHtml = htmlspecialchars(str_replace('Slot ', 'TS', $listElem[1]), ENT_QUOTES, 'UTF-8');
$cs = htmlspecialchars((string)$listElem[2], ENT_QUOTES, 'UTF-8');
$csUrl = rawurlencode((string)$listElem[2]);
$csSuffix = htmlspecialchars((string)$listElem[3], ENT_QUOTES, 'UTF-8');
// Target normalisation: if it's a single character left-pad
// it to 8 spaces (cosmetic, matches legacy layout). The
// visible value is HTML-escaped, then spaces become
// non-breaking-space entities AFTER the escape so the
// entity isn't double-encoded.
$tgtRaw = (string)$listElem[4];
// Append this operator's own alias in parentheses when the
// target is a plain "TG <id>" and TGList_CN.json has a name for
// it — MMDVMHost's log never carries a name, only the numeric
// ID, so without this lookup the column can only ever show
// digits (see getTGNameMap() in functions.php).
if (preg_match('/^TG\s+(\d+)$/', trim($tgtRaw), $tgMatch)) {
$tgName = getTGNameMap()[(int)$tgMatch[1]] ?? null;
if ($tgName !== null) { $tgtRaw .= " ($tgName)"; }
}
if (strlen($tgtRaw) === 1) { $tgtRaw = str_pad($tgtRaw, 8, ' ', STR_PAD_LEFT); }
$tgtHtml = htmlspecialchars($tgtRaw, ENT_QUOTES, 'UTF-8');
$src = htmlspecialchars((string)$listElem[5], ENT_QUOTES, 'UTF-8');
$dur = htmlspecialchars((string)$listElem[6], ENT_QUOTES, 'UTF-8');
$loss = htmlspecialchars((string)(isset($listElem[7]) ? $listElem[7] : ''), ENT_QUOTES, 'UTF-8');
$ber = htmlspecialchars((string)(isset($listElem[8]) ? $listElem[8] : ''), ENT_QUOTES, 'UTF-8');
echo "<tr>";
echo "<td align=\"left\">$local_time</td>";
echo "<td align=\"left\">$modeHtml</td>";
if (is_numeric($listElem[2])) {
if ($listElem[2] > 9999) { echo "<td align=\"left\"><a href=\"".$idLookupUrl.$csUrl."\" target=\"_blank\">$cs</a></td>"; }
else { echo "<td align=\"left\">$cs</td>"; }
} elseif (!preg_match('/[A-Za-z].*[0-9]|[0-9].*[A-Za-z]/', $listElem[2])) {
echo "<td align=\"left\">$cs</td>";
} else {
// Strip any "-suffix" before linking — re-derive the
// url-encoded form from the trimmed value.
$csTrim = (strpos($listElem[2], "-") > 0)
? substr($listElem[2], 0, strpos($listElem[2], "-"))
: (string)$listElem[2];
$csTrimHtml = htmlspecialchars($csTrim, ENT_QUOTES, 'UTF-8');
$csTrimUrl = rawurlencode($csTrim);
if ( $listElem[3] && $listElem[3] != ' ' ) {
echo "<td align=\"left\"><div style=\"float:left;\"><a href=\"".$callsignLookupUrl.$csTrimUrl."\" target=\"_blank\">$csTrimHtml</a>/$csSuffix</div> <div style=\"text-align:right;\">&#40;<a href=\"https://aprs.fi/#!call=".$csTrimUrl."*\" target=\"_blank\">GPS</a>&#41;</div></td>";
} else {
echo "<td align=\"left\"><div style=\"float:left;\"><a href=\"".$callsignLookupUrl.$csTrimUrl."\" target=\"_blank\">$csTrimHtml</a></div> <div style=\"text-align:right;\">&#40;<a href=\"https://aprs.fi/#!call=".$csTrimUrl."*\" target=\"_blank\">GPS</a>&#41;</div></td>";
}
}
if ( substr($tgtRaw, 0, 6) === 'CQCQCQ' ) {
echo "<td align=\"left\">$tgtHtml</td>";
} else {
echo "<td align=\"left\">".str_replace(' ', '&nbsp;', $tgtHtml)."</td>";
}
if ($listElem[5] == "RF"){
echo "<td style=\"background:#1d1;\">RF</td>";
}else{
echo "<td>$src</td>";
}
if ($listElem[6] == null) {
// Live duration
$utc_time = $listElem[0];
$utc_tz = new DateTimeZone('UTC');
$now = new DateTime("now", $utc_tz);
$dt = new DateTime($utc_time, $utc_tz);
$duration = $now->getTimestamp() - $dt->getTimestamp();
$duration_string = $duration<999 ? round($duration) . "+" : "&infin;";
echo "<td colspan =\"3\" style=\"background:#f33;\">TX " . $duration_string . " sec</td>";
} else if ($listElem[6] == "DMR Data") {
echo "<td colspan =\"3\" style=\"background:#1d1;\">DMR Data</td>";
} else if ($listElem[6] == "POCSAG Data") {
echo "<td colspan =\"3\" style=\"background:#1d1;\">POCSAG Data</td>";
} else {
echo "<td>$dur</td>";
// Colour the Loss Field
if (floatval($listElem[7]) < 1) { echo "<td>$loss</td>"; }
elseif (floatval($listElem[7]) == 1) { echo "<td style=\"background:#1d1;\">$loss</td>"; }
elseif (floatval($listElem[7]) > 1 && floatval($listElem[7]) <= 3) { echo "<td style=\"background:#fa0;\">$loss</td>"; }
else { echo "<td style=\"background:#f33;\">$loss</td>"; }
// Colour the BER Field
if (floatval($listElem[8]) == 0) { echo "<td>$ber</td>"; }
elseif (floatval($listElem[8]) >= 0.0 && floatval($listElem[8]) <= 1.9) { echo "<td style=\"background:#1d1;\">$ber</td>"; }
elseif (floatval($listElem[8]) >= 2.0 && floatval($listElem[8]) <= 4.9) { echo "<td style=\"background:#fa0;\">$ber</td>"; }
else { echo "<td style=\"background:#f33;\">$ber</td>"; }
}
echo "</tr>\n";
}
}
}
?>
</table>
+108
View File
@@ -0,0 +1,108 @@
<?php
include_once $_SERVER['DOCUMENT_ROOT'].'/config/config.php'; // MMDVMDash Config
include_once $_SERVER['DOCUMENT_ROOT'].'/mmdvmhost/tools.php'; // MMDVMDash Tools
include_once $_SERVER['DOCUMENT_ROOT'].'/mmdvmhost/functions.php'; // MMDVMDash Functions
include_once $_SERVER['DOCUMENT_ROOT'].'/config/language.php'; // Translation Code
$localTXList = $lastHeard;
// Check if the config file exists
if (file_exists('/etc/pistar-css.ini')) {
// Use the values from the file
$piStarCssFile = '/etc/pistar-css.ini';
if (fopen($piStarCssFile,'r')) { $piStarCss = parse_ini_file($piStarCssFile, true); }
// Set the Values from the config file
if (isset($piStarCss['Lookup']['Service'])) { $callsignLookupSvc = $piStarCss['Lookup']['Service']; } // Lookup Service "QRZ" or "RadioID"
else { $callsignLookupSvc = "RadioID"; } // Set the default if its missing // Set the default if its missing
} else {
// Default values
$callsignLookupSvc = "RadioID";
}
// Safety net
if (($callsignLookupSvc != "RadioID") && ($callsignLookupSvc != "QRZ")) { $callsignLookupSvc = "RadioID"; }
// Setup the URL(s)
$idLookupUrl = "https://database.radioid.net/database/view?id=";
if ($callsignLookupSvc == "RadioID") { $callsignLookupUrl = "https://database.radioid.net/database/view?callsign="; }
if ($callsignLookupSvc == "QRZ") { $callsignLookupUrl = "https://www.qrz.com/db/"; }
?>
<b><?php echo $lang['local_tx_list'];?></b>
<table>
<tr>
<th><a class="tooltip" href="#"><?php echo $lang['time'];?> (<?php echo date('T')?>)<span><b>Time in <?php echo date('T')?> time zone</b></span></a></th>
<th><a class="tooltip" href="#"><?php echo $lang['mode'];?><span><b>Transmitted Mode</b></span></a></th>
<th style="min-width:14ch"><a class="tooltip" href="#"><?php echo $lang['callsign'];?><span><b>Callsign</b></span></a></th>
<th><a class="tooltip" href="#"><?php echo $lang['target'];?><span><b>Target, D-Star Reflector, DMR Talk Group etc</b></span></a></th>
<th><a class="tooltip" href="#"><?php echo $lang['src'];?><span><b>Received from source</b></span></a></th>
<th><a class="tooltip" href="#"><?php echo $lang['dur'];?>(s)<span><b>Duration in Seconds</b></span></a></th>
<th style="min-width:5ch"><a class="tooltip" href="#"><?php echo $lang['ber'];?><span><b>Bit Error Rate</b></span></a></th>
<th style="min-width:8ch"><a class="tooltip" href="#">RSSI<span><b>Received Signal Strength Indication</b></span></a></th>
</tr>
<?php
$counter = 0;
$i = 0;
$TXListLim = count($localTXList);
for ($i = 0; $i < $TXListLim; $i++) {
$listElem = $localTXList[$i];
if ($listElem[5] == "RF" && ($listElem[1] == "D-Star" || startsWith($listElem[1], "DMR") || $listElem[1] == "YSF" || $listElem[1]== "P25" || $listElem[1]== "NXDN" || $listElem[1]== "M17")) {
if ($counter <= 19) { //last 20 calls
$utc_time = $listElem[0];
$utc_tz = new DateTimeZone('UTC');
$local_tz = new DateTimeZone(date_default_timezone_get ());
$dt = new DateTime($utc_time, $utc_tz);
$dt->setTimeZone($local_tz);
$local_time = $dt->format('H:i:s M jS');
echo "<tr>";
echo "<td align=\"left\">$local_time</td>";
echo "<td align=\"left\">".str_replace('Slot ', 'TS', $listElem[1])."</td>";
if (is_numeric($listElem[2])) {
if ($listElem[2] > 9999) { echo "<td align=\"left\"><a href=\"".$idLookupUrl.$listElem[2]."\" target=\"_blank\">$listElem[2]</a></td>"; }
else { echo "<td align=\"left\">".$listElem[2]."</td>"; }
} elseif (!preg_match('/[A-Za-z].*[0-9]|[0-9].*[A-Za-z]/', $listElem[2])) {
echo "<td align=\"left\">$listElem[2]</td>";
} else {
if (strpos($listElem[2],"-") > 0) { $listElem[2] = substr($listElem[2], 0, strpos($listElem[2],"-")); }
if ($listElem[3] && $listElem[3] != ' ' ) {
echo "<td align=\"left\"><div style=\"float:left;\"><a href=\"".$callsignLookupUrl.$listElem[2]."\" target=\"_blank\">$listElem[2]</a>/$listElem[3]</div> <div style=\"text-align:right;\">&#40;<a href=\"https://aprs.fi/#!call=".$listElem[2]."*\" target=\"_blank\">GPS</a>&#41;</div></td>";
} else {
echo "<td align=\"left\"><div style=\"float:left;\"><a href=\"".$callsignLookupUrl.$listElem[2]."\" target=\"_blank\">$listElem[2]</a></div> <div style=\"text-align:right;\">&#40;<a href=\"https://aprs.fi/#!call=".$listElem[2]."*\" target=\"_blank\">GPS</a>&#41;</div></td>";
}
}
if (strlen($listElem[4]) == 1) { $listElem[4] = str_pad($listElem[4], 8, " ", STR_PAD_LEFT); }
echo"<td align=\"left\">".str_replace(" ","&nbsp;", $listElem[4])."</td>";
if ($listElem[5] == "RF"){
echo "<td style=\"background:#1d1;\">RF</td>";
} else {
echo "<td>$listElem[5]</td>";
}
if ($listElem[6] == null) {
// Live duration
$utc_time = $listElem[0];
$utc_tz = new DateTimeZone('UTC');
$now = new DateTime("now", $utc_tz);
$dt = new DateTime($utc_time, $utc_tz);
$duration = $now->getTimestamp() - $dt->getTimestamp();
$duration_string = $duration<999 ? round($duration) . "+" : "&infin;";
echo "<td colspan=\"3\" style=\"background:#f33;\">TX " . $duration_string . " sec</td>";
} else if ($listElem[6] == "DMR Data") {
echo "<td colspan=\"3\" style=\"background:#1d1;\">DMR Data</td>";
} else {
echo"<td>$listElem[6]</td>"; //duration
// Colour the BER Field
if (floatval($listElem[8]) == 0) { echo "<td>$listElem[8]</td>"; }
elseif (floatval($listElem[8]) >= 0.0 && floatval($listElem[8]) <= 1.9) { echo "<td style=\"background:#1d1;\">$listElem[8]</td>"; }
elseif (floatval($listElem[8]) >= 2.0 && floatval($listElem[8]) <= 4.9) { echo "<td style=\"background:#fa0;\">$listElem[8]</td>"; }
else { echo "<td style=\"background:#f33;\">$listElem[8]</td>"; }
echo"<td>$listElem[9]</td>"; //rssi
}
echo "</tr>\n";
$counter++; }
}
}
?>
</table>
+142
View File
@@ -0,0 +1,142 @@
<?php
if ($_SERVER["PHP_SELF"] == "/admin/index.php") { // Stop this working outside of the admin page
include_once $_SERVER['DOCUMENT_ROOT'].'/config/config.php'; // MMDVMDash Config
include_once $_SERVER['DOCUMENT_ROOT'].'/mmdvmhost/tools.php'; // MMDVMDash Tools
include_once $_SERVER['DOCUMENT_ROOT'].'/mmdvmhost/functions.php'; // MMDVMDash Functions
include_once $_SERVER['DOCUMENT_ROOT'].'/config/language.php'; // Translation Code
// Check if M17 is Enabled
$testMMDVModeM17 = getConfigItem("M17 Network", "Enable", $mmdvmconfigs);
if ( $testMMDVModeM17 == 1 ) {
//Load the m17gateway config file
$m17GatewayConfigFile = '/etc/m17gateway';
if (fopen($m17GatewayConfigFile,'r')) { $configm17gateway = parse_ini_file($m17GatewayConfigFile, true); }
// Check that the remote is enabled
if ( $configm17gateway['Remote Commands']['Enable'] == 1 ) {
$remotePort = $configm17gateway['Remote Commands']['Port'];
if (!empty($_POST) && isset($_POST["m17MgrSubmit"])) {
// Handle Posted Data
if (preg_match('/[^A-Za-z0-9-]/',$_POST['m17LinkHost'])) { unset ($_POST['m17LinkHost']); unset ($_POST['m17LinkRoom']); }
if (preg_match('/[^A-Z]/',$_POST['m17LinkRoom'])) { unset ($_POST['m17LinkHost']); unset ($_POST['m17LinkRoom']); }
if ($_POST["Link"] == "LINK") {
if ($_POST['m17LinkHost'] == "none") {
$remoteCommand = "cd /var/log/pi-star && sudo /usr/local/bin/RemoteCommand ".$remotePort." Reflector unlink";
} else {
$remoteCommand = "cd /var/log/pi-star && sudo /usr/local/bin/RemoteCommand ".$remotePort." Reflector ".$_POST['m17LinkHost']." ".$_POST['m17LinkRoom'];
}
} elseif ($_POST["Link"] == "UNLINK") {
$remoteCommand = "cd /var/log/pi-star && sudo /usr/local/bin/RemoteCommand ".$remotePort." Reflector unlink";
} else {
echo "<b>M17 Link Manager</b>\n";
echo "<table>\n<tr><th>Command Output</th></tr>\n<tr><td>";
echo "Somthing wrong with your input, (Neither Link nor Unlink Sent) - please try again";
echo "</td></tr>\n</table>\n<br />\n";
unset($_POST);
echo '<script type="text/javascript">setTimeout(function() { window.location=window.location;},2000);</script>';
}
if (empty($_POST['m17LinkHost'])) {
echo "<b>M17 Link Manager</b>\n";
echo "<table>\n<tr><th>Command Output</th></tr>\n<tr><td>";
echo "Somthing wrong with your input, (No target specified) - please try again";
echo "</td></tr>\n</table>\n<br />\n";
unset($_POST);
echo '<script type="text/javascript">setTimeout(function() { window.location=window.location;},2000);</script>';
}
if (isset($remoteCommand)) {
echo "<b>M17 Link Manager</b>\n";
echo "<table>\n<tr><th>Command Output</th></tr>\n<tr><td>";
echo exec($remoteCommand);
echo "</td></tr>\n</table>\n<br />\n";
echo '<script type="text/javascript">setTimeout(function() { window.location=window.location;},2000);</script>';
}
} else {
// Output HTML
?>
<b>M17 Link Manager</b>
<form action="//<?php echo htmlentities($_SERVER['HTTP_HOST']).htmlentities($_SERVER['PHP_SELF']); ?>" method="post">
<table>
<tr>
<th width="150"><a class="tooltip" href="#">Reflector<span><b>Reflector</b></span></a></th>
<th width="150"><a class="tooltip" href="#">Link / Un-Link<span><b>Link / Un-Link</b></span></a></th>
<th width="150"><a class="tooltip" href="#">Action<span><b>Action</b></span></a></th>
</tr>
<tr>
<td>
<select name="m17LinkHost">
<?php
$m17Hosts = fopen("/usr/local/etc/M17Hosts.txt", "r");
if (isset($configm17gateway['Network']['Startup'])) { $testM17Host = explode("_", $configm17gateway['Network']['Startup'])[0]; } else { $testM17Host = ""; }
if ($testM17Host == "") { echo " <option value=\"none\" selected=\"selected\">None</option>\n"; }
else { echo " <option value=\".$testM17Host.\">None</option>\n"; }
while (!feof($m17Hosts)) {
$m17HostsLine = fgets($m17Hosts);
$m17Host = preg_split('/\s+/', $m17HostsLine);
if ((strpos($m17Host[0], '#') === FALSE ) && ($m17Host[0] != '')) {
if ($testM17Host == $m17Host[0]) { echo " <option value=\"$m17Host[0]\" selected=\"selected\">$m17Host[0]</option>\n"; }
else { echo " <option value=\"$m17Host[0]\">$m17Host[0]</option>\n"; }
}
}
fclose($m17Hosts);
if (file_exists('/root/M17Hosts.txt')) {
$m17Hosts2 = fopen("/root/M17Hosts.txt", "r");
while (!feof($m17Hosts2)) {
$m17HostsLine2 = fgets($m17Hosts2);
$m17Host2 = preg_split('/\s+/', $m17HostsLine2);
if ((strpos($m17Host2[0], '#') === FALSE ) && ($m17Host2[0] != '')) {
if ($testM17Host == $m17Host2[0]) { echo " <option value=\"$m17Host2[0]\" selected=\"selected\">$m17Host2[0]</option>\n"; }
else { echo " <option value=\"$m17Host2[0]\">$m17Host2[0]</option>\n"; }
}
}
fclose($m17Hosts2);
}
?>
</select>
<select name="m17LinkRoom">
<?php if (isset($configm17gateway['Network']['Startup'])) { echo "<option value=\"".substr($configm17gateway['Network']['Startup'], -1)."\" selected=\"selected\">".substr($configm17gateway['Network']['Startup'], -1)."</option>"; } ?>
<option>A</option>
<option>B</option>
<option>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>
<input type="radio" name="Link" value="LINK" checked="checked" />Link
<input type="radio" name="Link" value="UNLINK" />UnLink
</td>
<td>
<input type="submit" name="m17MgrSubmit" value="Request Change" />
</td>
</tr>
</table>
</form>
<br />
<?php
}
}
}
}
?>
+114
View File
@@ -0,0 +1,114 @@
<?php
if ($_SERVER["PHP_SELF"] == "/admin/index.php") { // Stop this working outside of the admin page
include_once $_SERVER['DOCUMENT_ROOT'].'/config/config.php'; // MMDVMDash Config
include_once $_SERVER['DOCUMENT_ROOT'].'/mmdvmhost/tools.php'; // MMDVMDash Tools
include_once $_SERVER['DOCUMENT_ROOT'].'/mmdvmhost/functions.php'; // MMDVMDash Functions
include_once $_SERVER['DOCUMENT_ROOT'].'/config/language.php'; // Translation Code
// Check if NXDN is Enabled
$testMMDVModeNXDN = getConfigItem("NXDN Network", "Enable", $mmdvmconfigs);
if ( $testMMDVModeNXDN == 1 ) {
//Load the nxdngateway config file
$nxdnGatewayConfigFile = '/etc/nxdngateway';
if (fopen($nxdnGatewayConfigFile,'r')) { $confignxdngateway = parse_ini_file($nxdnGatewayConfigFile, true); }
// Check that the remote is enabled
if ( $confignxdngateway['Remote Commands']['Enable'] == 1 ) {
$remotePort = $confignxdngateway['Remote Commands']['Port'];
if (!empty($_POST) && isset($_POST["nxdnMgrSubmit"])) {
// Handle Posted Data
if (preg_match('/[^A-Za-z0-9]/',$_POST['nxdnLinkHost'])) { unset ($_POST['nxdnLinkHost']);}
if ($_POST["Link"] == "LINK") {
if ($_POST['nxdnLinkHost'] == "none") {
$remoteCommand = "cd /var/log/pi-star && sudo /usr/local/bin/RemoteCommand ".$remotePort." TalkGroup unlink";
} else {
$remoteCommand = "cd /var/log/pi-star && sudo /usr/local/bin/RemoteCommand ".$remotePort." TalkGroup ".$_POST['nxdnLinkHost'];
}
} elseif ($_POST["Link"] == "UNLINK") {
$remoteCommand = "cd /var/log/pi-star && sudo /usr/local/bin/RemoteCommand ".$remotePort." TalkGroup unlink";
} else {
echo "<b>NXDN Link Manager</b>\n";
echo "<table>\n<tr><th>Command Output</th></tr>\n<tr><td>";
echo "Somthing wrong with your input, (Neither Link nor Unlink Sent) - please try again";
echo "</td></tr>\n</table>\n<br />\n";
unset($_POST);
echo '<script type="text/javascript">setTimeout(function() { window.location=window.location;},2000);</script>';
}
if (empty($_POST['nxdnLinkHost'])) {
echo "<b>NXDN Link Manager</b>\n";
echo "<table>\n<tr><th>Command Output</th></tr>\n<tr><td>";
echo "Somthing wrong with your input, (No target specified) - please try again";
echo "</td></tr>\n</table>\n<br />\n";
unset($_POST);
echo '<script type="text/javascript">setTimeout(function() { window.location=window.location;},2000);</script>';
}
if (isset($remoteCommand)) {
echo "<b>NXDN Link Manager</b>\n";
echo "<table>\n<tr><th>Command Output</th></tr>\n<tr><td>";
echo exec($remoteCommand);
echo "</td></tr>\n</table>\n<br />\n";
echo '<script type="text/javascript">setTimeout(function() { window.location=window.location;},2000);</script>';
}
} else {
// Output HTML
?>
<b>NXDN Link Manager</b>
<form action="//<?php echo htmlentities($_SERVER['HTTP_HOST']).htmlentities($_SERVER['PHP_SELF']); ?>" method="post">
<table>
<tr>
<th width="150"><a class="tooltip" href="#">Reflector<span><b>Reflector</b></span></a></th>
<th width="150"><a class="tooltip" href="#">Link / Un-Link<span><b>Link / Un-Link</b></span></a></th>
<th width="150"><a class="tooltip" href="#">Action<span><b>Action</b></span></a></th>
</tr>
<tr>
<td>
<select name="nxdnLinkHost">
<?php
$nxdnHosts = fopen("/usr/local/etc/NXDNHosts.txt", "r");
if (isset($confignxdngateway['Network']['Static'])) { $testNXDNHost = $confignxdngateway['Network']['Static']; } else { $testNXDNHost = ""; }
if ($testNXDNHost == "") { echo " <option value=\"none\" selected=\"selected\">None</option>\n"; }
else { echo " <option value=\"none\">None</option>\n"; }
if ($testNXDNHost == "10") { echo " <option value=\"10\" selected=\"selected\">10 - Parrot</option>\n"; }
else { echo " <option value=\"10\">10 - Parrot</option>\n"; }
while (!feof($nxdnHosts)) {
$nxdnHostsLine = fgets($nxdnHosts);
$nxdnHost = preg_split('/\s+/', $nxdnHostsLine);
if ((strpos($nxdnHost[0], '#') === FALSE ) && ($nxdnHost[0] != '')) {
if ($testNXDNHost == $nxdnHost[0]) { echo " <option value=\"$nxdnHost[0]\" selected=\"selected\">$nxdnHost[0] - $nxdnHost[1]</option>\n"; }
else { echo " <option value=\"$nxdnHost[0]\">$nxdnHost[0] - $nxdnHost[1]</option>\n"; }
}
}
fclose($nxdnHosts);
if (file_exists('/usr/local/etc/NXDNHostsLocal.txt')) {
$nxdnHosts2 = fopen("/usr/local/etc/NXDNHostsLocal.txt", "r");
while (!feof($nxdnHosts2)) {
$nxdnHostsLine2 = fgets($nxdnHosts2);
$nxdnHost2 = preg_split('/\s+/', $nxdnHostsLine2);
if ((strpos($nxdnHost2[0], '#') === FALSE ) && ($nxdnHost2[0] != '')) {
if ($testNXDNHost == $nxdnHost2[0]) { echo " <option value=\"$nxdnHost2[0]\" selected=\"selected\">$nxdnHost2[0] - $nxdnHost2[1]</option>\n"; }
else { echo " <option value=\"$nxdnHost2[0]\">$nxdnHost2[0] - $nxdnHost2[1]</option>\n"; }
}
}
fclose($nxdnHosts2);
}
?>
</select>
</td>
<td>
<input type="radio" name="Link" value="LINK" checked="checked" />Link
<input type="radio" name="Link" value="UNLINK" />UnLink
</td>
<td>
<input type="submit" name="nxdnMgrSubmit" value="Request Change" />
</td>
</tr>
</table>
</form>
<br />
<?php
}
}
}
}
?>
+115
View File
@@ -0,0 +1,115 @@
<?php
if ($_SERVER["PHP_SELF"] == "/admin/index.php") { // Stop this working outside of the admin page
include_once $_SERVER['DOCUMENT_ROOT'].'/config/config.php'; // MMDVMDash Config
include_once $_SERVER['DOCUMENT_ROOT'].'/mmdvmhost/tools.php'; // MMDVMDash Tools
include_once $_SERVER['DOCUMENT_ROOT'].'/mmdvmhost/functions.php'; // MMDVMDash Functions
include_once $_SERVER['DOCUMENT_ROOT'].'/config/language.php'; // Translation Code
// Check if P25 is Enabled
$testMMDVModeP25 = getConfigItem("P25 Network", "Enable", $mmdvmconfigs);
if ( $testMMDVModeP25 == 1 ) {
//Load the p25gateway config file
$p25GatewayConfigFile = '/etc/p25gateway';
if (fopen($p25GatewayConfigFile,'r')) { $configp25gateway = parse_ini_file($p25GatewayConfigFile, true); }
// Check that the remote is enabled
if ( $configp25gateway['Remote Commands']['Enable'] == 1 ) {
$remotePort = $configp25gateway['Remote Commands']['Port'];
if (!empty($_POST) && isset($_POST["p25MgrSubmit"])) {
// Handle Posted Data
if (preg_match('/[^A-Za-z0-9]/',$_POST['p25LinkHost'])) { unset ($_POST['p25LinkHost']);}
if ($_POST["Link"] == "LINK") {
if ($_POST['p25LinkHost'] == "none") {
$remoteCommand = "cd /var/log/pi-star && sudo /usr/local/bin/RemoteCommand ".$remotePort." TalkGroup 9999";
} else {
$remoteCommand = "cd /var/log/pi-star && sudo /usr/local/bin/RemoteCommand ".$remotePort." TalkGroup ".$_POST['p25LinkHost'];
}
} elseif ($_POST["Link"] == "UNLINK") {
$remoteCommand = "cd /var/log/pi-star && sudo /usr/local/bin/RemoteCommand ".$remotePort." TalkGroup 9999";
} else {
echo "<b>P25 Link Manager</b>\n";
echo "<table>\n<tr><th>Command Output</th></tr>\n<tr><td>";
echo "Somthing wrong with your input, (Neither Link nor Unlink Sent) - please try again";
echo "</td></tr>\n</table>\n<br />\n";
unset($_POST);
echo '<script type="text/javascript">setTimeout(function() { window.location=window.location;},2000);</script>';
}
if (empty($_POST['p25LinkHost'])) {
echo "<b>P25 Link Manager</b>\n";
echo "<table>\n<tr><th>Command Output</th></tr>\n<tr><td>";
echo "Somthing wrong with your input, (No target specified) - please try again";
echo "</td></tr>\n</table>\n<br />\n";
unset($_POST);
echo '<script type="text/javascript">setTimeout(function() { window.location=window.location;},2000);</script>';
}
if (isset($remoteCommand)) {
echo "<b>P25 Link Manager</b>\n";
echo "<table>\n<tr><th>Command Output</th></tr>\n<tr><td>";
echo exec($remoteCommand);
echo "</td></tr>\n</table>\n<br />\n";
echo '<script type="text/javascript">setTimeout(function() { window.location=window.location;},2000);</script>';
}
} else {
// Output HTML
?>
<b>P25 Link Manager</b>
<form action="//<?php echo htmlentities($_SERVER['HTTP_HOST']).htmlentities($_SERVER['PHP_SELF']); ?>" method="post">
<table>
<tr>
<th width="150"><a class="tooltip" href="#">Reflector<span><b>Reflector</b></span></a></th>
<th width="150"><a class="tooltip" href="#">Link / Un-Link<span><b>Link / Un-Link</b></span></a></th>
<th width="150"><a class="tooltip" href="#">Action<span><b>Action</b></span></a></th>
</tr>
<tr>
<td>
<select name="p25LinkHost">
<?php
if (isset($configp25gateway['Network']['Startup'])) { $testP25Host = $configp25gateway['Network']['Startup']; }
elseif (isset($configp25gateway['Network']['Static'])) { $testP25Host = $configp25gateway['Network']['Static']; }
else { $testP25Host = "none"; }
if ($testP25Host == "") { echo " <option value=\"none\" selected=\"selected\">None</option>\n"; }
else { echo " <option value=\"none\">None</option>\n"; }
if ($testP25Host == "10") { echo " <option value=\"10\" selected=\"selected\">10 - Parrot</option>\n"; }
else { echo " <option value=\"10\">10 - Parrot</option>\n"; }
$p25Hosts = fopen("/usr/local/etc/P25Hosts.txt", "r");
while (!feof($p25Hosts)) {
$p25HostsLine = fgets($p25Hosts);
$p25Host = preg_split('/\s+/', $p25HostsLine);
if ((strpos($p25Host[0], '#') === FALSE ) && ($p25Host[0] != '')) {
if ($testP25Host == $p25Host[0]) { echo " <option value=\"$p25Host[0]\" selected=\"selected\">$p25Host[0] - $p25Host[1]</option>\n"; }
else { echo " <option value=\"$p25Host[0]\">$p25Host[0] - $p25Host[1]</option>\n"; }
}
}
fclose($p25Hosts);
if (file_exists('/usr/local/etc/P25HostsLocal.txt')) {
$p25Hosts2 = fopen("/usr/local/etc/P25HostsLocal.txt", "r");
while (!feof($p25Hosts2)) {
$p25HostsLine2 = fgets($p25Hosts2);
$p25Host2 = preg_split('/\s+/', $p25HostsLine2);
if ((strpos($p25Host2[0], '#') === FALSE ) && ($p25Host2[0] != '')) {
if ($testP25Host == $p25Host2[0]) { echo " <option value=\"$p25Host2[0]\" selected=\"selected\">$p25Host2[0] - $p25Host2[1]</option>\n"; }
else { echo " <option value=\"$p25Host2[0]\">$p25Host2[0] - $p25Host2[1]</option>\n"; }
}
}
fclose($p25Hosts2);
}
?>
</td>
<td>
<input type="radio" name="Link" value="LINK" checked="checked" />Link
<input type="radio" name="Link" value="UNLINK" />UnLink
</td>
<td>
<input type="submit" name="p25MgrSubmit" value="Request Change" />
</td>
</tr>
</table>
</form>
<br />
<?php
}
}
}
}
?>
+137
View File
@@ -0,0 +1,137 @@
<?php
// Most of the work here contributed by geeks4hire (Ben Horan)
// Skyper decode by Andy Taylor (MW0MWZ)
include_once $_SERVER['DOCUMENT_ROOT'].'/config/config.php'; // MMDVMDash Config
include_once $_SERVER['DOCUMENT_ROOT'].'/mmdvmhost/tools.php'; // MMDVMDash Tools
include_once $_SERVER['DOCUMENT_ROOT'].'/mmdvmhost/functions.php'; // MMDVMDash Functions
include_once $_SERVER['DOCUMENT_ROOT'].'/config/language.php'; // Translation Code
// Function to reverse the ROT1 used for Skyper
function un_rot($message) {
$output = "";
$messageTextArray = str_split($message);
// ROT -1
foreach($messageTextArray as $asciiChar) {
$asciiAsInt = ord($asciiChar);
$convretedAsciiAsInt = $asciiAsInt -1;
$convertedAsciiChar = chr($convretedAsciiAsInt);
$output .= $convertedAsciiChar;
}
// Return the clear text
return $output;
}
// Function to handle Skyper Messages
function skyper($message, $pocsagric) {
$output = "";
$messageTextArray = str_split($message);
if ($pocsagric == "0002504") { // Skyper OTA TimeSync Messages
$output = "[Skyper OTA Time] ".$message;
return $output;
}
if ($pocsagric == "0004512") { // Skyper Rubric Index
if (isset($messageTextArray[0])) { // This is hard coded to 1 for rubric index
unset($messageTextArray[0]);
}
if (isset($messageTextArray[1])) { // Rubric Number
$skyperRubric = ord($messageTextArray[1]) - 31;
unset($messageTextArray[1]);
}
if (isset($messageTextArray[2])) { // Message number, hard coded to 10 for Rubric Index
unset($messageTextArray[2]);
}
if (count($messageTextArray) >= 1) { // Check to see if there is a message to decode
$output = "[Skyper Index Rubric:$skyperRubric] ".un_rot(implode($messageTextArray));
}
else {
$output = "[Skyper Index Rubric:$skyperRubric] No Name";
}
return $output;
}
if ($pocsagric == "0004520") { // Skyper Message
if (isset($messageTextArray[0])) { // Rubric Number
$skyperRubric = ord($messageTextArray[0]) - 31;
unset($messageTextArray[0]);
}
if (isset($messageTextArray[1])) { // Message number
$skyperMsgNr = ord($messageTextArray[1]) - 32;
unset($messageTextArray[1]);
}
if (count($messageTextArray) >= 1) { // Check to see if there is a message to decode
$output = "[Skyper Rubric:$skyperRubric Msg:$skyperMsgNr] ".un_rot(implode($messageTextArray));
}
else {
$output = "[Skyper Rubric:$skyperRubric] No Message";
}
return $output;
}
}
?>
<b><?php echo $lang['pocsag_list'];?></b>
<table>
<tr>
<th><a class="tooltip" href="#"><?php echo $lang['time'];?> (<?php echo date('T')?>)<span><b>Time in <?php echo date('T')?> time zone</b></span></a></th>
<th><a class="tooltip" href="#"><?php echo $lang['pocsag_timeslot'];?><span><b>Message Mode</b></span></a></th>
<th><a class="tooltip" href="#"><?php echo $lang['target'];?><span><b>RIC / CapCode of the receiving Pager</b></span></a></th>
<th><a class="tooltip" href="#"><?php echo $lang['pocsag_msg'];?><span><b>Message contents</b></span></a></th>
</tr>
<?php
foreach ($logLinesDAPNETGateway as $dapnetMessageLine) {
$dapnetMessageArr = explode(" ", $dapnetMessageLine);
$dapnetMessageTxtArr = explode('"', $dapnetMessageLine);
$utc_time = $dapnetMessageArr["0"]." ".substr($dapnetMessageArr["1"],0,-4);
$utc_tz = new DateTimeZone('UTC');
$local_tz = new DateTimeZone(date_default_timezone_get ());
$dt = new DateTime($utc_time, $utc_tz);
$dt->setTimeZone($local_tz);
$local_time = $dt->format('H:i:s M jS');
$pocsag_timeslot = $dapnetMessageArr["6"];
$pocsag_ric = str_replace(',', '', $dapnetMessageArr["8"]);
// Fix incorrectly truncated strings containing double quotes
unset($dapnetMessageTxtArr[0]);
if (count($dapnetMessageTxtArr) > 2) {
unset($dapnetMessageTxtArr[count($dapnetMessageTxtArr)]);
$pocsag_msg = implode('"', $dapnetMessageTxtArr);
} else {
$pocsag_msg = $dapnetMessageTxtArr[1];
}
// Decode Skyper Messages
if ( ($pocsag_ric == "0004520") || ($pocsag_ric == "0004512") || ($pocsag_ric == "0002504") ) {
$pocsag_msg = skyper($pocsag_msg, $pocsag_ric);
}
// Formatting long messages without spaces
if (strpos($pocsag_msg, ' ') == 0 && strlen($pocsag_msg) >= 45) {
$pocsag_msg = wordwrap($pocsag_msg, 45, ' ', true);
}
// Sanitise the data before displaying the HTML
if (isset($local_time)) { $local_time = htmlspecialchars($local_time, ENT_QUOTES, 'UTF-8'); }
if (isset($pocsag_timeslot)) { $pocsag_timeslot = htmlspecialchars($pocsag_timeslot, ENT_QUOTES, 'UTF-8'); }
if (isset($pocsag_ric)) { $pocsag_ric = htmlspecialchars($pocsag_ric, ENT_QUOTES, 'UTF-8'); }
if (isset($pocsag_msg)) { $pocsag_msg = htmlspecialchars($pocsag_msg, ENT_QUOTES, 'UTF-8'); }
?>
<tr>
<td style="width: 140px; vertical-align: top; text-align: left;"><?php echo $local_time; ?></td>
<td style="width: 70px; vertical-align: top; text-align: center;"><?php echo "Slot ".$pocsag_timeslot; ?></td>
<td style="width: 70px; vertical-align: top; text-align: center;"><?php echo $pocsag_ric; ?></td>
<td style="width: max-content; vertical-align: top; text-align: left; word-wrap: break-word; white-space: normal !important;"><?php echo $pocsag_msg; ?></td>
</tr>
<?php
} // foreach
?>
</table>

Some files were not shown because too many files have changed in this diff Show More