<?php
// PHP File Explorer with Add/Delete Functionality
// Security: Define the root directory to prevent directory traversal attacks
$rootDir = __DIR__; // Changed from __DIR__ . '/apps' to __DIR__ (current directory)
$currentDir = isset($_GET['dir']) ? $_GET['dir'] : '';
// Configure your domain - CHANGE THIS to your actual domain
$baseDomain= 'http://108.61.85.3'; // Change this to your domain
$webPath = ''; // Changed from '/apps' to '' (root web path)
// Sanitize the directory path
$currentDir = str_replace(['../', '..\\'], '', $currentDir);
$fullPath = realpath($rootDir . '/' . $currentDir);
// Security check: Ensure we're still within the root directory
if (!$fullPath || strpos($fullPath, realpath($rootDir)) !== 0) {
$fullPath = realpath($rootDir);
$currentDir = '';
}
$message = '';
$messageType = '';
// Handle form submissions (create, upload, delete)
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['action'])) {
switch ($_POST['action']) {
case 'create_file':
$fileName = trim($_POST['file_name']);
if ($fileName && !preg_match('/[<>:"|?*]/', $fileName)) {
$newFilePath = $fullPath . '/' . $fileName;
if (!file_exists($newFilePath)) {
if (file_put_contents($newFilePath, '') !== false) {
setPermissions($newFilePath);
$message = "File '$fileName' created successfully with 777 permissions!";
$messageType = 'success';
} else {
$message = "Failed to create file '$fileName'.";
$messageType = 'error';
}
} else {
$message = "File '$fileName' already exists.";
$messageType = 'error';
}
} else {
$message = "Invalid file name.";
$messageType = 'error';
}
break;
case 'create_folder':
$folderName = trim($_POST['folder_name']);
if ($folderName && !preg_match('/[<>:"|?*]/', $folderName)) {
$newFolderPath = $fullPath . '/' . $folderName;
if (!file_exists($newFolderPath)) {
umask(0000); // Ensure proper permissions for new directories
if (mkdir($newFolderPath, 0777)) {
setPermissions($newFolderPath);
$message = "Folder '$folderName' created successfully with 777 permissions!";
$messageType = 'success';
} else {
$message = "Failed to create folder '$folderName'.";
$messageType = 'error';
}
} else {
$message = "Folder '$folderName' already exists.";
$messageType = 'error';
}
} else {
$message = "Invalid folder name.";
$messageType = 'error';
}
break;
case 'upload_file':
if (isset($_FILES['file']) && $_FILES['file']['error'] === UPLOAD_ERR_OK) {
$fileName = basename($_FILES['file']['name']);
$targetPath = $fullPath . '/' . $fileName;
if (move_uploaded_file($_FILES['file']['tmp_name'], $targetPath)) {
setPermissions($targetPath);
$message = "File '$fileName' uploaded successfully with 777 permissions!";
$messageType = 'success';
} else {
$message = "Failed to upload file '$fileName'.";
$messageType = 'error';
}
} else {
$message = "No file selected or upload error.";
$messageType = 'error';
}
break;
case 'delete_items':
if (isset($_POST['selected_items']) && is_array($_POST['selected_items'])) {
$deletedCount = 0;
foreach ($_POST['selected_items'] as $item) {
$itemPath = $fullPath . '/' . basename($item);
if (file_exists($itemPath)) {
if (is_dir($itemPath)) {
if (rmdir_recursive($itemPath)) {
$deletedCount++;
}
} else {
if (unlink($itemPath)) {
$deletedCount++;
}
}
}
}
if ($deletedCount > 0) {
$message = "$deletedCount item(s) deleted successfully!";
$messageType = 'success';
} else {
$message = "No items were deleted.";
$messageType = 'error';
}
} else {
$message = "No items selected for deletion.";
$messageType = 'error';
}
break;
}
}
}
// Recursive directory removal function
function rmdir_recursive($dir) {
if (!is_dir($dir)) return false;
$files = array_diff(scandir($dir), ['.', '..']);
foreach ($files as $file) {
$filePath = $dir . '/' . $file;
if (is_dir($filePath)) {
rmdir_recursive($filePath);
} else {
unlink($filePath);
}
}
return rmdir($dir);
}
// Helper function to ensure proper permissions
function setPermissions($path) {
if (file_exists($path)) {
chmod($path, 0777);
// Also try to set umask to ensure new files get proper permissions
umask(0000);
return true;
}
return false;
}
// Get directory contents
$items = [];
if (is_dir($fullPath)) {
$files = scandir($fullPath);
foreach ($files as $file) {
if ($file !== '.' && $file !== '..') {
$filePath = $fullPath . '/' . $file;
$relativePath = $currentDir ? $currentDir . '/' . $file : $file;
$items[] = [
'name' => $file,
'path' => $relativePath,
'is_dir' => is_dir($filePath),
'size' => is_file($filePath) ? filesize($filePath) : 0,
'modified' => filemtime($filePath)
];
}
}
}
// Sort items: directories first, then files, alphabetically
usort($items, function($a, $b) {
if ($a['is_dir'] !== $b['is_dir']) {
return $b['is_dir'] - $a['is_dir'];
}
return strcasecmp($a['name'], $b['name']);
});
// Helper function to format file sizes
function formatBytes($size, $precision = 2) {
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
for ($i = 0; $size > 1024 && $i < count($units) - 1; $i++) {
$size /= 1024;
}
return round($size, $precision) . ' ' . $units[$i];
}
// Helper function to get breadcrumb navigation
function getBreadcrumbs($currentDir) {
$breadcrumbs = [['name' => 'root', 'path' => '']]; // Changed from 'apps' to 'root'
if ($currentDir) {
$parts = explode('/', $currentDir);
$path = '';
foreach ($parts as $part) {
$path .= ($path ? '/' : '') . $part;
$breadcrumbs[] = ['name' => $part, 'path' => $path];
}
}
return $breadcrumbs;
}
// Helper function to check if file is viewable
function isViewableFile($filename) {
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
$editableExts = ['txt', 'php', 'html', 'css', 'js', 'json', 'xml', 'md', 'py', 'java', 'cpp', 'c', 'h', 'sql', 'yml', 'yaml'];
$imageExts = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg'];
return in_array($ext, $editableExts) || in_array($ext, $imageExts);
}
// Helper function to check if file can be opened in browser
function isWebAccessible($filename) {
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
$webExts = ['html', 'htm', 'php', 'css', 'js', 'json', 'xml', 'txt', 'svg', 'jpg', 'jpeg', 'png', 'gif', 'pdf'];
return in_array($ext, $webExts);
}
// Helper function to generate web URL for file
function getWebUrl($currentDir, $filename, $baseDomain, $webPath) {
$path = $currentDir ? $currentDir . '/' . $filename : $filename;
return $baseDomain . $webPath . '/' . $path;
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PHP File Explorer</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background-color: #f5f5f5;
color: #333;
line-height: 1.6;
}
.container {
max-width: 1400px;
margin: 0 auto;
padding: 20px;
}
.navigation {
background: white;
padding: 15px 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.breadcrumb {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.breadcrumb a {
color: #3498db;
text-decoration: none;
padding: 4px 8px;
border-radius: 4px;
transition: background-color 0.2s;
}
.breadcrumb a:hover {
background-color: #ecf0f1;
}
.breadcrumb span {
color: #7f8c8d;
}
.message {
padding: 12px 20px;
border-radius: 6px;
margin-bottom: 20px;
}
.message.success {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.message.error {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.main-content {
display: block;
}
.file-browser {
background: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.toolbar {
padding: 20px;
border-top: 1px solid #ecf0f1;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 12px;
}
.toolbar-section {
display: flex;
flex-direction: column;
gap: 8px;
}
.toolbar-section h3 {
color: #2c3e50;
font-size: 14px;
margin-bottom: 5px;
}
.form-group {
display: flex;
gap: 8px;
align-items: center;
}
.form-group input[type="text"],
.form-group input[type="file"] {
flex: 1;
padding: 6px 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 13px;
}
.btn {
padding: 6px 12px;
border: none;
border-radius: 4px;
font-size: 13px;
cursor: pointer;
transition: background-color 0.2s;
text-decoration: none;
display: inline-block;
text-align: center;
}
.btn-primary {
background-color: #3498db;
color: white;
}
.btn-primary:hover {
background-color: #2980b9;
}
.btn-danger {
background-color: #e74c3c;
color: white;
}
.btn-danger:hover {
background-color: #c0392b;
}
.btn:disabled {
background-color: #bdc3c7;
cursor: not-allowed;
}
.file-list {
max-height: 500px;
overflow-y: auto;
}
.file-item {
display: flex;
align-items: center;
padding: 8px 20px;
border-bottom: 1px solid #ecf0f1;
transition: background-color 0.2s;
}
.file-item:hover {
background-color: #f8f9fa;
}
.file-item:last-child {
border-bottom: none;
}
.file-checkbox {
margin-right: 8px;
}
.file-icon {
width: 20px;
height: 20px;
margin-right: 8px;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
}
.file-name {
flex: 1;
min-width: 0;
}
.file-name a {
color: #2c3e50;
text-decoration: none;
font-weight: 500;
}
.file-name a:hover {
color: #3498db;
}
.file-actions {
margin-left: 8px;
}
.file-actions a {
color: #7f8c8d;
text-decoration: none;
font-size: 12px;
padding: 2px 6px;
border-radius: 3px;
margin-left: 4px;
}
.file-actions a:hover {
background-color: #ecf0f1;
}
.file-info {
display: flex;
gap: 15px;
color: #7f8c8d;
font-size: 12px;
margin-left: auto;
}
.file-size {
min-width: 60px;
text-align: right;
}
.empty-folder {
padding: 40px 20px;
text-align: center;
color: #7f8c8d;
font-style: italic;
}
.select-all {
padding: 10px 20px;
background: #f8f9fa;
border-bottom: 1px solid #ecf0f1;
}
.footer {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
text-align: center;
}
.footer h1 {
color: #2c3e50;
margin: 0;
}
@media (max-width: 1024px) {
.toolbar {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="container">
<!-- Navigation at the top -->
<div class="navigation">
<nav class="breadcrumb">
<?php
$breadcrumbs = getBreadcrumbs($currentDir);
foreach ($breadcrumbs as $index => $crumb):
?>
<?php if ($index > 0): ?>
<span>/</span>
<?php endif; ?>
<?php if ($index === count($breadcrumbs) - 1): ?>
<span><?= htmlspecialchars($crumb['name']) ?></span>
<?php else: ?>
<a href="?dir=<?= urlencode($crumb['path']) ?>">
<?= htmlspecialchars($crumb['name']) ?>
</a>
<?php endif; ?>
<?php endforeach; ?>
</nav>
</div>
<?php if ($message): ?>
<div class="message <?= $messageType ?>">
<?= htmlspecialchars($message) ?>
</div>
<?php endif; ?>
<!-- File Explorer -->
<div class="main-content">
<div class="file-browser">
<div class="file-list">
<?php if (empty($items)): ?>
<div class="empty-folder">
This folder is empty
</div>
<?php else: ?>
<div class="select-all">
<label>
<input type="checkbox" id="selectAll"> Select All
</label>
</div>
<?php foreach ($items as $item): ?>
<div class="file-item">
<input type="checkbox" class="file-checkbox" name="selected_items[]"
value="<?= htmlspecialchars($item['name']) ?>" form="deleteForm">
<div class="file-icon">
<?= $item['is_dir'] ? '📁' : '📄' ?>
</div>
<div class="file-name">
<?php if ($item['is_dir']): ?>
<a href="?dir=<?= urlencode($item['path']) ?>">
<?= htmlspecialchars($item['name']) ?>
</a>
<?php else: ?>
<span><?= htmlspecialchars($item['name']) ?></span>
<?php endif; ?>
</div>
<?php if (!$item['is_dir']): ?>
<div class="file-actions">
<?php if (isViewableFile($item['name'])): ?>
<a href="editor.php?dir=<?= urlencode($currentDir) ?>&view=<?= urlencode($item['name']) ?>">Edit</a>
<?php endif; ?>
<?php if (isWebAccessible($item['name'])): ?>
<a href="<?= getWebUrl($currentDir, $item['name'], $baseDomain, $webPath) ?>" target="_blank">Open</a>
<?php endif; ?>
</div>
<?php endif; ?>
<div class="file-info">
<div class="file-size">
<?= $item['is_dir'] ? '-' : formatBytes($item['size']) ?>
</div>
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
<div class="toolbar">
<div class="toolbar-section">
<h3>📄 Create File</h3>
<form method="post" class="form-group">
<input type="hidden" name="action" value="create_file">
<input type="text" name="file_name" placeholder="filename.ext" required>
<button type="submit" class="btn btn-primary">Create</button>
</form>
</div>
<div class="toolbar-section">
<h3>📁 Create Folder</h3>
<form method="post" class="form-group">
<input type="hidden" name="action" value="create_folder">
<input type="text" name="folder_name" placeholder="Folder name" required>
<button type="submit" class="btn btn-primary">Create</button>
</form>
</div>
<div class="toolbar-section">
<h3>📄 Upload File</h3>
<form method="post" enctype="multipart/form-data" class="form-group">
<input type="hidden" name="action" value="upload_file">
<input type="file" name="file" required>
<button type="submit" class="btn btn-primary">Upload</button>
</form>
</div>
<div class="toolbar-section">
<h3>🗑️ Delete Selected</h3>
<form method="post" id="deleteForm" class="form-group">
<input type="hidden" name="action" value="delete_items">
<button type="submit" class="btn btn-danger" id="deleteBtn" disabled
onclick="return confirm('Are you sure you want to delete the selected items? This action cannot be undone.')">
Delete
</button>
</form>
</div>
</div>
</div>
</div>
<!-- Title at the bottom -->
<div class="footer">
<h1>📁 File Explorer</h1>
</div>
</div>
<script>
// Handle select all functionality
const selectAllCheckbox = document.getElementById('selectAll');
const fileCheckboxes = document.querySelectorAll('.file-checkbox');
const deleteBtn = document.getElementById('deleteBtn');
function updateDeleteButton() {
const checkedBoxes = document.querySelectorAll('.file-checkbox:checked');
deleteBtn.disabled = checkedBoxes.length === 0;
}
if (selectAllCheckbox) {
selectAllCheckbox.addEventListener('change', function() {
fileCheckboxes.forEach(checkbox => {
checkbox.checked = this.checked;
});
updateDeleteButton();
});
}
fileCheckboxes.forEach(checkbox => {
checkbox.addEventListener('change', function() {
const allChecked = Array.from(fileCheckboxes).every(cb => cb.checked);
const noneChecked = Array.from(fileCheckboxes).every(cb => !cb.checked);
if (selectAllCheckbox) {
selectAllCheckbox.checked = allChecked;
selectAllCheckbox.indeterminate = !allChecked && !noneChecked;
}
updateDeleteButton();
});
});
// Initial state
updateDeleteButton();
</script>
</body>
</html>