WebApp production ver

This commit is contained in:
2025-04-14 10:26:56 +02:00
parent 8ac8b3ad5b
commit ec5191a7d3
865 changed files with 192011 additions and 2 deletions

View File

@@ -0,0 +1,35 @@
<?php
require '../../vendor/autoload.php';
use phpseclib3\Net\SFTP;
$defPath = $_SESSION['defPath'] ?? '/';
// SFTP Configuration
$host = 'localhost';
$username = 'UNAME';
$password = 'PSWD';
$defaultPath = $defPath;
function initializeSFTP($host, $username, $password) {
$sftp = new SFTP($host);
if (!$sftp->login($username, $password)) {
die('Login Failed');
}
return $sftp;
}
function normalizePath($path) {
$parts = array_filter(explode('/', $path), fn($part) => $part !== '' && $part !== '.');
$stack = [];
foreach ($parts as $part) {
if ($part === '..') {
array_pop($stack);
} else {
$stack[] = $part;
}
}
return '/' . implode('/', $stack);
}
$sftp = initializeSFTP($host, $username, $password);
$currentPath = normalizePath($defaultPath);

View File

@@ -0,0 +1,41 @@
<?php
// Include this at the top to see potential errors
// Comment out in production
ini_set('display_errors', 1);
error_reporting(E_ALL);
require 'config.php';
$sftp = initializeSFTP($host, $username, $password);
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'createDir') {
$currentPath = isset($_POST['currentPath']) ? normalizePath($_POST['currentPath']) : $defaultPath;
if (!isset($_POST['dirName'])) {
http_response_code(400);
echo "Directory name is required";
exit;
}
$dirName = $_POST['dirName'];
$dirName = preg_replace('/[^\w\-\.]/', '_', $dirName);
if (strpos($currentPath, $defaultPath) !== 0) {
$currentPath = $defaultPath;
}
$newDirPath = $currentPath . '/' . $dirName;
if ($sftp->mkdir($newDirPath)) {
echo "Directory created successfully!";
} else {
http_response_code(500);
echo "Failed to create directory.";
}
exit;
}
http_response_code(400);
echo "Invalid request";
exit;

View File

@@ -0,0 +1,68 @@
<?php
// Include this at the top to see potential errors
// Comment out in production
ini_set('display_errors', 1);
error_reporting(E_ALL);
session_start();
if(!isset($_SESSION['uname'])){
header("location: ../index.html");
session_destroy();
exit;
}
if(!isset($_SESSION["upPer"]) || $_SESSION["upPer"] != true) {
http_response_code(403);
echo "You don't have permission to create files.";
exit;
}
require 'config.php';
$sftp = initializeSFTP($host, $username, $password);
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'createFile') {
$currentPath = isset($_POST['currentPath']) ? normalizePath($_POST['currentPath']) : $defaultPath;
if (!isset($_POST['fileName'])) {
http_response_code(400);
echo "File name is required";
exit;
}
$fileName = $_POST['fileName'];
if (strpos($fileName, '.') === false) {
http_response_code(400);
echo "File name must include an extension (e.g., .txt, .html, .php)";
exit;
}
$fileName = preg_replace('/[^\w\-\.]/', '_', $fileName);
if (strpos($currentPath, $defaultPath) !== 0) {
$currentPath = $defaultPath;
}
$newFilePath = $currentPath . '/' . $fileName;
$tempFile = tempnam(sys_get_temp_dir(), 'new_file_');
file_put_contents($tempFile, '');
if ($sftp->put($newFilePath, $tempFile)) {
@unlink($tempFile);
echo json_encode(['success' => true, 'filePath' => $newFilePath]);
} else {
@unlink($tempFile);
http_response_code(500);
echo json_encode(['success' => false, 'message' => 'Failed to create file.']);
}
exit;
}
http_response_code(400);
echo "Invalid request";
exit;

View File

@@ -0,0 +1,128 @@
html, body {
height: 100%;
margin: 0;
overflow-x: hidden;
overflow-y: auto;
}
.custom-container {
display: flex;
flex-direction: column;
min-height: 100vh;
margin: 0;
padding: 0;
max-width: 100%;
overflow: hidden;
}
section.row {
flex: 1;
margin: 0;
width: 100%;
justify-content: center;
}
article.col-8 {
max-width: 1600px;
}
@media (max-width: 1800px) {
article.col-8 {
flex: 0 0 auto;
width: 95%;
}
}
@media (max-width: 768px) {
.custom-container {
height: auto;
min-height: initial;
}
section.row {
flex: 0 0 auto;
}
footer {
margin-top: 20px;
position: relative;
}
body {
height: auto;
min-height: initial;
}
article {
padding-bottom: 20px;
}
}
table {
width: 100%;
border-collapse: collapse;
margin: 20px 0;
table-layout: fixed;
}
th, td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
word-wrap: break-word;
overflow-wrap: break-word;
}
.dropzone {
width: 100%;
padding: 20px;
border: 2px dashed #007bff;
border-radius: 10px;
text-align: center;
margin-bottom: 20px;
cursor: pointer;
box-sizing: border-box;
}
.dropzone.dragover {
background-color: #e0f7fa;
}
.action-buttons {
margin-bottom: 15px;
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 10px;
}
.action-buttons button {
margin-right: 0;
}
body {
min-width: 500px;
}
.hover-effect {
transition: opacity 0.3s ease;
}
.hover-effect:hover {
opacity: 0.8;
}
.theme-light .dark-logo {
display: none;
}
.theme-dark .light-logo {
display: none;
}
footer {
margin-top: auto;
padding: 20px;
border-top: 1px solid #ddd;
width: 100%;
}

View File

