.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/.) * 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 require_once('../config/language.php'); //Load the Pi-Star Release file $pistarReleaseConfig = '/etc/pistar-release'; $configPistarRelease = array(); $configPistarRelease = parse_ini_file($pistarReleaseConfig, true); //Load the Version Info require_once('../config/version.php'); ?> Pi-Star - Digital Voice Dashboard - Expert Editor
// 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 if($_POST) { $data = $_POST; //update ini file, call function update_ini_file($data, $filepath); } //this is the function going to update your ini file function update_ini_file($data, $filepath) { $content = ""; //parse the ini file to get the sections //parse the ini file using default parse_ini_file() PHP function $parsed_ini = parse_ini_file($filepath, true); foreach($data as $section=>$values) { // UnBreak special cases $section = str_replace("_", " ", $section); $content .= "[".$section."]\n"; //append the values foreach($values as $key=>$value) { if ($section == "DMR Network" && $key == "Password" && $value) { $value = str_replace('"', "", $value); $content .= $key."=\"".$value."\"\n"; } elseif ($section == "DMR Network" && $key == "Options" && $value) { $value = str_replace('"', "", $value); $content .= $key."=\"".$value."\"\n"; } elseif ($section == "DMR Network" && $key == "Options" && !$value) { $content .= $key."= \n"; } elseif ($value == '') { $content .= $key."=none\n"; } else { $content .= $key."=".$value."\n"; } } $content .= "\n"; } //write it into file if (!$handle = fopen($filepath, 'w')) { return false; } $success = fwrite($handle, $content); fclose($handle); // Updates complete - install the working file back to the // proper location. L-5: atomic install replaces the prior // cp + chmod + chown triplet (which the tightened // /etc/sudoers.d/pistar-dashboard rejects on the bare // chmod/chown against /etc/ — only the install pattern // is allowlisted there). 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 exec('sudo systemctl restart mmdvmhost.service'); // Reload the daemon return $success; } //parse the ini file using default parse_ini_file() PHP function $parsed_ini = parse_ini_file($filepath, true); echo '
'."\n"; echo csrf_field_html()."\n"; foreach($parsed_ini as $section=>$values) { // INI section / key / value all come from /etc/mmdvmhost. // Operator-trusted in principle, but the M-3 audit class // means a value with a literal `"` or `<` (legitimate INI // content — e.g. "Options=foo=1,bar" or a callsign with a // space) breaks out of `value="…"` attributes and renders // as HTML. // // htmlspecialchars(ENT_QUOTES) preserves round-trip safety: // on display: `"` -> `"`, `<` -> `<` etc. // browser decode (form parse): `"` -> `"` again. // POST: the raw byte gets back to the save handler. // save handler at line ~108: writes `key=value` to INI // verbatim — no further escaping or unescaping. // So values like `Options="foo=1,bar"` go in, get displayed // as `Options="foo=1,bar"`, the browser still shows // `"foo=1,bar"` in the input field, and re-saving produces // a byte-identical INI line. $sectionHtml = htmlspecialchars((string)$section, ENT_QUOTES, 'UTF-8'); // keep the section as hidden text so we can update once the form submitted echo "\n"; echo "\n"; echo "\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 "\n"; } elseif (($key == "Display") && ($value == '')) { echo "\n"; } else { echo "\n"; } } echo "
$sectionHtml
$keyHtml
$keyHtml
$keyHtml
\n"; echo ''."\n"; echo "
\n"; } echo "
"; ?>