content= // // RESPONSE // {"ok":true,"error":null} // {"ok":false,"error":"POST only"|"File not permitted"|"Syntax error: …"|"Failed to write file"} // // DEPENDS ON // include/config.php vv_get_conf_files(), vv_write_conf_raw(), CONF_DIR // ═══════════════════════════════════════════════════════════════════════════════════════════════ header('Content-Type: application/json'); require_once dirname(__DIR__) . '/include/config.php'; if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['ok' => false, 'error' => 'POST only']); exit; } $file = trim($_POST['file'] ?? ''); $content = $_POST['content'] ?? ''; // Must be an allowed file for this host $allowed = vv_get_conf_files(); if (!$file || !in_array($file, $allowed)) { echo json_encode(['ok' => false, 'error' => 'File not permitted']); exit; } // Every script sources these. A syntax error here takes the whole system down, so the // candidate is parsed before it is allowed to replace a working file. $check = tempnam(sys_get_temp_dir(), 'vvconf'); if ($check !== false) { file_put_contents($check, $content); $out = []; $rc = 0; exec('bash -n ' . escapeshellarg($check) . ' 2>&1', $out, $rc); @unlink($check); if ($rc !== 0) { $msg = implode(' ', array_filter(array_map('trim', $out))); echo json_encode([ 'ok' => false, 'error' => 'Syntax error: ' . str_replace($check, $file, $msg ?: 'conf does not parse'), ]); exit; } } $ok = vv_write_conf_raw($file, $content); echo json_encode(['ok' => $ok, 'error' => $ok ? null : 'Failed to write file']);