@@ -0,0 +1,54 @@
.editor-container {
border: 1px solid #ccc;
border-radius: 4px;
margin-bottom: 20px;
position: relative;
}
#editor {
width: 100%;
min-height: 400px;
font-family: monospace;
padding: 10px;
white-space: pre;
overflow: auto;
resize: vertical;
tab-size: 4;
-moz-tab-size: 4;
padding-left: 55px; /* Make room for line numbers */
}
.line-numbers {
position: absolute;
left: 0;
top: 0;
width: 45px;
text-align: right;
padding: 10px 5px 10px 0;
border-right: 1px solid #ccc;
color: #999;
user-select: none;
font-family: monospace;
overflow: hidden;
z-index: 1;
background-color: #f8f9fa; /* Light mode default */
}
[data-bs-theme="dark"] .line-numbers {
background-color: #212529; /* Dark mode background */
border-right-color: #495057; /* Darker border for dark mode */
color: #6c757d; /* Lighter text for dark mode */
}
.header-container {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.readonly-notice {
color: #dc3545;
font-weight: bold;
margin-left: 10px;
}

View File

@@ -0,0 +1,86 @@
html, body {
height: 100%;
margin: 0;
overflow-x: hidden;
}
.custom-container {
display: flex;
flex-direction: column;
min-height: 100vh;
margin: 0;
padding: 0;
max-width: 100%;
}
.media-container {
display: flex;
justify-content: center;
align-items: center;
max-width: 100%;
max-height: 80vh;
margin: 0 auto;
overflow: visible;
flex-direction: column;
}
.media-container img {
max-width: 100%;
max-height: 70vh;
object-fit: contain;
}
.media-container video {
max-width: 100%;
max-height: 70vh;
}
.media-container audio {
width: 100%;
max-width: 600px;
margin: 20px 0;
}
.controls {
margin: 20px 0;
text-align: center;
}
footer {
margin-top: auto;
padding: 20px;
border-top: 1px solid #ddd;
width: 100%;
}
.hover-effect {
transition: opacity 0.3s ease;
}
.hover-effect:hover {
opacity: 0.8;
}
.theme-light .dark-logo {
display: none;
}
.theme-dark .light-logo {
display: none;
}
.file-info table {
table-layout: fixed;
word-wrap: break-word;
}
@media (max-width: 576px) {
.file-info table {
width: 95% !important;
}
.file-info th {
width: 40%;
}
.file-info td {
width: 60%;
}
}

View File

@@ -0,0 +1,39 @@
<?php
require 'config.php';
$sftp = initializeSFTP($host, $username, $password);
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete'])) {
$itemToDelete = $_POST['delete'];
$parentDir = dirname($itemToDelete);
if ($parentDir === '/' || $parentDir === '.') {
$parentDir = $defaultPath;
}
if ($sftp->is_dir($itemToDelete)) {
function deleteFolder($sftp, $folderPath) {
$items = $sftp->nlist($folderPath);
foreach ($items as $item) {
if ($item === '.' || $item === '..') continue;
$itemPath = $folderPath . '/' . $item;
if ($sftp->is_dir($itemPath)) {
deleteFolder($sftp, $itemPath);
} else {
$sftp->delete($itemPath);
}
}
return $sftp->rmdir($folderPath);
}
$success = deleteFolder($sftp, $itemToDelete);
} else {
$success = $sftp->delete($itemToDelete);
}
header("Location: index.php?path=" . urlencode($parentDir));
exit;
}
header("Location: index.php");
exit;

View File

@@ -0,0 +1,217 @@
<?php
set_time_limit(900); // 15 minutes max execution time
// Include this at the top to see potential errors
// Comment out in production
ini_set('display_errors', 0);
error_reporting(0);
ob_start();
try {
require 'config.php';
$logFile = __DIR__ . '/download_log.txt';
file_put_contents($logFile, "Download started: " . date('Y-m-d H:i:s') . "\n", FILE_APPEND);
$sftp = null;
try {
$sftp = initializeSFTP($host, $username, $password);
file_put_contents($logFile, "SFTP connection established\n", FILE_APPEND);
} catch (Exception $e) {
file_put_contents($logFile, "SFTP connection failed: " . $e->getMessage() . "\n", FILE_APPEND);
throw new Exception("Failed to connect to SFTP server: " . $e->getMessage());
}
function zipFolderRecursive($sftp, $remoteBasePath, $currentPath, $zip, $logFile, &$tempFiles) {
file_put_contents($logFile, "Processing directory: $currentPath\n", FILE_APPEND);
$files = $sftp->nlist($currentPath);
if ($files === false) {
file_put_contents($logFile, "Failed to list directory contents for: $currentPath\n", FILE_APPEND);
throw new Exception("Failed to list directory contents for: $currentPath");
}
file_put_contents($logFile, "Found " . count($files) . " items in $currentPath\n", FILE_APPEND);
foreach ($files as $file) {
if ($file == '.' || $file == '..') continue;
$fullRemotePath = rtrim($currentPath, '/') . '/' . $file;
$baseDirName = basename($remoteBasePath);
$relPathFromBase = substr($fullRemotePath, strlen(dirname($remoteBasePath)) + 1);
file_put_contents($logFile, "Processing: $fullRemotePath (relative: $relPathFromBase)\n", FILE_APPEND);
$isDir = $sftp->is_dir($fullRemotePath);
if ($isDir) {
file_put_contents($logFile, "Found subdirectory: $fullRemotePath\n", FILE_APPEND);
$zip->addEmptyDir($relPathFromBase);
zipFolderRecursive($sftp, $remoteBasePath, $fullRemotePath, $zip, $logFile, $tempFiles);
} else {
$localTempFile = tempnam(sys_get_temp_dir(), 'sftp');
$tempFiles[] = $localTempFile;
file_put_contents($logFile, "Downloading to temp file: $localTempFile\n", FILE_APPEND);
$downloadStart = time();
$downloadSuccess = $sftp->get($fullRemotePath, $localTempFile);
$downloadTime = time() - $downloadStart;
if ($downloadSuccess) {
$fileSize = filesize($localTempFile);
file_put_contents($logFile, "Download successful ($downloadTime seconds), size: $fileSize bytes\n", FILE_APPEND);
$zip->addFile($localTempFile, $relPathFromBase);
} else {
file_put_contents($logFile, "Failed to download file after $downloadTime seconds\n", FILE_APPEND);
}
}
}
}
function zipFolder($sftp, $folderPath, $zipFilePath, $logFile) {
file_put_contents($logFile, "Starting to zip folder recursively: $folderPath\n", FILE_APPEND);
$zip = new ZipArchive();
if ($zip->open($zipFilePath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== TRUE) {
file_put_contents($logFile, "Failed to create zip archive\n", FILE_APPEND);
throw new Exception("Unable to create the zip file.");
}
$tempFiles = [];
try {
zipFolderRecursive($sftp, $folderPath, $folderPath, $zip, $logFile, $tempFiles);
file_put_contents($logFile, "Closing zip file\n", FILE_APPEND);
$zipSuccess = $zip->close();
foreach ($tempFiles as $tempFile) {
if (file_exists($tempFile)) {
@unlink($tempFile);
}
}
if ($zipSuccess) {
file_put_contents($logFile, "Zip created successfully, size: " . filesize($zipFilePath) . " bytes\n", FILE_APPEND);
return true;
} else {
file_put_contents($logFile, "Failed to create zip\n", FILE_APPEND);
return false;
}
} catch (Exception $e) {
foreach ($tempFiles as $tempFile) {
if (file_exists($tempFile)) {
@unlink($tempFile);
}
}
file_put_contents($logFile, "Error during zip creation: " . $e->getMessage() . "\n", FILE_APPEND);
throw $e;
}
}
if (($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['file'])) ||
($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['file']))) {
$file = $_SERVER['REQUEST_METHOD'] === 'POST' ? $_POST['file'] : $_GET['file'];
file_put_contents($logFile, "Requested file: $file\n", FILE_APPEND);
$fileExists = $sftp->file_exists($file);
$isDir = $sftp->is_dir($file);
file_put_contents($logFile, "File exists: " . ($fileExists ? "Yes" : "No") . "\n", FILE_APPEND);
file_put_contents($logFile, "Is directory: " . ($isDir ? "Yes" : "No") . "\n", FILE_APPEND);
if (!$fileExists && !$isDir) {
throw new Exception("File not found: $file");
}
if ($isDir) {
$zipFilePath = tempnam(sys_get_temp_dir(), 'folder_') . '.zip';
file_put_contents($logFile, "Creating zip at: $zipFilePath\n", FILE_APPEND);
if (zipFolder($sftp, $file, $zipFilePath, $logFile)) {
if (file_exists($zipFilePath) && filesize($zipFilePath) > 0) {
file_put_contents($logFile, "Sending zip file to browser, size: " . filesize($zipFilePath) . " bytes\n", FILE_APPEND);
while (ob_get_level()) {
ob_end_clean();
}
header('Content-Type: application/zip');
header('Content-Disposition: attachment; filename="' . basename($file) . '.zip"');
header('Content-Length: ' . filesize($zipFilePath));
header('Cache-Control: no-cache, must-revalidate');
header('Pragma: no-cache');
readfile($zipFilePath);
file_put_contents($logFile, "Download completed\n", FILE_APPEND);
@unlink($zipFilePath);
exit;
} else {
throw new Exception("Failed to create zip file or zip file is empty");
}
} else {
throw new Exception("Failed to create zip archive");
}
} else {
$localFile = basename($file);
file_put_contents($logFile, "Downloading single file: $localFile\n", FILE_APPEND);
$tempFile = tempnam(sys_get_temp_dir(), 'file_');
$downloadStart = time();
$downloadSuccess = $sftp->get($file, $tempFile);
$downloadTime = time() - $downloadStart;
file_put_contents($logFile, "Download " . ($downloadSuccess ? "successful" : "failed") . " ($downloadTime seconds)\n", FILE_APPEND);
if ($downloadSuccess) {
$fileSize = filesize($tempFile);
file_put_contents($logFile, "Downloaded file size: $fileSize bytes\n", FILE_APPEND);
if ($fileSize > 0) {
while (ob_get_level()) {
ob_end_clean();
}
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $localFile . '"');
header('Content-Length: ' . $fileSize);
header('Cache-Control: no-cache, must-revalidate');
header('Pragma: no-cache');
readfile($tempFile);
file_put_contents($logFile, "Download completed\n", FILE_APPEND);
@unlink($tempFile);
exit;
} else {
@unlink($tempFile);
throw new Exception("Downloaded file is empty");
}
} else {
@unlink($tempFile);
throw new Exception("Failed to download file from SFTP server");
}
}
} else {
throw new Exception("Invalid request method or missing file parameter");
}
} catch (Exception $e) {
$errorMessage = "Error: " . $e->getMessage();
file_put_contents($logFile, $errorMessage . "\n", FILE_APPEND);
while (ob_get_level()) {
ob_end_clean();
}
header("HTTP/1.1 500 Internal Server Error");
echo $errorMessage;
}
?>

267
Web/content/ftp/index.php Normal file
View File

@@ -0,0 +1,267 @@
<?php
session_start();
if(!isset($_SESSION['uname'])){
header("location: ../../index.php");
session_destroy();
exit;
}
// Include this at the top to see potential errors
// Comment out in production
ini_set('display_errors', 1);
error_reporting(E_ALL);
require 'config.php';
$sftp = initializeSFTP($host, $username, $password);
$currentPath = isset($_GET['path']) ? normalizePath($_GET['path']) : $defaultPath;
if (strpos($currentPath, $defaultPath) !== 0) {
$currentPath = $defaultPath;
}
if (!$sftp->chdir($currentPath)) {
die("Failed to navigate to the selected folder: $currentPath");
}
$items = $sftp->nlist();
$imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg'];
$videoExtensions = ['mp4', 'webm', 'ogg', 'mov', 'avi', 'mkv'];
$audioExtensions = ['mp3', 'wav', 'm4a', 'flac', 'aac'];
$editableExtensions = ['txt', 'html', 'css', 'js', 'php', 'xml', 'json', 'md', 'csv', 'log', 'ini', 'conf', 'sh', 'bat', 'py', 'rb', 'java', 'c', 'cpp', 'h', 'hpp'];
?>
<!DOCTYPE html>
<html lang="en">
<head>
<title>FTP</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="icon" href="../../img/favicon.ico" type="image/x-icon">
<link rel="stylesheet" href="../../css/bootstrap.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<link rel="stylesheet" href="css/index.css">
<script src="../../js/bootstrap.bundle.js"></script>
</head>
<body class="text-center">
<div class="d-flex justify-content-end p-3">
<button id="themeToggle" class="btn btn-sm theme-toggle">
<i class="bi"></i>
<span id="themeText"></span>
</button>
</div>
<div class="custom-container">
<header class="row border-bottom m-5">
<h1>USB RAID Array</h1>
<div class="mb-3 p-3">
<a href="../logout.php" class="btn btn-danger">Logout</a>
<a href="../changepassword.php" class="btn btn-warning">Change Password</a>
<?php if (isset($_SESSION["admin"]) && $_SESSION["admin"] == true) { ?>
<a href="../adminpanel.php" class="btn btn-primary">Admin Panel</a>
<?php } ?>
</div>
</header>
<section class="row">
<article class="col-8 border border-2 border-primary rounded p-2">
<div class="col">
<h4>Current Path: <?= htmlspecialchars($currentPath) ?></h4>
</div>
<div class="col">
<!-- Action Buttons -->
<div class="action-buttons">
<?php if (isset($_SESSION["upPer"]) && $_SESSION["upPer"] == true) { ?>
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#createFileModal">Create File</button>
<button type="button" class="btn btn-success" onclick="document.getElementById('fileInput').click()">Upload Files</button>
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#createDirModal">Create Directory</button>
<button type="button" class="btn btn-info" onclick="document.getElementById('dirInput').click()">Upload Directory</button>
<input type="file" id="fileInput" multiple style="display: none;" onchange="handleFileSelect(event)">
<input type="file" id="dirInput" webkitdirectory directory multiple style="display: none;" onchange="handleFileSelect(event)">
<?php } ?>
</div>
<!-- Create Directory Dialog -->
<div class="modal fade" id="createDirModal" tabindex="-1" aria-labelledby="createDirModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="createDirModalLabel">Create New Directory</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label for="dirName" class="form-label">Directory Name</label>
<input type="text" class="form-control" id="dirName" placeholder="Enter directory name">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" onclick="createDirectory()">Create</button>
</div>
</div>
</div>
</div>
<!-- Create File Dialog -->
<div class="modal fade" id="createFileModal" tabindex="-1" aria-labelledby="createFileModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="createFileModalLabel">Create New File</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label for="fileName" class="form-label">File Name (with extension)</label>
<input type="text" class="form-control" id="fileName" placeholder="example.txt">
<div class="form-text">Supported extensions: .txt, .html, .css, .js, .php, etc.</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" onclick="createFile()">Create</button>
</div>
</div>
</div>
</div>
<?php if (isset($_SESSION["upPer"]) && $_SESSION["upPer"] == true) { ?>
<div class="dropzone" ondragover="handleDragOver(event)" ondragleave="handleDragLeave(event)" ondrop="handleDrop(event)">
Drop files or folders here to upload
</div>
<?php } ?>
<table>
<thead>
<tr>
<th>Name</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php
$directories = [];
$files = [];
foreach ($items as $item) {
if ($item === '.' || $item === '..') continue;
if ($sftp->is_dir($item)) {
$directories[] = $item;
} else {
$files[] = $item;
}
}
// Sort directories and files alphabetically
sort($directories, SORT_STRING | SORT_FLAG_CASE);
sort($files, SORT_STRING | SORT_FLAG_CASE);
if ($currentPath !== $defaultPath) : ?>
<tr>
<td colspan="2"><a class="text-danger" href="?path=<?= urlencode(dirname($currentPath)) ?>"><b>.. (Go Back)</b></a></td>
</tr>
<?php endif;
foreach ($directories as $directory) : ?>
<tr>
<td>
<a href="?path=<?= urlencode($currentPath . '/' . $directory) ?>"><?= htmlspecialchars($directory) ?>/</a>
</td>
<td>
<?php if (isset($_SESSION["delPer"]) && $_SESSION["delPer"] == true) : ?>
<form method="POST" action="delete.php" style="display:inline;">
<input type="hidden" name="delete" value="<?= htmlspecialchars($currentPath . '/' . $directory) ?>">
<button type="submit" class="btn btn-danger" onclick="confirmDelete(event)">Delete</button>
</form>
<?php endif; ?>
<?php if (isset($_SESSION["downPer"]) && $_SESSION["downPer"] == true) : ?>
<form method="POST" action="download.php" style="display:inline;">
<input type="hidden" name="file" value="<?= htmlspecialchars($currentPath . '/' . $directory) ?>">
<button type="submit" class="btn btn-success">Download</button>
</form>
<?php endif; ?>
</td>
</tr>
<?php endforeach;
foreach ($files as $file) : ?>
<?php
$fileExtension = pathinfo($file, PATHINFO_EXTENSION);
$isImage = in_array($fileExtension, $imageExtensions);
$isVideo = in_array($fileExtension, $videoExtensions);
$isAudio = in_array($fileExtension, $audioExtensions);
$isMedia = $isImage || $isVideo || $isAudio;
$isEditable = in_array($fileExtension, $editableExtensions);
?>
<tr>
<td>
<?php if ($isMedia): ?>
<a class="text-info-emphasis" href="view.php?file=<?= urlencode($currentPath . '/' . $file) ?>">
<?= htmlspecialchars($file) ?>
</a>
<?php if ($isImage): ?>
<span class="badge bg-success rounded-pill">Image</span>
<?php elseif ($isVideo): ?>
<span class="badge bg-primary rounded-pill">Video</span>
<?php elseif ($isAudio): ?>
<span class="badge bg-info rounded-pill">Audio</span>
<?php endif; ?>
<?php elseif ($isEditable): ?>
<a class="text-warning-emphasis" href="open.php?file=<?= urlencode($currentPath . '/' . $file) ?>">
<?= htmlspecialchars($file) ?>
</a>
<span class="badge bg-secondary rounded-pill"><?= htmlspecialchars($fileExtension) ?></span>
<?php else: ?>
<?= htmlspecialchars($file) ?>
<span class="badge bg-light text-dark rounded-pill"><?= htmlspecialchars($fileExtension) ?></span>
<?php endif; ?>
</td>
<td>
<?php if (isset($_SESSION["delPer"]) && $_SESSION["delPer"] == true) : ?>
<form method="POST" action="delete.php" style="display:inline;">
<input type="hidden" name="delete" value="<?= htmlspecialchars($currentPath . '/' . $file) ?>">
<button type="submit" class="btn btn-danger" onclick="confirmDelete(event)">Delete</button>
</form>
<?php endif; ?>
<?php if (isset($_SESSION["downPer"]) && $_SESSION["downPer"] == true) : ?>
<form method="POST" action="download.php" style="display:inline;">
<input type="hidden" name="file" value="<?= htmlspecialchars($currentPath . '/' . $file) ?>">
<button type="submit" class="btn btn-success">Download</button>
</form>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</article>
<footer class="d-flex flex-column justify-content-center align-items-center p-3 border-top gap-3 m-3">
<span class="text-muted">Developed by Michal Sedlák</span>
<div class="d-flex gap-3">
<a href="https://github.com/michalcz10/USB-RAID-pole" class="text-decoration-none" target="_blank" rel="noopener noreferrer">
<img src="../../img/GitHub_Logo.png" alt="GitHub Logo" class="img-fluid hover-effect light-logo" style="height: 32px;">
<img src="../../img/GitHub_Logo_White.png" alt="GitHub Logo" class="img-fluid hover-effect dark-logo" style="height: 32px;">
</a>
<a href="https://app.freelo.io/public/shared-link-view/?a=81efbcb4df761b3f29cdc80855b41e6d&b=4519c717f0729cc8e953af661e9dc981" class="text-decoration-none" target="_blank" rel="noopener noreferrer">
<img src="../../img/freelo-logo-rgb.png" alt="Freelo Logo" class="img-fluid hover-effect light-logo" style="height: 24px;">
<img src="../../img/freelo-logo-rgb-on-dark.png" alt="Freelo Logo" class="img-fluid hover-effect dark-logo" style="height: 24px;">
</a>
</div>
</footer>
</div>
<script>
// Pass PHP variables to JavaScript
var currentPath = "<?php echo $currentPath; ?>";
</script>
<script src="js/index.js"></script>
</body>
</html>

278
Web/content/ftp/js/index.js Normal file
View File

@@ -0,0 +1,278 @@
function handleDragOver(event) {
event.preventDefault();
event.currentTarget.classList.add('dragover');
}
function handleDragLeave(event) {
event.currentTarget.classList.remove('dragover');
}
function handleDrop(event) {
event.preventDefault();
event.currentTarget.classList.remove('dragover');
if (event.dataTransfer.items) {
const items = event.dataTransfer.items;
processDroppedItems(items);
} else {
const files = event.dataTransfer.files;
uploadFiles(files);
}
}
function processDroppedItems(items) {
const allFiles = [];
let pendingItems = 0;
function handleEntry(entry, path = '') {
if (entry.isFile) {
pendingItems++;
entry.file(file => {
Object.defineProperty(file, 'webkitRelativePath', {
value: path + file.name
});
allFiles.push(file);
pendingItems--;
if (pendingItems === 0) {
uploadFiles(allFiles);
}
});
} else if (entry.isDirectory) {
const reader = entry.createReader();
readEntries(reader, path + entry.name + '/');
}
}
function readEntries(reader, path) {
pendingItems++;
reader.readEntries(entries => {
if (entries.length > 0) {
for (const entry of entries) {
handleEntry(entry, path);
}
readEntries(reader, path);
}
pendingItems--;
if (pendingItems === 0 && allFiles.length > 0) {
uploadFiles(allFiles);
}
});
}
for (let i = 0; i < items.length; i++) {
const item = items[i];
if (item.kind !== 'file') continue;
const entry = item.webkitGetAsEntry ? item.webkitGetAsEntry() : item.getAsEntry();
if (entry) {
handleEntry(entry);
}
}
if (pendingItems === 0 && allFiles.length === 0) {
alert('No valid files or directories found.');
}
}
function handleFileSelect(event) {
const files = event.target.files;
uploadFiles(files);
}
function uploadFiles(files) {
if (!files || files.length === 0) {
alert('No files selected for upload.');
return;
}
const formData = new FormData();
formData.append('currentPath', currentPath);
let filesAdded = 0;
let directories = new Set();
for (const file of files) {
const relativePath = file.webkitRelativePath || '';
if (relativePath) {
const parts = relativePath.split('/');
let currentPath = '';
for (let i = 0; i < parts.length - 1; i++) {
currentPath += (i > 0 ? '/' : '') + parts[i];
if (currentPath) {
directories.add(currentPath);
}
}
}
formData.append('files[]', file);
formData.append('paths[]', relativePath);
filesAdded++;
}
if (directories.size > 0) {
formData.append('create_dirs', JSON.stringify(Array.from(directories)));
}
if (filesAdded === 0) {
alert('No valid files selected for upload.');
return;
}
const uploadStatus = document.createElement('div');
uploadStatus.className = 'alert alert-info';
uploadStatus.textContent = 'Uploading files, please wait...';
document.querySelector('.action-buttons').after(uploadStatus);
const xhr = new XMLHttpRequest();
xhr.open('POST', 'upload.php', true);
xhr.onerror = () => {
uploadStatus.className = 'alert alert-danger';
uploadStatus.textContent = 'Network error occurred during upload.';
setTimeout(() => uploadStatus.remove(), 5000);
};
xhr.timeout = 300000; // 5 minutes
xhr.ontimeout = () => {
uploadStatus.className = 'alert alert-danger';
uploadStatus.textContent = 'Upload timed out. Try with smaller files or fewer files.';
setTimeout(() => uploadStatus.remove(), 5000);
};
xhr.onload = () => {
if (xhr.status === 200) {
uploadStatus.className = 'alert alert-success';
uploadStatus.textContent = 'Upload successful!';
setTimeout(() => {
uploadStatus.remove();
window.location.reload();
}, 1500);
} else {
uploadStatus.className = 'alert alert-danger';
uploadStatus.textContent = 'Upload failed: ' + (xhr.responseText || xhr.statusText);
setTimeout(() => uploadStatus.remove(), 5000);
}
};
xhr.send(formData);
}
function createDirectory() {
const dirName = document.getElementById('dirName').value.trim();
if (!dirName) {
alert('Please enter a directory name.');
return;
}
const formData = new FormData();
formData.append('currentPath', currentPath);
formData.append('dirName', dirName);
formData.append('action', 'createDir');
const xhr = new XMLHttpRequest();
xhr.open('POST', 'createdir.php', true);
xhr.onload = () => {
if (xhr.status === 200) {
alert('Directory created successfully!');
const modal = bootstrap.Modal.getInstance(document.getElementById('createDirModal'));
if (modal) modal.hide();
window.location.reload();
} else {
alert('Failed to create directory: ' + xhr.responseText || xhr.statusText);
}
};
xhr.send(formData);
}
function confirmDelete(event) {
event.preventDefault();
if (confirm("Do you really want to delete this file?")) {
event.target.form.submit();
}
}
function createFile() {
const fileName = document.getElementById('fileName').value.trim();
if (!fileName) {
alert('Please enter a file name.');
return;
}
if (fileName.indexOf('.') === -1) {
alert('Please include a file extension (e.g., .txt, .html, .php)');
return;
}
const formData = new FormData();
formData.append('currentPath', currentPath);
formData.append('fileName', fileName);
formData.append('action', 'createFile');
const xhr = new XMLHttpRequest();
xhr.open('POST', 'createfile.php', true);
xhr.onload = () => {
if (xhr.status === 200) {
try {
const response = JSON.parse(xhr.responseText);
if (response.success) {
alert('File created successfully!');
const modal = bootstrap.Modal.getInstance(document.getElementById('createFileModal'));
if (modal) modal.hide();
window.location.reload();
} else {
alert('Failed to create file: ' + (response.message || 'Unknown error'));
}
} catch (e) {
alert('Error processing response: ' + xhr.responseText);
}
} else {
alert('Failed to create file: ' + xhr.responseText || xhr.statusText);
}
};
xhr.send(formData);
}
document.addEventListener('DOMContentLoaded', function() {
const themeToggle = document.getElementById('themeToggle');
const html = document.documentElement;
const themeText = document.getElementById('themeText');
const themeIcon = themeToggle.querySelector('.bi');
function setTheme(theme) {
html.setAttribute('data-bs-theme', theme);
document.body.classList.remove('theme-light', 'theme-dark');
document.body.classList.add('theme-' + theme);
localStorage.setItem('theme', theme);
if (theme === 'dark') {
themeText.textContent = 'Light Mode';
themeIcon.className = 'bi bi-sun';
themeToggle.classList.remove('btn-dark');
themeToggle.classList.add('btn-light');
} else {
themeText.textContent = 'Dark Mode';
themeIcon.className = 'bi bi-moon';
themeToggle.classList.remove('btn-light');
themeToggle.classList.add('btn-dark');
}
}
const savedTheme = localStorage.getItem('theme');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
if (savedTheme) {
setTheme(savedTheme);
} else {
setTheme(prefersDark ? 'dark' : 'light');
}
themeToggle.addEventListener('click', function() {
const currentTheme = html.getAttribute('data-bs-theme');
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
setTheme(newTheme);
});
});

204
Web/content/ftp/open.php Normal file
View File

@@ -0,0 +1,204 @@
<?php
// Include this at the top to see potential errors
// Comment out in production
ini_set('display_errors', 1);
error_reporting(E_ALL);
session_start();
if(!isset($_SESSION['uname'])){
header("location: ../../index.php");
session_destroy();
exit;
}
require 'config.php';
$sftp = initializeSFTP($host, $username, $password);
$filePath = isset($_GET['file']) ? $_GET['file'] : '';
$content = '';
$fileName = basename($filePath);
$extension = pathinfo($fileName, PATHINFO_EXTENSION);
$editable = false;
$editableExtensions = ['txt', 'html', 'css', 'js', 'php', 'xml', 'json', 'md', 'csv', 'log', 'ini', 'conf', 'sh', 'bat', 'py', 'rb', 'java', 'c', 'cpp', 'h', 'hpp'];
if (!empty($filePath) && $sftp->file_exists($filePath) && !$sftp->is_dir($filePath)) {
if (in_array(strtolower($extension), $editableExtensions)) {
$editable = true;
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['content']) && isset($_SESSION["upPer"]) && $_SESSION["upPer"] == true) {
$newContent = $_POST['content'];
if ($sftp->put($filePath, $newContent)) {
$saveSuccess = true;
} else {
$saveError = "Failed to save changes. Check permissions.";
}
}
$content = $sftp->get($filePath);
if ($content === false) {
$error = "Failed to read file contents.";
}
} else {
$error = "This file type is not supported for editing.";
}
} else {
$error = "File not found or is a directory.";
}
$showLineNumbers = in_array(strtolower($extension), ['php', 'js', 'html', 'css', 'py', 'java', 'c', 'cpp', 'h', 'hpp', 'rb', 'sh', 'xml', 'json']);
$parentDir = dirname($filePath);
?>
<!DOCTYPE html>
<html lang="en">
<head>
<title>Edit File - <?= htmlspecialchars($fileName) ?></title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="icon" href="../../img/favicon.ico" type="image/x-icon">
<link rel="stylesheet" href="../../css/bootstrap.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<link rel="stylesheet" href="css/open.css">
<script src="../../js/bootstrap.bundle.js"></script>
</head>
<body>
<div class="d-flex justify-content-end p-3">
<button id="themeToggle" class="btn btn-sm theme-toggle">
<i class="bi"></i>
<span id="themeText"></span>
</button>
</div>
<div class="container mt-4">
<div class="header-container">
<h1>Edit File: <?= htmlspecialchars($fileName) ?></h1>
<a href="index.php?path=<?= urlencode($parentDir) ?>" class="btn btn-secondary">Back to File List</a>
</div>
<?php if (isset($error)): ?>
<div class="alert alert-danger"><?= $error ?></div>
<?php elseif ($editable): ?>
<?php if (isset($saveSuccess)): ?>
<div class="alert alert-success">File saved successfully!</div>
<?php endif; ?>
<?php if (isset($saveError)): ?>
<div class="alert alert-danger"><?= $saveError ?></div>
<?php endif; ?>
<form method="POST" id="editorForm">
<div class="mb-3">
<div class="d-flex justify-content-between align-items-center mb-2">
<label for="editor" class="form-label">File Content</label>
<?php if (!isset($_SESSION["upPer"]) || $_SESSION["upPer"] != true): ?>
<span class="readonly-notice">Read-only mode (you don't have upload permissions)</span>
<?php endif; ?>
</div>
<div class="editor-container">
<?php if ($showLineNumbers): ?>
<div id="lineNumbers" class="line-numbers"></div>
<?php endif; ?>
<textarea id="editor" name="content" class="form-control" <?= (!isset($_SESSION["upPer"]) || $_SESSION["upPer"] != true) ? 'readonly' : '' ?>><?= htmlspecialchars($content) ?></textarea>
</div>
</div>
<?php if (isset($_SESSION["upPer"]) && $_SESSION["upPer"] == true): ?>
<div class="mb-3">
<button type="submit" class="btn btn-primary">Save Changes</button>
</div>
<?php endif; ?>
</form>
<?php endif; ?>
</div>
<script src="../../js/theme.js"></script>
<script>
function updateLineNumbers() {
const editor = document.getElementById('editor');
const lineNumbers = document.getElementById('lineNumbers');
if (!lineNumbers) return;
const lines = editor.value.split('\n');
const lineCount = lines.length;
let html = '';
for (let i = 1; i <= lineCount; i++) {
html += i + '<br>';
}
lineNumbers.innerHTML = html;
syncLineNumbersHeight();
lineNumbers.scrollTop = editor.scrollTop;
}
function syncLineNumbersHeight() {
const editor = document.getElementById('editor');
const lineNumbers = document.getElementById('lineNumbers');
if (!lineNumbers || !editor) return;
lineNumbers.style.height = editor.clientHeight + 'px';
}
document.addEventListener('DOMContentLoaded', function() {
const editor = document.getElementById('editor');
const lineNumbers = document.getElementById('lineNumbers');
if (editor && lineNumbers) {
setTimeout(() => {
updateLineNumbers();
syncLineNumbersHeight();
}, 0);
editor.addEventListener('input', updateLineNumbers);
editor.addEventListener('keydown', function(e) {
if (e.key === 'Tab') {
e.preventDefault();
const start = this.selectionStart;
const end = this.selectionEnd;
this.value = this.value.substring(0, start) + ' ' + this.value.substring(end);
this.selectionStart = this.selectionEnd = start + 4;
updateLineNumbers();
}
});
editor.addEventListener('scroll', function() {
if (lineNumbers) {
lineNumbers.scrollTop = this.scrollTop;
}
});
editor.addEventListener('mouseup', syncLineNumbersHeight);
const observer = new MutationObserver(function(mutations) {
syncLineNumbersHeight();
});
observer.observe(editor, {
attributes: true,
attributeFilter: ['style']
});
if (typeof ResizeObserver === 'function') {
const resizeObserver = new ResizeObserver(() => {
syncLineNumbersHeight();
});
resizeObserver.observe(editor);
} else {
window.addEventListener('resize', syncLineNumbersHeight);
}
}
});
</script>
</body>
</html>

130
Web/content/ftp/upload.php Normal file
View File

@@ -0,0 +1,130 @@
<?php
// Include this at the top to see potential errors
// Comment out in production
ini_set('display_errors', 1);
error_reporting(E_ALL);
require 'config.php';
use phpseclib3\Net\SFTP;
$sftp = initializeSFTP($host, $username, $password);
$currentPath = isset($_POST['currentPath']) ? normalizePath($_POST['currentPath']) : $defaultPath;
if (strpos($currentPath, $defaultPath) !== 0) {
$currentPath = $defaultPath;
}
function createDirectoryRecursive($sftp, $path) {
if ($sftp->is_dir($path)) {
return true;
}
$parent = dirname($path);
if ($parent != '/' && !$sftp->is_dir($parent)) {
createDirectoryRecursive($sftp, $parent);
}
return $sftp->mkdir($path, 0755);
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['create_dirs'])) {
$directories = json_decode($_POST['create_dirs'], true);
if (is_array($directories)) {
foreach ($directories as $dir) {
$remoteDirPath = $currentPath . '/' . $dir;
createDirectoryRecursive($sftp, $remoteDirPath);
}
}
}
if (!isset($_FILES['files']) || empty($_FILES['files']['name'][0])) {
echo "No files received or file size exceeds PHP limits.";
http_response_code(400);
exit;
}
if ($_FILES['files']['error'][0] !== 0) {
$error = $_FILES['files']['error'][0];
$errorMessage = "Upload error code: $error";
switch ($error) {
case UPLOAD_ERR_INI_SIZE:
$errorMessage = "File exceeds upload_max_filesize directive in php.ini";
break;
case UPLOAD_ERR_FORM_SIZE:
$errorMessage = "File exceeds MAX_FILE_SIZE directive in the HTML form";
break;
case UPLOAD_ERR_PARTIAL:
$errorMessage = "File was only partially uploaded";
break;
case UPLOAD_ERR_NO_FILE:
$errorMessage = "No file was uploaded";
break;
case UPLOAD_ERR_NO_TMP_DIR:
$errorMessage = "Missing a temporary folder";
break;
case UPLOAD_ERR_CANT_WRITE:
$errorMessage = "Failed to write file to disk";
break;
case UPLOAD_ERR_EXTENSION:
$errorMessage = "A PHP extension stopped the file upload";
break;
}
echo $errorMessage;
http_response_code(400);
exit;
}
$uploadStatus = array();
$anySuccess = false;
foreach ($_FILES['files']['tmp_name'] as $index => $tmpName) {
if (empty($tmpName)) continue;
$fileName = $_FILES['files']['name'][$index];
$relativePath = isset($_POST['paths']) && isset($_POST['paths'][$index]) ? $_POST['paths'][$index] : '';
if (!empty($relativePath)) {
$fileName = basename($relativePath);
$dirPart = dirname($relativePath);
if ($dirPart !== '.' && $dirPart !== '') {
$remoteDirPath = $currentPath . '/' . $dirPart;
if (!$sftp->is_dir($remoteDirPath)) {
createDirectoryRecursive($sftp, $remoteDirPath);
}
$remotePath = $remoteDirPath . '/' . $fileName;
} else {
$remotePath = $currentPath . '/' . $fileName;
}
} else {
$remotePath = $currentPath . '/' . $fileName;
}
if ($sftp->put($remotePath, $tmpName, SFTP::SOURCE_LOCAL_FILE)) {
$uploadStatus[] = "Uploaded: " . ($relativePath ? $relativePath : $fileName);
$anySuccess = true;
} else {
$uploadStatus[] = "Failed to upload: " . ($relativePath ? $relativePath : $fileName);
}
}
if ($anySuccess) {
echo implode("\n", $uploadStatus);
exit;
} else {
echo "Failed to upload any files. Please check SFTP connection and permissions.";
http_response_code(500);
exit;
}
}
echo "No files received or invalid request.";
http_response_code(400);
exit;

248
Web/content/ftp/view.php Normal file
View File

@@ -0,0 +1,248 @@
<?php
session_start();
if(!isset($_SESSION['uname'])){
header("location: ../../index.php");
session_destroy();
exit;
}
// Include this at the top to see potential errors
// Comment out in production
ini_set('display_errors', 1);
error_reporting(E_ALL);
ini_set('memory_limit', '512M');
require 'config.php';
$sftp = initializeSFTP($host, $username, $password);
if (!isset($_GET['file'])) {
die("No file specified");
}
$filePath = $_GET['file'];
$fileName = basename($filePath);
$fileExtension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
if (!$sftp->stat($filePath)) {
die("File not found: $filePath");
}
$imageTypes = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg'];
$videoTypes = ['mp4', 'webm', 'ogg', 'mov', 'avi', 'mkv'];
$audioTypes = ['mp3', 'wav', 'ogg', 'm4a', 'flac', 'aac'];
$isImage = in_array($fileExtension, $imageTypes);
$isVideo = in_array($fileExtension, $videoTypes);
$isAudio = in_array($fileExtension, $audioTypes);
if (!$isImage && !$isVideo && !$isAudio) {
die("Unsupported file type");
}
$fileSize = $sftp->stat($filePath)['size'];
$mimeMap = [
'mp4' => 'video/mp4',
'webm' => 'video/mp4',
'ogg' => 'video/mp4',
'mov' => 'video/mp4',
'avi' => 'video/mp4',
'mkv' => 'video/mp4',
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'png' => 'image/png',
'gif' => 'image/gif',
'bmp' => 'image/bmp',
'webp' => 'image/webp',
'svg' => 'image/svg+xml',
'mp3' => 'audio/mpeg',
'wav' => 'audio/wav',
'm4a' => 'audio/mp4',
'flac' => 'audio/flac',
'aac' => 'audio/aac'
];
$mimeType = isset($mimeMap[$fileExtension]) ? $mimeMap[$fileExtension] : 'application/octet-stream';
if (isset($_GET['stream'])) {
$tempFile = tempnam(sys_get_temp_dir(), 'media_');
$start = 0;
$end = $fileSize - 1;
$length = $fileSize;
if (isset($_SERVER['HTTP_RANGE'])) {
$rangeHeader = $_SERVER['HTTP_RANGE'];
$matches = [];
if (preg_match('/bytes=(\d+)-(\d*)/', $rangeHeader, $matches)) {
$start = intval($matches[1]);
if (!empty($matches[2])) {
$end = intval($matches[2]);
}
$length = $end - $start + 1;
header('HTTP/1.1 206 Partial Content');
header('Content-Range: bytes ' . $start . '-' . $end . '/' . $fileSize);
}
}
header("Content-Type: $mimeType");
header("Accept-Ranges: bytes");
header("Content-Length: $length");
while (ob_get_level()) {
ob_end_clean();
}
$chunkSize = 8 * 1024 * 1024; // 8MB chunks for better performance
$currentPosition = $start;
$bytesRemaining = $length;
$tempHandle = fopen($tempFile, 'w+');
while ($bytesRemaining > 0) {
$readSize = min($chunkSize, $bytesRemaining);
$chunkTemp = tempnam(sys_get_temp_dir(), 'chunk_');
if ($sftp->get($filePath, $chunkTemp, $currentPosition, $readSize)) {
$chunkData = file_get_contents($chunkTemp);
echo $chunkData;
unlink($chunkTemp);
$bytesRemaining -= strlen($chunkData);
$currentPosition += strlen($chunkData);
flush();
} else {
error_log("SFTP reading error at position $currentPosition");
break;
}
if (connection_status() != CONNECTION_NORMAL) {
break;
}
}
fclose($tempHandle);
if (file_exists($tempFile)) {
unlink($tempFile);
}
exit;
}
?>
<!DOCTYPE html>
<html lang="en" data-bs-theme="<?= isset($_COOKIE['theme']) ? $_COOKIE['theme'] : 'light' ?>">
<head>
<title>Media Viewer - <?= htmlspecialchars($fileName) ?></title>
<link rel="icon" href="../../img/favicon.ico" type="image/x-icon">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="../../css/bootstrap.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<link rel="stylesheet" href="css/view.css">
<script src="../../js/bootstrap.bundle.js"></script>
</head>
<body class="text-center">
<div class="d-flex justify-content-end p-3">
<button id="themeToggle" class="btn btn-sm theme-toggle">
<i class="bi"></i>
<span id="themeText"></span>
</button>
</div>
<div class="custom-container">
<header class="row border-bottom m-5">
<h1>Media Viewer</h1>
<div class="mb-3 p-3">
<a href="index.php?path=<?= urlencode(dirname($filePath)) ?>" class="btn btn-primary">Back to Files</a>
</div>
</header>
<section class="row">
<article class="col-12">
<div class="media-container position-relative">
<?php if ($isImage): ?>
<img src="view.php?file=<?= urlencode($filePath) ?>&stream=1" alt="<?= htmlspecialchars($fileName) ?>" class="img-fluid">
<?php elseif ($isVideo): ?>
<video id="videoPlayer" controls autoplay playsinline>
<source src="view.php?file=<?= urlencode($filePath) ?>&stream=1" type="<?= $mimeType ?>">
Your browser does not support this video format. Try downloading the file instead.
</video>
<div class="alert alert-warning mt-2">
Note: For best results, use MP4 format (H.264 codec). Other formats may not play correctly in all browsers.
</div>
<?php elseif ($isAudio): ?>
<audio controls autoplay style="width: 80%;">
<source src="view.php?file=<?= urlencode($filePath) ?>&stream=1" type="<?= $mimeType ?>">
Your browser does not support the audio element.
</audio>
<?php endif; ?>
<?php if (isset($_SESSION["downPer"]) && $_SESSION["downPer"] == true) : ?>
<a href="download.php?file=<?= urlencode($filePath) ?>" class="btn btn-success m-3">Download</a>
<?php endif; ?>
</div>
<div class="file-info mt-4">
<h4>File Information</h4>
<table class="table table-bordered w-auto mx-auto text-wrap">
<tr>
<th>File Name</th>
<td class="text-break"><?= htmlspecialchars($fileName) ?></td>
</tr>
<tr>
<th>File Type</th>
<td><?= htmlspecialchars(strtoupper($fileExtension)) ?></td>
</tr>
<tr>
<th>MIME Type</th>
<td class="text-break"><?= htmlspecialchars($mimeType) ?></td>
</tr>
<tr>
<th>File Size</th>
<td><?= formatBytes($fileSize) ?></td>
</tr>
</table>
</div>
</article>
</section>
<footer class="d-flex flex-column justify-content-center align-items-center p-3 border-top gap-3 m-3">
<span class="text-muted">Developed by Michal Sedlák</span>
<div class="d-flex gap-3">
<a href="https://github.com/michalcz10/USB-RAID-pole" class="text-decoration-none" target="_blank" rel="noopener noreferrer">
<img src="../../img/GitHub_Logo.png" alt="GitHub Logo" class="img-fluid hover-effect light-logo" style="height: 32px;">
<img src="../../img/GitHub_Logo_White.png" alt="GitHub Logo" class="img-fluid hover-effect dark-logo" style="height: 32px;">
</a>
<a href="https://app.freelo.io/public/shared-link-view/?a=81efbcb4df761b3f29cdc80855b41e6d&b=4519c717f0729cc8e953af661e9dc981" class="text-decoration-none" target="_blank" rel="noopener noreferrer">
<img src="../../img/freelo-logo-rgb.png" alt="Freelo Logo" class="img-fluid hover-effect light-logo" style="height: 24px;">
<img src="../../img/freelo-logo-rgb-on-dark.png" alt="Freelo Logo" class="img-fluid hover-effect dark-logo" style="height: 24px;">
</a>
</div>
</footer>
</div>
<script src="../../js/theme.js"></script>
</body>
</html>
<?php
function formatBytes($bytes, $precision = 2) {
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
$bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1);
$bytes /= (1 << (10 * $pow));
return round($bytes, $precision) . ' ' . $units[$pow];
}
?>