mirror of
https://github.com/michalcz10/USB-RAID-Array.git
synced 2026-11-11 16:09:03 +00:00
Compare commits
1
Commits
dc452059f3
...
V2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
99b1c96f4b |
@@ -129,6 +129,7 @@ Z kořene repozitáře:
|
||||
php tests/workspace_test.php
|
||||
php tests/serverstats_test.php
|
||||
php tests/remotepaths_test.php
|
||||
php tests/backend_test.php
|
||||
node --check Web/js/theme.js
|
||||
node --check Web/js/ui.js
|
||||
node --check Web/content/ftp/js/index.js
|
||||
@@ -136,6 +137,10 @@ node --check Web/content/ftp/js/index.js
|
||||
|
||||
PHP testy používají testovací data a nepotřebují připojení k databázi ani SSH. Kontrolují vykreslení stránek, navigaci podle role, zpracování statistik a stavy RAID polí. Node.js slouží u uvedených příkazů ke kontrole syntaxe; pro běh aplikace není potřeba.
|
||||
|
||||
Test backendu navíc ověřuje přihlášení, oprávnění operací, hranice vzdálených cest, vytváření prázdných souborů a upload. Spouští handlery v oddělených PHP procesech s testovacími náhradami databáze a SFTP; vyžaduje dostupné `proc_open`.
|
||||
|
||||
V backendu používej popisné názvy lokálních proměnných ve stylu `camelCase`, například `$username`, `$passwordHash`, `$database`, `$statement`, `$directoryPath` a `$canUpload`. Konfigurační proměnné `$DB_*`, `$SFTP_*`, `$SSH_*` a existující klíče databáze, formulářů a session (`uname`, `pswd`, `defPath`, `upPer` apod.) zůstávají kvůli kompatibilitě zachované. Společné kontroly přihlášení, oprávnění a hesel jsou v `Web/includes/auth.php`; práci s POSIX cestami soustřeď do `Web/includes/remotepaths.php`.
|
||||
|
||||
## Časté problémy
|
||||
|
||||
| Problém | Co zkontrolovat |
|
||||
|
||||
@@ -19,7 +19,7 @@ $SFTP_HOST = '172.16.1.11';
|
||||
$SFTP_USER = 'usbraid';
|
||||
$SFTP_PASS = 'usbraid';
|
||||
// Default remote path – uses session value if set
|
||||
$defaultPath = $_SESSION['defPath'] ?? '/';
|
||||
$defaultPath = normalizePath($_SESSION['defPath'] ?? '/');
|
||||
|
||||
// SSH credentials (placeholders)
|
||||
$SSH_HOST = '172.16.1.11';
|
||||
@@ -29,12 +29,12 @@ $SSH_PASS = 'usbraid';
|
||||
/** Helper: Get a MySQLi connection */
|
||||
function getDatabaseConnection(): mysqli {
|
||||
global $DB_HOST, $DB_USER, $DB_PASS, $DB_NAME;
|
||||
$conn = new mysqli($DB_HOST, $DB_USER, $DB_PASS, $DB_NAME);
|
||||
if ($conn->connect_error) {
|
||||
die('Connection failed: ' . $conn->connect_error);
|
||||
$database = new mysqli($DB_HOST, $DB_USER, $DB_PASS, $DB_NAME);
|
||||
if ($database->connect_error) {
|
||||
die('Connection failed: ' . $database->connect_error);
|
||||
}
|
||||
$conn->set_charset('utf8');
|
||||
return $conn;
|
||||
$database->set_charset('utf8');
|
||||
return $database;
|
||||
}
|
||||
|
||||
/** Helper: Initialise SFTP connection */
|
||||
|
||||
+59
-118
@@ -1,139 +1,80 @@
|
||||
<?php
|
||||
session_start();
|
||||
if (empty($_SESSION['admin']) || $_SESSION['admin'] !== true) {
|
||||
session_destroy();
|
||||
header("location: ../index.php");
|
||||
exit();
|
||||
}
|
||||
|
||||
ini_set('display_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once __DIR__ . '/../includes/auth.php';
|
||||
requireLogin('../index.php');
|
||||
requirePermission('admin', 'You do not have permission to manage users.');
|
||||
require_once __DIR__ . '/../config/config.php';
|
||||
require_once __DIR__ . '/../includes/workspace.php';
|
||||
// $username = "UNAME";
|
||||
// $password = "PSWD";
|
||||
// $db = "usbraidlogin";
|
||||
|
||||
$conn = getDatabaseConnection();
|
||||
// charset already set in getDatabaseConnection()
|
||||
|
||||
if ($conn->connect_error) {
|
||||
die("Database connection failed: " . $conn->connect_error);
|
||||
}
|
||||
$database = getDatabaseConnection();
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if (isset($_POST['uname']) && isset($_POST['pswd'])) {
|
||||
$uname = trim($_POST['uname']);
|
||||
$pswd = trim($_POST['pswd']);
|
||||
$is_admin = isset($_POST['admin']) ? 1 : 0;
|
||||
$defPath = isset($_POST['defPath']) ? trim($_POST['defPath']) : '';
|
||||
$delPer = isset($_POST['delPer']) ? (int)$_POST['delPer'] : 0;
|
||||
$dowPer = isset($_POST['downPer']) ? (int)$_POST['downPer'] : 0;
|
||||
$upPer = isset($_POST['upPer']) ? (int)$_POST['upPer'] : 0;
|
||||
$username = is_string($_POST['uname'] ?? null) ? trim($_POST['uname']) : '';
|
||||
$password = is_string($_POST['pswd'] ?? null) ? $_POST['pswd'] : '';
|
||||
$defaultPath = is_string($_POST['defPath'] ?? null) ? normalizePath(trim($_POST['defPath'])) : '/';
|
||||
$isAdmin = isset($_POST['admin']) ? 1 : 0;
|
||||
$canDelete = isset($_POST['delPer']) ? 1 : 0;
|
||||
$canDownload = isset($_POST['downPer']) ? 1 : 0;
|
||||
$canUpload = isset($_POST['upPer']) ? 1 : 0;
|
||||
$_SESSION['message_type'] = 'error';
|
||||
|
||||
if (empty($uname) || empty($pswd)) {
|
||||
if ($username === '' || $password === '') {
|
||||
$_SESSION['message'] = 'Error: Username and password are required!';
|
||||
$_SESSION['message_type'] = 'error';
|
||||
} else if (!CheckPassword($pswd)) {
|
||||
} elseif (!isPasswordValid($password)) {
|
||||
$_SESSION['message'] = 'Error: Password must be at least 8 characters long, contain at least one number and one uppercase letter!';
|
||||
$_SESSION['message_type'] = 'error';
|
||||
} else {
|
||||
$sql_check = "SELECT * FROM users WHERE uname = ?";
|
||||
$stmt_check = $conn->prepare($sql_check);
|
||||
if (!$stmt_check) {
|
||||
$_SESSION['message'] = 'Error: Database preparation failed.';
|
||||
$_SESSION['message_type'] = 'error';
|
||||
} else {
|
||||
$stmt_check->bind_param("s", $uname);
|
||||
$stmt_check->execute();
|
||||
$result_check = $stmt_check->get_result();
|
||||
try {
|
||||
$statement = $database->prepare('SELECT uname FROM users WHERE uname = ?');
|
||||
$statement->bind_param('s', $username);
|
||||
$statement->execute();
|
||||
$result = $statement->get_result();
|
||||
$userExists = $result->num_rows > 0;
|
||||
$result->free();
|
||||
$statement->close();
|
||||
|
||||
if ($result_check->num_rows > 0) {
|
||||
if ($userExists) {
|
||||
$_SESSION['message'] = 'Error: Username already exists!';
|
||||
$_SESSION['message_type'] = 'error';
|
||||
} else {
|
||||
$sql_insert = "INSERT INTO users (uname, pswd, admin, defPath, delPer, downPer, upPer) VALUES (?, ?, ?, ?, ?, ?, ?)";
|
||||
$stmt_insert = $conn->prepare($sql_insert);
|
||||
if (!$stmt_insert) {
|
||||
$_SESSION['message'] = 'Error: Database preparation failed.';
|
||||
$_SESSION['message_type'] = 'error';
|
||||
} else {
|
||||
$hash = password_hash($pswd, PASSWORD_BCRYPT);
|
||||
if (!$hash) {
|
||||
$_SESSION['message'] = 'Error: Password hashing failed.';
|
||||
$_SESSION['message_type'] = 'error';
|
||||
} else {
|
||||
$stmt_insert->bind_param("ssisiii", $uname, $hash, $is_admin, $defPath, $delPer, $dowPer, $upPer);
|
||||
if ($stmt_insert->execute()) {
|
||||
$passwordHash = password_hash($password, PASSWORD_BCRYPT);
|
||||
$statement = $database->prepare('INSERT INTO users (uname, pswd, admin, defPath, delPer, downPer, upPer) VALUES (?, ?, ?, ?, ?, ?, ?)');
|
||||
$statement->bind_param('ssisiii', $username, $passwordHash, $isAdmin, $defaultPath, $canDelete, $canDownload, $canUpload);
|
||||
$statement->execute();
|
||||
$statement->close();
|
||||
$_SESSION['message'] = 'User added successfully!';
|
||||
$_SESSION['message_type'] = 'success';
|
||||
} else {
|
||||
}
|
||||
} catch (mysqli_sql_exception $exception) {
|
||||
error_log($exception->getMessage());
|
||||
$_SESSION['message'] = 'Error: Failed to add user. Please try again later.';
|
||||
$_SESSION['message_type'] = 'error';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$stmt_check->close();
|
||||
if (isset($stmt_insert)) {
|
||||
$stmt_insert->close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
header("Location: adminpanel.php");
|
||||
exit();
|
||||
} else {
|
||||
$_SESSION['message'] = 'Error: Missing form data!';
|
||||
$_SESSION['message_type'] = 'error';
|
||||
header("Location: adminpanel.php");
|
||||
exit();
|
||||
}
|
||||
$database->close();
|
||||
header('Location: adminpanel.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
if (isset($_GET['delete'])) {
|
||||
$delete_uname = htmlspecialchars($_GET['delete']);
|
||||
$sql = "DELETE FROM users WHERE uname=?";
|
||||
$stmt = $conn->prepare($sql);
|
||||
if ($stmt) {
|
||||
$stmt->bind_param("s", $delete_uname);
|
||||
if ($stmt->execute()) {
|
||||
if (isset($_GET['delete']) && is_string($_GET['delete'])) {
|
||||
$usernameToDelete = $_GET['delete'];
|
||||
try {
|
||||
$statement = $database->prepare('DELETE FROM users WHERE uname = ?');
|
||||
$statement->bind_param('s', $usernameToDelete);
|
||||
$statement->execute();
|
||||
$statement->close();
|
||||
$_SESSION['message'] = 'User deleted successfully!';
|
||||
$_SESSION['message_type'] = 'success';
|
||||
} else {
|
||||
} catch (mysqli_sql_exception $exception) {
|
||||
error_log($exception->getMessage());
|
||||
$_SESSION['message'] = 'Error: Failed to delete user.';
|
||||
$_SESSION['message_type'] = 'error';
|
||||
}
|
||||
$stmt->close();
|
||||
} else {
|
||||
$_SESSION['message'] = 'Error: Database preparation failed.';
|
||||
$_SESSION['message_type'] = 'error';
|
||||
}
|
||||
|
||||
header("Location: adminpanel.php");
|
||||
exit();
|
||||
$database->close();
|
||||
header('Location: adminpanel.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$result = $conn->query("SELECT uname, admin, defPath, delPer, downPer, upPer FROM users");
|
||||
|
||||
$result = $database->query('SELECT uname, admin, defPath, delPer, downPer, upPer FROM users');
|
||||
$message = $_SESSION['message'] ?? '';
|
||||
$message_type = $_SESSION['message_type'] ?? '';
|
||||
unset($_SESSION['message']);
|
||||
unset($_SESSION['message_type']);
|
||||
|
||||
function CheckPassword($password) {
|
||||
if (strlen($password) < 8) {
|
||||
return false;
|
||||
}
|
||||
if (!preg_match('/[0-9]/', $password)) {
|
||||
return false;
|
||||
}
|
||||
if (!preg_match('/[A-Z]/', $password)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
$messageType = $_SESSION['message_type'] ?? '';
|
||||
unset($_SESSION['message'], $_SESSION['message_type']);
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
@@ -169,7 +110,7 @@ function CheckPassword($password) {
|
||||
<section class="users-layout">
|
||||
|
||||
<?php if ($message): ?>
|
||||
<div class="alert alert-<?php echo $message_type === 'success' ? 'success' : 'danger'; ?>">
|
||||
<div class="alert alert-<?php echo $messageType === 'success' ? 'success' : 'danger'; ?>">
|
||||
<?php echo htmlspecialchars($message); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
@@ -223,16 +164,16 @@ function CheckPassword($password) {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php while ($row = $result->fetch_assoc()) { ?>
|
||||
<?php while ($user = $result->fetch_assoc()) { ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($row['uname']); ?></td>
|
||||
<td><?php echo $row['admin'] ? 'Yes' : 'No'; ?></td>
|
||||
<td><?php echo htmlspecialchars($row['defPath']); ?></td>
|
||||
<td><?php echo $row['delPer'] ? 'Yes' : 'No'; ?></td>
|
||||
<td><?php echo $row['downPer'] ? 'Yes' : 'No'; ?></td>
|
||||
<td><?php echo $row['upPer'] ? 'Yes' : 'No'; ?></td>
|
||||
<td><?php echo htmlspecialchars($user['uname']); ?></td>
|
||||
<td><?php echo $user['admin'] ? 'Yes' : 'No'; ?></td>
|
||||
<td><?php echo htmlspecialchars($user['defPath']); ?></td>
|
||||
<td><?php echo $user['delPer'] ? 'Yes' : 'No'; ?></td>
|
||||
<td><?php echo $user['downPer'] ? 'Yes' : 'No'; ?></td>
|
||||
<td><?php echo $user['upPer'] ? 'Yes' : 'No'; ?></td>
|
||||
<td>
|
||||
<button class="btn btn-danger btn-sm" onclick="confirmDelete('<?php echo htmlspecialchars($row['uname']); ?>')">Delete</button>
|
||||
<button class="btn btn-danger btn-sm" onclick="confirmDelete(<?= workspaceEscape(json_encode($user['uname'])) ?>)">Delete</button>
|
||||
</td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
@@ -244,5 +185,5 @@ function CheckPassword($password) {
|
||||
</body>
|
||||
</html>
|
||||
<?php
|
||||
$conn->close();
|
||||
$database->close();
|
||||
?>
|
||||
|
||||
@@ -1,95 +1,56 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
if (!isset($_SESSION['uname'])) {
|
||||
header("location: ../index.php");
|
||||
exit();
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/../includes/auth.php';
|
||||
requireLogin('../index.php');
|
||||
require_once __DIR__ . '/../config/config.php';
|
||||
require_once __DIR__ . '/../includes/workspace.php';
|
||||
// $username = "UNAME";
|
||||
// $password = "PSWD";
|
||||
// $db = "usbraidlogin";
|
||||
|
||||
$message = "";
|
||||
$messageType = "";
|
||||
$message = '';
|
||||
$messageType = '';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if (isset($_POST['current_password'], $_POST['new_password'], $_POST['confirm_password'])) {
|
||||
$current_password = htmlspecialchars($_POST['current_password']);
|
||||
$new_password = htmlspecialchars($_POST['new_password']);
|
||||
$confirm_password = htmlspecialchars($_POST['confirm_password']);
|
||||
$uname = $_SESSION['uname'];
|
||||
$messageType = 'danger';
|
||||
$currentPassword = $_POST['current_password'] ?? null;
|
||||
$newPassword = $_POST['new_password'] ?? null;
|
||||
$confirmPassword = $_POST['confirm_password'] ?? null;
|
||||
|
||||
if(!CheckPassword($new_password)) {
|
||||
$message = "New password must be at least 8 characters long, contain at least one number and one uppercase letter!";
|
||||
$messageType = "danger";
|
||||
} else if ($new_password !== $confirm_password) {
|
||||
$message = "New passwords do not match!";
|
||||
$messageType = "danger";
|
||||
if (!is_string($currentPassword) || !is_string($newPassword) || !is_string($confirmPassword)) {
|
||||
$message = 'All fields are required!';
|
||||
} elseif (!isPasswordValid($newPassword)) {
|
||||
$message = 'New password must be at least 8 characters long, contain at least one number and one uppercase letter!';
|
||||
} elseif ($newPassword !== $confirmPassword) {
|
||||
$message = 'New passwords do not match!';
|
||||
} else {
|
||||
$conn = getDatabaseConnection();
|
||||
// charset already set in getDatabaseConnection()
|
||||
$database = getDatabaseConnection();
|
||||
try {
|
||||
$username = $_SESSION['uname'];
|
||||
$statement = $database->prepare('SELECT pswd FROM users WHERE uname = ?');
|
||||
$statement->bind_param('s', $username);
|
||||
$statement->execute();
|
||||
$result = $statement->get_result();
|
||||
$user = $result->fetch_assoc();
|
||||
$result->free();
|
||||
$statement->close();
|
||||
|
||||
if ($conn->connect_error) {
|
||||
$message = "Database connection failed!";
|
||||
$messageType = "danger";
|
||||
if (!$user) {
|
||||
$message = 'User not found!';
|
||||
} elseif (!password_verify($currentPassword, $user['pswd'])) {
|
||||
$message = 'Current password is incorrect!';
|
||||
} else {
|
||||
$sql = "SELECT pswd FROM users WHERE uname=?";
|
||||
$stmt = $conn->prepare($sql);
|
||||
$stmt->bind_param("s", $uname);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
|
||||
if ($result && $result->num_rows > 0) {
|
||||
$row = $result->fetch_assoc();
|
||||
$stored_hash = $row["pswd"];
|
||||
|
||||
if (password_verify($current_password, $stored_hash)) {
|
||||
$new_hash = password_hash($new_password, PASSWORD_BCRYPT);
|
||||
$update_sql = "UPDATE users SET pswd=? WHERE uname=?";
|
||||
$update_stmt = $conn->prepare($update_sql);
|
||||
$update_stmt->bind_param("ss", $new_hash, $uname);
|
||||
|
||||
if ($update_stmt->execute()) {
|
||||
$message = "Password changed successfully!";
|
||||
$messageType = "success";
|
||||
} else {
|
||||
$message = "Error updating password: " . $conn->error;
|
||||
$messageType = "danger";
|
||||
$passwordHash = password_hash($newPassword, PASSWORD_BCRYPT);
|
||||
$statement = $database->prepare('UPDATE users SET pswd = ? WHERE uname = ?');
|
||||
$statement->bind_param('ss', $passwordHash, $username);
|
||||
$statement->execute();
|
||||
$statement->close();
|
||||
$message = 'Password changed successfully!';
|
||||
$messageType = 'success';
|
||||
}
|
||||
$update_stmt->close();
|
||||
} else {
|
||||
$message = "Current password is incorrect!";
|
||||
$messageType = "danger";
|
||||
}
|
||||
} else {
|
||||
$message = "User not found!";
|
||||
$messageType = "danger";
|
||||
}
|
||||
|
||||
$stmt->close();
|
||||
$conn->close();
|
||||
} catch (mysqli_sql_exception $exception) {
|
||||
error_log($exception->getMessage());
|
||||
$message = 'Unable to change password. Please try again.';
|
||||
} finally {
|
||||
$database->close();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$message = "All fields are required!";
|
||||
$messageType = "danger";
|
||||
}
|
||||
}
|
||||
|
||||
function CheckPassword($password) {
|
||||
if (strlen($password) < 8) {
|
||||
return false;
|
||||
}
|
||||
if (!preg_match('/[0-9]/', $password)) {
|
||||
return false;
|
||||
}
|
||||
if (!preg_match('/[A-Z]/', $password)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
?>
|
||||
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
<?php
|
||||
// Include this at the top to see potential errors
|
||||
// Comment out in production
|
||||
ini_set('display_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
require_once __DIR__ . '/../../includes/auth.php';
|
||||
requireLogin('../../index.php');
|
||||
requirePermission('upPer', 'You do not have permission to perform this operation.');
|
||||
|
||||
require_once __DIR__ . '/../../config/config.php';
|
||||
|
||||
$sftp = initializeSFTP();
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'createDir') {
|
||||
$currentPath = isset($_POST['currentPath']) ? normalizePath($_POST['currentPath']) : $defaultPath;
|
||||
$currentPath = requireRemotePath($_POST['currentPath'] ?? $defaultPath, $defaultPath);
|
||||
|
||||
if (!isset($_POST['dirName'])) {
|
||||
http_response_code(400);
|
||||
@@ -17,17 +16,18 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['
|
||||
exit;
|
||||
}
|
||||
|
||||
$dirName = $_POST['dirName'];
|
||||
$directoryName = $_POST['dirName'];
|
||||
|
||||
$dirName = preg_replace('/[^\w\-\.]/', '_', $dirName);
|
||||
$directoryName = preg_replace('/[^\w\-\.]/', '_', $directoryName);
|
||||
|
||||
if (strpos($currentPath, $defaultPath) !== 0) {
|
||||
$currentPath = $defaultPath;
|
||||
if (!isRemoteNameValid($directoryName)) {
|
||||
http_response_code(400);
|
||||
exit('Invalid directory name.');
|
||||
}
|
||||
|
||||
$newDirPath = $currentPath . '/' . $dirName;
|
||||
$newDirectoryPath = rtrim($currentPath, '/') . '/' . $directoryName;
|
||||
|
||||
if ($sftp->mkdir($newDirPath)) {
|
||||
if ($sftp->mkdir($newDirectoryPath)) {
|
||||
echo "Directory created successfully!";
|
||||
} else {
|
||||
http_response_code(500);
|
||||
|
||||
@@ -1,29 +1,14 @@
|
||||
<?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_once __DIR__ . '/../../includes/auth.php';
|
||||
requireLogin('../../index.php');
|
||||
requirePermission('upPer', 'You do not have permission to perform this operation.');
|
||||
|
||||
require_once __DIR__ . '/../../config/config.php';
|
||||
|
||||
$sftp = initializeSFTP();
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'createFile') {
|
||||
$currentPath = isset($_POST['currentPath']) ? normalizePath($_POST['currentPath']) : $defaultPath;
|
||||
$currentPath = requireRemotePath($_POST['currentPath'] ?? $defaultPath, $defaultPath);
|
||||
|
||||
if (!isset($_POST['fileName'])) {
|
||||
http_response_code(400);
|
||||
@@ -41,22 +26,16 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['
|
||||
|
||||
$fileName = preg_replace('/[^\w\-\.]/', '_', $fileName);
|
||||
|
||||
if (strpos($currentPath, $defaultPath) !== 0) {
|
||||
$currentPath = $defaultPath;
|
||||
if (!isRemoteNameValid($fileName)) {
|
||||
http_response_code(400);
|
||||
exit('Invalid file name.');
|
||||
}
|
||||
|
||||
$newFilePath = $currentPath . '/' . $fileName;
|
||||
|
||||
$tempFile = tempnam(sys_get_temp_dir(), 'new_file_');
|
||||
file_put_contents($tempFile, '');
|
||||
|
||||
if ($sftp->put($newFilePath, $tempFile)) {
|
||||
@unlink($tempFile);
|
||||
|
||||
$newFilePath = rtrim($currentPath, '/') . '/' . $fileName;
|
||||
header('Content-Type: application/json; charset=UTF-8');
|
||||
if ($sftp->put($newFilePath, '')) {
|
||||
echo json_encode(['success' => true, 'filePath' => $newFilePath]);
|
||||
} else {
|
||||
@unlink($tempFile);
|
||||
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'message' => 'Failed to create file.']);
|
||||
}
|
||||
|
||||
@@ -1,26 +1,35 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../includes/auth.php';
|
||||
requireLogin('../../index.php');
|
||||
requirePermission('delPer', 'You do not have permission to perform this operation.');
|
||||
|
||||
require_once __DIR__ . '/../../config/config.php';
|
||||
|
||||
$sftp = initializeSFTP();
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete'])) {
|
||||
$itemToDelete = $_POST['delete'];
|
||||
$parentDir = remoteDirname($itemToDelete);
|
||||
$itemToDelete = requireRemotePath($_POST['delete'], $defaultPath);
|
||||
if ($itemToDelete === $defaultPath) {
|
||||
http_response_code(400);
|
||||
exit('Cannot delete the workspace root.');
|
||||
}
|
||||
$parentPath = remoteDirname($itemToDelete);
|
||||
|
||||
if ($parentDir === '/' || $parentDir === '.') {
|
||||
$parentDir = $defaultPath;
|
||||
if ($parentPath === '/' || $parentPath === '.') {
|
||||
$parentPath = $defaultPath;
|
||||
}
|
||||
|
||||
if ($sftp->is_dir($itemToDelete)) {
|
||||
function deleteFolder($sftp, $folderPath) {
|
||||
$items = $sftp->nlist($folderPath);
|
||||
if ($items === false) return false;
|
||||
foreach ($items as $item) {
|
||||
if ($item === '.' || $item === '..') continue;
|
||||
$itemPath = $folderPath . '/' . $item;
|
||||
if ($sftp->is_dir($itemPath)) {
|
||||
deleteFolder($sftp, $itemPath);
|
||||
if (!deleteFolder($sftp, $itemPath)) return false;
|
||||
} else {
|
||||
$sftp->delete($itemPath);
|
||||
if (!$sftp->delete($itemPath)) return false;
|
||||
}
|
||||
}
|
||||
return $sftp->rmdir($folderPath);
|
||||
@@ -31,7 +40,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete'])) {
|
||||
$success = $sftp->delete($itemToDelete);
|
||||
}
|
||||
|
||||
header("Location: index.php?path=" . urlencode($parentDir));
|
||||
if (!$success) {
|
||||
http_response_code(500);
|
||||
exit('Failed to delete the item.');
|
||||
}
|
||||
header("Location: index.php?path=" . urlencode($parentPath));
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<?php
|
||||
set_time_limit(900); // 15 minutes max execution time
|
||||
require_once __DIR__ . '/../../includes/auth.php';
|
||||
requireLogin('../../index.php');
|
||||
requirePermission('downPer', 'You do not have permission to perform this operation.');
|
||||
session_write_close();
|
||||
|
||||
// Include this at the top to see potential errors
|
||||
// Comment out in production
|
||||
ini_set('display_errors', 0);
|
||||
error_reporting(0);
|
||||
set_time_limit(900); // 15 minutes max execution time
|
||||
|
||||
ob_start();
|
||||
|
||||
@@ -18,12 +18,12 @@ try {
|
||||
try {
|
||||
$sftp = initializeSFTP();
|
||||
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());
|
||||
} catch (Exception $exception) {
|
||||
file_put_contents($logFile, "SFTP connection failed: " . $exception->getMessage() . "\n", FILE_APPEND);
|
||||
throw new Exception("Failed to connect to SFTP server: " . $exception->getMessage());
|
||||
}
|
||||
|
||||
function zipFolderRecursive($sftp, $remoteBasePath, $currentPath, $zip, $logFile, &$tempFiles) {
|
||||
function zipFolderRecursive($sftp, $remoteBasePath, $currentPath, $zip, $logFile, &$temporaryFiles) {
|
||||
file_put_contents($logFile, "Processing directory: $currentPath\n", FILE_APPEND);
|
||||
|
||||
$files = $sftp->nlist($currentPath);
|
||||
@@ -35,36 +35,35 @@ try {
|
||||
|
||||
file_put_contents($logFile, "Found " . count($files) . " items in $currentPath\n", FILE_APPEND);
|
||||
|
||||
foreach ($files as $file) {
|
||||
if ($file == '.' || $file == '..') continue;
|
||||
foreach ($files as $fileName) {
|
||||
if ($fileName == '.' || $fileName == '..') continue;
|
||||
|
||||
$fullRemotePath = rtrim($currentPath, '/') . '/' . $file;
|
||||
$remotePath = rtrim($currentPath, '/') . '/' . $fileName;
|
||||
|
||||
$baseDirName = basename($remoteBasePath);
|
||||
$relPathFromBase = substr($fullRemotePath, strlen(rtrim(remoteDirname($remoteBasePath), '/')) + 1);
|
||||
file_put_contents($logFile, "Processing: $fullRemotePath (relative: $relPathFromBase)\n", FILE_APPEND);
|
||||
$relativePath = substr($remotePath, strlen(rtrim(remoteDirname($remoteBasePath), '/')) + 1);
|
||||
file_put_contents($logFile, "Processing: $remotePath (relative: $relativePath)\n", FILE_APPEND);
|
||||
|
||||
$isDir = $sftp->is_dir($fullRemotePath);
|
||||
$isDirectory = $sftp->is_dir($remotePath);
|
||||
|
||||
if ($isDir) {
|
||||
file_put_contents($logFile, "Found subdirectory: $fullRemotePath\n", FILE_APPEND);
|
||||
$zip->addEmptyDir($relPathFromBase);
|
||||
if ($isDirectory) {
|
||||
file_put_contents($logFile, "Found subdirectory: $remotePath\n", FILE_APPEND);
|
||||
$zip->addEmptyDir($relativePath);
|
||||
|
||||
zipFolderRecursive($sftp, $remoteBasePath, $fullRemotePath, $zip, $logFile, $tempFiles);
|
||||
zipFolderRecursive($sftp, $remoteBasePath, $remotePath, $zip, $logFile, $temporaryFiles);
|
||||
} else {
|
||||
$localTempFile = tempnam(sys_get_temp_dir(), 'sftp');
|
||||
$tempFiles[] = $localTempFile;
|
||||
file_put_contents($logFile, "Downloading to temp file: $localTempFile\n", FILE_APPEND);
|
||||
$temporaryFile = tempnam(sys_get_temp_dir(), 'sftp');
|
||||
$temporaryFiles[] = $temporaryFile;
|
||||
file_put_contents($logFile, "Downloading to temp file: $temporaryFile\n", FILE_APPEND);
|
||||
|
||||
$downloadStart = time();
|
||||
$downloadSuccess = $sftp->get($fullRemotePath, $localTempFile);
|
||||
$downloadSuccess = $sftp->get($remotePath, $temporaryFile);
|
||||
$downloadTime = time() - $downloadStart;
|
||||
|
||||
if ($downloadSuccess) {
|
||||
$fileSize = filesize($localTempFile);
|
||||
$fileSize = filesize($temporaryFile);
|
||||
file_put_contents($logFile, "Download successful ($downloadTime seconds), size: $fileSize bytes\n", FILE_APPEND);
|
||||
|
||||
$zip->addFile($localTempFile, $relPathFromBase);
|
||||
$zip->addFile($temporaryFile, $relativePath);
|
||||
} else {
|
||||
file_put_contents($logFile, "Failed to download file after $downloadTime seconds\n", FILE_APPEND);
|
||||
}
|
||||
@@ -83,17 +82,17 @@ try {
|
||||
}
|
||||
|
||||
|
||||
$tempFiles = [];
|
||||
$temporaryFiles = [];
|
||||
|
||||
try {
|
||||
zipFolderRecursive($sftp, $folderPath, $folderPath, $zip, $logFile, $tempFiles);
|
||||
zipFolderRecursive($sftp, $folderPath, $folderPath, $zip, $logFile, $temporaryFiles);
|
||||
|
||||
file_put_contents($logFile, "Closing zip file\n", FILE_APPEND);
|
||||
$zipSuccess = $zip->close();
|
||||
|
||||
foreach ($tempFiles as $tempFile) {
|
||||
if (file_exists($tempFile)) {
|
||||
@unlink($tempFile);
|
||||
foreach ($temporaryFiles as $temporaryFile) {
|
||||
if (file_exists($temporaryFile)) {
|
||||
@unlink($temporaryFile);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,39 +103,39 @@ try {
|
||||
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);
|
||||
} catch (Exception $exception) {
|
||||
foreach ($temporaryFiles as $temporaryFile) {
|
||||
if (file_exists($temporaryFile)) {
|
||||
@unlink($temporaryFile);
|
||||
}
|
||||
}
|
||||
|
||||
file_put_contents($logFile, "Error during zip creation: " . $e->getMessage() . "\n", FILE_APPEND);
|
||||
throw $e;
|
||||
file_put_contents($logFile, "Error during zip creation: " . $exception->getMessage() . "\n", FILE_APPEND);
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
$filePath = requireRemotePath($_SERVER['REQUEST_METHOD'] === 'POST' ? $_POST['file'] : $_GET['file'], $defaultPath);
|
||||
file_put_contents($logFile, "Requested file: $filePath\n", FILE_APPEND);
|
||||
|
||||
$fileExists = $sftp->file_exists($file);
|
||||
$isDir = $sftp->is_dir($file);
|
||||
$fileExists = $sftp->file_exists($filePath);
|
||||
$isDirectory = $sftp->is_dir($filePath);
|
||||
|
||||
file_put_contents($logFile, "File exists: " . ($fileExists ? "Yes" : "No") . "\n", FILE_APPEND);
|
||||
file_put_contents($logFile, "Is directory: " . ($isDir ? "Yes" : "No") . "\n", FILE_APPEND);
|
||||
file_put_contents($logFile, "Is directory: " . ($isDirectory ? "Yes" : "No") . "\n", FILE_APPEND);
|
||||
|
||||
if (!$fileExists && !$isDir) {
|
||||
throw new Exception("File not found: $file");
|
||||
if (!$fileExists && !$isDirectory) {
|
||||
throw new Exception("File not found: $filePath");
|
||||
}
|
||||
|
||||
if ($isDir) {
|
||||
if ($isDirectory) {
|
||||
$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 (zipFolder($sftp, $filePath, $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);
|
||||
|
||||
@@ -145,7 +144,7 @@ try {
|
||||
}
|
||||
|
||||
header('Content-Type: application/zip');
|
||||
header('Content-Disposition: attachment; filename="' . basename($file) . '.zip"');
|
||||
header('Content-Disposition: attachment; filename="' . basename($filePath) . '.zip"');
|
||||
header('Content-Length: ' . filesize($zipFilePath));
|
||||
header('Cache-Control: no-cache, must-revalidate');
|
||||
header('Pragma: no-cache');
|
||||
@@ -161,19 +160,19 @@ try {
|
||||
throw new Exception("Failed to create zip archive");
|
||||
}
|
||||
} else {
|
||||
$localFile = basename($file);
|
||||
file_put_contents($logFile, "Downloading single file: $localFile\n", FILE_APPEND);
|
||||
$fileName = basename($filePath);
|
||||
file_put_contents($logFile, "Downloading single file: $fileName\n", FILE_APPEND);
|
||||
|
||||
$tempFile = tempnam(sys_get_temp_dir(), 'file_');
|
||||
$temporaryFile = tempnam(sys_get_temp_dir(), 'file_');
|
||||
|
||||
$downloadStart = time();
|
||||
$downloadSuccess = $sftp->get($file, $tempFile);
|
||||
$downloadSuccess = $sftp->get($filePath, $temporaryFile);
|
||||
$downloadTime = time() - $downloadStart;
|
||||
|
||||
file_put_contents($logFile, "Download " . ($downloadSuccess ? "successful" : "failed") . " ($downloadTime seconds)\n", FILE_APPEND);
|
||||
|
||||
if ($downloadSuccess) {
|
||||
$fileSize = filesize($tempFile);
|
||||
$fileSize = filesize($temporaryFile);
|
||||
file_put_contents($logFile, "Downloaded file size: $fileSize bytes\n", FILE_APPEND);
|
||||
|
||||
if ($fileSize > 0) {
|
||||
@@ -182,29 +181,29 @@ try {
|
||||
}
|
||||
|
||||
header('Content-Type: application/octet-stream');
|
||||
header('Content-Disposition: attachment; filename="' . $localFile . '"');
|
||||
header('Content-Disposition: attachment; filename="' . $fileName . '"');
|
||||
header('Content-Length: ' . $fileSize);
|
||||
header('Cache-Control: no-cache, must-revalidate');
|
||||
header('Pragma: no-cache');
|
||||
|
||||
readfile($tempFile);
|
||||
readfile($temporaryFile);
|
||||
file_put_contents($logFile, "Download completed\n", FILE_APPEND);
|
||||
@unlink($tempFile);
|
||||
@unlink($temporaryFile);
|
||||
exit;
|
||||
} else {
|
||||
@unlink($tempFile);
|
||||
@unlink($temporaryFile);
|
||||
throw new Exception("Downloaded file is empty");
|
||||
}
|
||||
} else {
|
||||
@unlink($tempFile);
|
||||
@unlink($temporaryFile);
|
||||
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();
|
||||
} catch (Exception $exception) {
|
||||
$errorMessage = "Error: " . $exception->getMessage();
|
||||
file_put_contents($logFile, $errorMessage . "\n", FILE_APPEND);
|
||||
|
||||
while (ob_get_level()) {
|
||||
|
||||
+24
-37
@@ -1,20 +1,7 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
if(!isset($_SESSION['uname'])){
|
||||
header("location: ../../index.php");
|
||||
session_destroy();
|
||||
exit;
|
||||
}
|
||||
|
||||
// Check if user has permission to upload/modify
|
||||
if(!isset($_SESSION["upPer"]) || $_SESSION["upPer"] != true) {
|
||||
die("You don't have permission to extract archives.");
|
||||
}
|
||||
|
||||
// Include error reporting for debugging
|
||||
ini_set('display_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
require_once __DIR__ . '/../../includes/auth.php';
|
||||
requireLogin('../../index.php');
|
||||
requirePermission('upPer', 'You do not have permission to perform this operation.');
|
||||
|
||||
require_once __DIR__ . '/../../config/config.php';
|
||||
use phpseclib3\Net\SFTP;
|
||||
@@ -23,21 +10,21 @@ $sftp = initializeSFTP();
|
||||
|
||||
// Process extraction request
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['archive'])) {
|
||||
$archivePath = $_POST['archive'];
|
||||
$dirPath = remoteDirname($archivePath);
|
||||
$archivePath = requireRemotePath($_POST['archive'], $defaultPath);
|
||||
$directoryPath = remoteDirname($archivePath);
|
||||
$archiveName = basename($archivePath);
|
||||
$extension = pathinfo($archiveName, PATHINFO_EXTENSION);
|
||||
|
||||
// Create a temporary local directory for extraction
|
||||
$tempDir = sys_get_temp_dir() . '/sftp_extract_' . time();
|
||||
if (!mkdir($tempDir, 0777, true)) {
|
||||
$temporaryDirectory = sys_get_temp_dir() . '/sftp_extract_' . time();
|
||||
if (!mkdir($temporaryDirectory, 0777, true)) {
|
||||
die("Failed to create temporary directory");
|
||||
}
|
||||
|
||||
// Download the archive to the temp directory
|
||||
$localArchivePath = $tempDir . '/' . $archiveName;
|
||||
$localArchivePath = $temporaryDirectory . '/' . $archiveName;
|
||||
if (!$sftp->get($archivePath, $localArchivePath)) {
|
||||
rmdir($tempDir);
|
||||
rmdir($temporaryDirectory);
|
||||
die("Failed to download the archive");
|
||||
}
|
||||
|
||||
@@ -48,11 +35,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['archive'])) {
|
||||
// Determine the extraction method based on file extension
|
||||
switch (strtolower($extension)) {
|
||||
case 'zip':
|
||||
$extractionSuccess = extractZip($localArchivePath, $tempDir);
|
||||
$extractionSuccess = extractZip($localArchivePath, $temporaryDirectory);
|
||||
break;
|
||||
|
||||
case 'rar':
|
||||
$extractionSuccess = extractRar($localArchivePath, $tempDir);
|
||||
$extractionSuccess = extractRar($localArchivePath, $temporaryDirectory);
|
||||
break;
|
||||
|
||||
case 'tar':
|
||||
@@ -60,7 +47,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['archive'])) {
|
||||
case 'bz2':
|
||||
case 'xz':
|
||||
case '7z':
|
||||
$extractionSuccess = extractArchive($localArchivePath, $tempDir);
|
||||
$extractionSuccess = extractArchive($localArchivePath, $temporaryDirectory);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -69,20 +56,20 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['archive'])) {
|
||||
|
||||
if ($extractionSuccess) {
|
||||
// Upload extracted files back to the server
|
||||
uploadExtractedFiles($sftp, $tempDir, $dirPath);
|
||||
uploadExtractedFiles($sftp, $temporaryDirectory, $directoryPath);
|
||||
|
||||
// Clean up temporary directory
|
||||
deleteDirectory($tempDir);
|
||||
deleteDirectory($temporaryDirectory);
|
||||
|
||||
// Redirect back to the file listing
|
||||
header("Location: index.php?path=" . urlencode($dirPath));
|
||||
header("Location: index.php?path=" . urlencode($directoryPath));
|
||||
exit;
|
||||
} else {
|
||||
die("Failed to extract the archive");
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
deleteDirectory($tempDir);
|
||||
die("Error during extraction: " . $e->getMessage());
|
||||
} catch (Exception $exception) {
|
||||
deleteDirectory($temporaryDirectory);
|
||||
die("Error during extraction: " . $exception->getMessage());
|
||||
}
|
||||
} else {
|
||||
header("Location: index.php");
|
||||
@@ -140,7 +127,7 @@ function extractArchive($archivePath, $destination) {
|
||||
$command = '';
|
||||
|
||||
// Change to the destination directory
|
||||
$currentDir = getcwd();
|
||||
$originalDirectory = getcwd();
|
||||
chdir($destination);
|
||||
|
||||
if (PHP_OS_FAMILY === 'Windows') {
|
||||
@@ -189,20 +176,20 @@ function extractArchive($archivePath, $destination) {
|
||||
exec($command, $output, $returnCode);
|
||||
|
||||
// Change back to the original directory
|
||||
chdir($currentDir);
|
||||
chdir($originalDirectory);
|
||||
|
||||
return $returnCode === 0;
|
||||
}
|
||||
|
||||
// Upload extracted files back to the SFTP server
|
||||
function uploadExtractedFiles($sftp, $localDir, $remoteDir) {
|
||||
$items = scandir($localDir);
|
||||
function uploadExtractedFiles($sftp, $localDirectory, $remoteDirectory) {
|
||||
$items = scandir($localDirectory);
|
||||
|
||||
foreach ($items as $item) {
|
||||
if ($item === '.' || $item === '..') continue;
|
||||
|
||||
$localPath = $localDir . '/' . $item;
|
||||
$remotePath = $remoteDir . '/' . $item;
|
||||
$localPath = $localDirectory . '/' . $item;
|
||||
$remotePath = $remoteDirectory . '/' . $item;
|
||||
|
||||
if (is_dir($localPath)) {
|
||||
// Create the directory on the remote server
|
||||
|
||||
+32
-39
@@ -1,38 +1,31 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once __DIR__ . '/../../includes/auth.php';
|
||||
requireLogin('../../index.php');
|
||||
|
||||
if(!isset($_SESSION['uname'])){
|
||||
header("location: ../../index.php");
|
||||
session_destroy();
|
||||
exit;
|
||||
}
|
||||
require_once __DIR__ . '/../../config/config.php';
|
||||
require_once __DIR__ . '/../../includes/workspace.php';
|
||||
$sftp = initializeSFTP();
|
||||
|
||||
// Include this at the top to see potential errors
|
||||
// Comment out in production
|
||||
ini_set('display_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once __DIR__ . '/../../config/config.php';
|
||||
require_once __DIR__ . '/../../includes/workspace.php';
|
||||
$sftp = initializeSFTP();
|
||||
|
||||
$defaultPath = normalizePath($defaultPath);
|
||||
$currentPath = isset($_GET['path']) ? normalizePath($_GET['path']) : $defaultPath;
|
||||
if ($currentPath !== $defaultPath && strpos($currentPath, rtrim($defaultPath, '/') . '/') !== 0) {
|
||||
$currentPath = isset($_GET['path']) ? normalizePath($_GET['path']) : $defaultPath;
|
||||
if (!isRemotePathWithin($currentPath, $defaultPath)) {
|
||||
$currentPath = $defaultPath;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$sftp->chdir($currentPath)) {
|
||||
if (!$sftp->chdir($currentPath)) {
|
||||
die("Failed to navigate to the selected folder: $currentPath");
|
||||
}
|
||||
}
|
||||
|
||||
$items = $sftp->nlist();
|
||||
$items = $sftp->nlist();
|
||||
if ($items === false) {
|
||||
http_response_code(500);
|
||||
exit('Failed to list directory contents.');
|
||||
}
|
||||
|
||||
$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'];
|
||||
$archiveExtensions = ['zip', 'rar', 'tar', 'gz', '7z', 'bz2', 'xz', 'tar.gz', 'tar.bz2', 'tar.xz'];
|
||||
$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'];
|
||||
$archiveExtensions = ['zip', 'rar', 'tar', 'gz', '7z', 'bz2', 'xz', 'tar.gz', 'tar.bz2', 'tar.xz'];
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
@@ -67,7 +60,7 @@
|
||||
<div class="file-content">
|
||||
<!-- Action Buttons -->
|
||||
<div class="action-buttons">
|
||||
<?php if (isset($_SESSION["upPer"]) && $_SESSION["upPer"] == true) { ?>
|
||||
<?php if (hasPermission('upPer')) { ?>
|
||||
<button type="button" class="btn" data-dialog="createFileModal">+ New file</button>
|
||||
<button type="button" class="btn btn-primary" onclick="document.getElementById('fileInput').click()">↑ Upload files</button>
|
||||
<button type="button" class="btn" data-dialog="createDirModal">+ New folder</button>
|
||||
@@ -154,7 +147,7 @@
|
||||
</dialog>
|
||||
<!-- End of Modal Forms -->
|
||||
|
||||
<?php if (isset($_SESSION["upPer"]) && $_SESSION["upPer"] == true) { ?>
|
||||
<?php if (hasPermission('upPer')) { ?>
|
||||
<div class="dropzone" ondragover="handleDragOver(event)" ondragleave="handleDragLeave(event)" ondrop="handleDrop(event)">
|
||||
<span class="upload-symbol" aria-hidden="true">↑</span><span><strong>Drop files or folders here</strong><small>Or use the upload buttons to browse your device</small></span>
|
||||
</div>
|
||||
@@ -198,20 +191,20 @@
|
||||
<td><span class="file-icon folder-icon" aria-hidden="true">▰</span>
|
||||
<a href="?path=<?= urlencode(rtrim($currentPath, '/') . '/' . $directory) ?>"><?= htmlspecialchars($directory) ?></a></td><td class="size-cell">—</td>
|
||||
<td>
|
||||
<?php if (isset($_SESSION["delPer"]) && $_SESSION["delPer"] == true) : ?>
|
||||
<?php if (hasPermission('delPer')) : ?>
|
||||
<form method="POST" action="delete.php" style="display:inline;">
|
||||
<input type="hidden" name="delete" value="<?= htmlspecialchars(rtrim($currentPath, '/') . '/' . $directory) ?>">
|
||||
<button type="submit" class="btn btn-danger mt-1 mb-1" onclick="confirmDelete(event)">Delete</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($_SESSION["downPer"]) && $_SESSION["downPer"] == true) : ?>
|
||||
<?php if (hasPermission('downPer')) : ?>
|
||||
<form method="POST" action="download.php" style="display:inline;">
|
||||
<input type="hidden" name="file" value="<?= htmlspecialchars(rtrim($currentPath, '/') . '/' . $directory) ?>">
|
||||
<button type="submit" class="btn btn-success mt-1 mb-1">Download</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
<?php if (isset($_SESSION["upPer"]) && $_SESSION["upPer"] == true) : ?>
|
||||
<?php if (hasPermission('upPer')) : ?>
|
||||
<button type="button" class="btn btn-warning mt-1 mb-1" onclick="openRenameModal('<?= htmlspecialchars(addslashes($directory)) ?>', '<?= htmlspecialchars(addslashes(rtrim($currentPath, '/') . '/' . $directory)) ?>', true)">Rename</button>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
@@ -221,8 +214,8 @@
|
||||
foreach ($files as $file) : ?>
|
||||
<?php
|
||||
|
||||
$fileExtension = pathinfo($file, PATHINFO_EXTENSION);
|
||||
$isPDF = strtolower($fileExtension) === 'pdf';
|
||||
$fileExtension = strtolower(pathinfo($file, PATHINFO_EXTENSION));
|
||||
$isPdf = $fileExtension === 'pdf';
|
||||
$isImage = in_array($fileExtension, $imageExtensions);
|
||||
$isVideo = in_array($fileExtension, $videoExtensions);
|
||||
$isAudio = in_array($fileExtension, $audioExtensions);
|
||||
@@ -251,7 +244,7 @@
|
||||
<?= htmlspecialchars($file) ?>
|
||||
</a>
|
||||
<span class="badge bg-secondary rounded-pill"><?= htmlspecialchars($fileExtension) ?></span>
|
||||
<?php elseif ($isPDF): ?>
|
||||
<?php elseif ($isPdf): ?>
|
||||
<a class="text-info-emphasis" href="pdf.php?file=<?= urlencode(rtrim($currentPath, '/') . '/' . $file) ?>&type=pdf">
|
||||
<?= htmlspecialchars($file) ?>
|
||||
</a>
|
||||
@@ -265,23 +258,23 @@
|
||||
<span class="text"><?= formatBytes($sftp->stat(rtrim($currentPath, '/') . '/' . $file)['size']) ?></span>
|
||||
</td>
|
||||
<td>
|
||||
<?php if (isset($_SESSION["delPer"]) && $_SESSION["delPer"] == true) : ?>
|
||||
<?php if (hasPermission('delPer')) : ?>
|
||||
<form method="POST" action="delete.php" style="display:inline;">
|
||||
<input type="hidden" name="delete" value="<?= htmlspecialchars(rtrim($currentPath, '/') . '/' . $file) ?>">
|
||||
<button type="submit" class="btn btn-danger mt-1 mb-1" onclick="confirmDelete(event)">Delete</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($_SESSION["downPer"]) && $_SESSION["downPer"] == true) : ?>
|
||||
<?php if (hasPermission('downPer')) : ?>
|
||||
<form method="POST" action="download.php" style="display:inline;">
|
||||
<input type="hidden" name="file" value="<?= htmlspecialchars(rtrim($currentPath, '/') . '/' . $file) ?>">
|
||||
<button type="submit" class="btn btn-success mt-1 mb-1">Download</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
<?php if (isset($_SESSION["upPer"]) && $_SESSION["upPer"] == true) : ?>
|
||||
<?php if (hasPermission('upPer')) : ?>
|
||||
<button type="button" class="btn btn-warning mt-1 mb-1" onclick="openRenameModal('<?= htmlspecialchars(addslashes($file)) ?>', '<?= htmlspecialchars(addslashes(rtrim($currentPath, '/') . '/' . $file)) ?>')">Rename</button>
|
||||
<?php endif; ?>
|
||||
<?php if ($isArchive && isset($_SESSION["upPer"]) && $_SESSION["upPer"] == true) : ?>
|
||||
<?php if ($isArchive && hasPermission('upPer')) : ?>
|
||||
<form method="POST" action="extract.php" style="display:inline;">
|
||||
<input type="hidden" name="archive" value="<?= htmlspecialchars(rtrim($currentPath, '/') . '/' . $file) ?>">
|
||||
<button type="submit" class="btn btn-info mt-1 mb-1">Extract</button>
|
||||
|
||||
@@ -1,22 +1,12 @@
|
||||
<?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_once __DIR__ . '/../../includes/auth.php';
|
||||
requireLogin('../../index.php');
|
||||
|
||||
require_once __DIR__ . '/../../config/config.php';
|
||||
require_once __DIR__ . '/../../includes/workspace.php';
|
||||
$sftp = initializeSFTP();
|
||||
|
||||
$filePath = isset($_GET['file']) ? $_GET['file'] : '';
|
||||
$filePath = requireRemotePath($_GET['file'] ?? $defaultPath, $defaultPath);
|
||||
|
||||
$content = '';
|
||||
$fileName = basename($filePath);
|
||||
@@ -29,7 +19,7 @@ if (!empty($filePath) && $sftp->file_exists($filePath) && !$sftp->is_dir($filePa
|
||||
if (in_array(strtolower($extension), $editableExtensions)) {
|
||||
$editable = true;
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['content']) && isset($_SESSION["upPer"]) && $_SESSION["upPer"] == true) {
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['content']) && hasPermission('upPer')) {
|
||||
$newContent = $_POST['content'];
|
||||
|
||||
if ($sftp->put($filePath, $newContent)) {
|
||||
@@ -52,7 +42,7 @@ if (!empty($filePath) && $sftp->file_exists($filePath) && !$sftp->is_dir($filePa
|
||||
|
||||
$showLineNumbers = in_array(strtolower($extension), ['php', 'js', 'html', 'css', 'py', 'java', 'c', 'cpp', 'h', 'hpp', 'rb', 'sh', 'xml', 'json']);
|
||||
|
||||
$parentDir = remoteDirname($filePath);
|
||||
$parentPath = remoteDirname($filePath);
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
@@ -75,7 +65,7 @@ $parentDir = remoteDirname($filePath);
|
||||
'title' => $fileName,
|
||||
'description' => 'View and edit text files in your remote workspace.',
|
||||
'connected' => true,
|
||||
'back' => 'index.php?path=' . urlencode($parentDir),
|
||||
'back' => 'index.php?path=' . urlencode($parentPath),
|
||||
'class' => 'editor-page',
|
||||
]); ?>
|
||||
<section class="content-panel">
|
||||
@@ -94,7 +84,7 @@ $parentDir = remoteDirname($filePath);
|
||||
<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): ?>
|
||||
<?php if (!hasPermission('upPer')): ?>
|
||||
<span class="readonly-notice">Read-only mode (you don't have upload permissions)</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
@@ -102,11 +92,11 @@ $parentDir = remoteDirname($filePath);
|
||||
<?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>
|
||||
<textarea id="editor" name="content" class="form-control" <?= (!hasPermission('upPer')) ? 'readonly' : '' ?>><?= htmlspecialchars($content) ?></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (isset($_SESSION["upPer"]) && $_SESSION["upPer"] == true): ?>
|
||||
<?php if (hasPermission('upPer')): ?>
|
||||
<div class="mb-3">
|
||||
<button type="submit" class="btn btn-primary">Save Changes</button>
|
||||
</div>
|
||||
|
||||
+6
-12
@@ -1,11 +1,6 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
if(!isset($_SESSION['uname'])){
|
||||
header("location: ../../index.php");
|
||||
session_destroy();
|
||||
exit;
|
||||
}
|
||||
require_once __DIR__ . '/../../includes/auth.php';
|
||||
requireLogin('../../index.php');
|
||||
|
||||
require_once __DIR__ . '/../../config/config.php';
|
||||
require_once __DIR__ . '/../../includes/workspace.php';
|
||||
@@ -15,7 +10,7 @@ if (!isset($_GET['file'])) {
|
||||
die("No file specified");
|
||||
}
|
||||
|
||||
$filePath = $_GET['file'];
|
||||
$filePath = requireRemotePath($_GET['file'], $defaultPath);
|
||||
$fileName = basename($filePath);
|
||||
$fileSize = $sftp->stat($filePath)['size'];
|
||||
|
||||
@@ -105,8 +100,8 @@ if (isset($_GET['stream'])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
error_log("Streaming error: " . $e->getMessage());
|
||||
} catch (Exception $exception) {
|
||||
error_log("Streaming error: " . $exception->getMessage());
|
||||
}
|
||||
exit;
|
||||
}
|
||||
@@ -138,7 +133,6 @@ if (isset($_GET['stream'])) {
|
||||
'class' => 'pdf-page',
|
||||
]); ?>
|
||||
|
||||
|
||||
<section class="content-panel pdf-workspace" aria-label="PDF document">
|
||||
<div id="pdfContainer" class="container-fluid position-relative">
|
||||
<div id="loadingMessage" class="d-flex align-items-center justify-content-center w-100 h-100">
|
||||
@@ -166,7 +160,7 @@ if (isset($_GET['stream'])) {
|
||||
</div>
|
||||
</div>
|
||||
<div class="actionButton">
|
||||
<?php if (isset($_SESSION["downPer"]) && $_SESSION["downPer"] == true) : ?>
|
||||
<?php if (hasPermission('downPer')) : ?>
|
||||
<a href="download.php?file=<?= urlencode($filePath) ?>" class="btn btn-success m-3">Download</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
+11
-19
@@ -1,18 +1,7 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
if(!isset($_SESSION['uname'])){
|
||||
header("location: ../../index.php");
|
||||
session_destroy();
|
||||
exit;
|
||||
}
|
||||
|
||||
if(!isset($_SESSION["upPer"]) || $_SESSION["upPer"] != true) {
|
||||
die("You don't have permission to rename files or directories.");
|
||||
}
|
||||
|
||||
ini_set('display_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
require_once __DIR__ . '/../../includes/auth.php';
|
||||
requireLogin('../../index.php');
|
||||
requirePermission('upPer', 'You do not have permission to perform this operation.');
|
||||
|
||||
require_once __DIR__ . '/../../config/config.php';
|
||||
$sftp = initializeSFTP();
|
||||
@@ -20,23 +9,26 @@ $sftp = initializeSFTP();
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$oldPath = isset($_POST['path']) ? $_POST['path'] : '';
|
||||
$newName = isset($_POST['newName']) ? trim($_POST['newName']) : '';
|
||||
$isDirectory = isset($_POST['isDirectory']) && $_POST['isDirectory'] == '1';
|
||||
|
||||
if (empty($oldPath) || empty($newName)) {
|
||||
die("Missing required information for renaming.");
|
||||
}
|
||||
|
||||
$dirPath = remoteDirname($oldPath);
|
||||
$oldName = basename($oldPath);
|
||||
$oldPath = requireRemotePath($oldPath, $defaultPath);
|
||||
if ($oldPath === $defaultPath || !isRemoteNameValid($newName)) {
|
||||
http_response_code(400);
|
||||
exit('Invalid item name or workspace root.');
|
||||
}
|
||||
$directoryPath = remoteDirname($oldPath);
|
||||
|
||||
$newPath = $dirPath . '/' . $newName;
|
||||
$newPath = rtrim($directoryPath, '/') . '/' . $newName;
|
||||
|
||||
if ($sftp->file_exists($newPath) || $sftp->is_dir($newPath)) {
|
||||
die("Error: A file or directory with this name already exists.");
|
||||
}
|
||||
|
||||
if ($sftp->rename($oldPath, $newPath)) {
|
||||
header("Location: index.php?path=" . urlencode($dirPath));
|
||||
header("Location: index.php?path=" . urlencode($directoryPath));
|
||||
exit;
|
||||
} else {
|
||||
die("Failed to rename the item. Please try again.");
|
||||
|
||||
@@ -1,16 +1,8 @@
|
||||
<?php
|
||||
// Include this at the top to see potential errors
|
||||
// Comment out in production
|
||||
ini_set('display_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
require_once __DIR__ . '/../../includes/auth.php';
|
||||
requireLogin('../../index.php');
|
||||
|
||||
session_start();
|
||||
|
||||
if (empty($_SESSION['admin']) || $_SESSION['admin'] !== true) {
|
||||
session_destroy();
|
||||
header("location: ../../index.php");
|
||||
exit();
|
||||
}
|
||||
requirePermission('admin', 'You do not have permission to view server statistics.');
|
||||
|
||||
require_once __DIR__ . '/../../config/config.php';
|
||||
require_once __DIR__ . '/../../includes/workspace.php';
|
||||
@@ -90,7 +82,6 @@ $monitors = [
|
||||
'class' => 'server-page',
|
||||
]); ?>
|
||||
|
||||
|
||||
<div class="content-toolbar"><p>Live statistics · refreshes every 5 seconds</p><span class="badge bg-light text-dark"><i class="ui-icon icon-clock" aria-hidden="true"></i> <?= htmlspecialchars($systemData['lastUpdate']) ?></span></div>
|
||||
<div class="stats-grid">
|
||||
<?php foreach ($monitors as $monitor): ?>
|
||||
|
||||
+16
-24
@@ -1,26 +1,19 @@
|
||||
<?php
|
||||
// Include this at the top to see potential errors
|
||||
// Comment out in production
|
||||
ini_set('display_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once __DIR__ . '/../../config/config.php';
|
||||
require_once __DIR__ . '/../../includes/workspace.php';
|
||||
|
||||
// getDatabaseConnection relocated to central config
|
||||
|
||||
if (!isset($_GET['token'])) {
|
||||
die("Invalid request. No token provided.");
|
||||
}
|
||||
|
||||
$token = $_GET['token'];
|
||||
$conn = getDatabaseConnection();
|
||||
$database = getDatabaseConnection();
|
||||
|
||||
// Get share information
|
||||
$stmt = $conn->prepare("SELECT * FROM share WHERE token = ? AND expiration > NOW()");
|
||||
$stmt->bind_param("s", $token);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$statement = $database->prepare("SELECT * FROM share WHERE token = ? AND expiration > NOW()");
|
||||
$statement->bind_param("s", $token);
|
||||
$statement->execute();
|
||||
$result = $statement->get_result();
|
||||
|
||||
if ($result->num_rows === 0) {
|
||||
die("Invalid or expired share link.");
|
||||
@@ -29,7 +22,9 @@ if ($result->num_rows === 0) {
|
||||
$share = $result->fetch_assoc();
|
||||
$filePath = $share['file_path'];
|
||||
$downloadAllowed = $share['download_allowed'] == 1;
|
||||
$stmt->close();
|
||||
$result->free();
|
||||
$statement->close();
|
||||
$database->close();
|
||||
|
||||
// Initialize SFTP
|
||||
$sftp = initializeSFTP();
|
||||
@@ -42,13 +37,13 @@ $fileName = basename($filePath);
|
||||
$fileExtension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
|
||||
$fileSize = $sftp->stat($filePath)['size'];
|
||||
|
||||
$imageTypes = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg'];
|
||||
$videoTypes = ['mp4', 'webm', 'ogg', 'mov', 'avi', 'mkv'];
|
||||
$audioTypes = ['mp3', 'wav', 'ogg', 'm4a', 'flac', 'aac'];
|
||||
$imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg'];
|
||||
$videoExtensions = ['mp4', 'webm', 'ogg', 'mov', 'avi', 'mkv'];
|
||||
$audioExtensions = ['mp3', 'wav', 'ogg', 'm4a', 'flac', 'aac'];
|
||||
|
||||
$isImage = in_array($fileExtension, $imageTypes);
|
||||
$isVideo = in_array($fileExtension, $videoTypes);
|
||||
$isAudio = in_array($fileExtension, $audioTypes);
|
||||
$isImage = in_array($fileExtension, $imageExtensions);
|
||||
$isVideo = in_array($fileExtension, $videoExtensions);
|
||||
$isAudio = in_array($fileExtension, $audioExtensions);
|
||||
|
||||
if (!$isImage && !$isVideo && !$isAudio) {
|
||||
die("Unsupported file type");
|
||||
@@ -165,8 +160,8 @@ if (isset($_GET['stream'])) {
|
||||
|
||||
usleep(100000);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
error_log("Shared streaming error: " . $e->getMessage());
|
||||
} catch (Exception $exception) {
|
||||
error_log("Shared streaming error: " . $exception->getMessage());
|
||||
}
|
||||
exit;
|
||||
}
|
||||
@@ -242,7 +237,6 @@ function formatBytes($bytes, $precision = 2) {
|
||||
'class' => 'media-page',
|
||||
]); ?>
|
||||
|
||||
|
||||
<div class="content-toolbar"><span class="badge">Shared link</span><p>Available until <?= htmlspecialchars(date('F j, Y, g:i a', strtotime($share['expiration']))) ?></p></div>
|
||||
<section class="content-panel media-panel">
|
||||
<article class="col-12">
|
||||
@@ -293,8 +287,6 @@ function formatBytes($bytes, $precision = 2) {
|
||||
|
||||
<?php renderWorkspaceEnd(); ?>
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const mediaElement = document.querySelector('video, audio');
|
||||
|
||||
+64
-109
@@ -1,130 +1,85 @@
|
||||
<?php
|
||||
// Include this at the top to see potential errors
|
||||
// Comment out in production
|
||||
ini_set('display_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once __DIR__ . '/../../includes/auth.php';
|
||||
requireLogin('../../index.php');
|
||||
requirePermission('upPer', 'You do not have permission to upload files.');
|
||||
session_write_close();
|
||||
require_once __DIR__ . '/../../config/config.php';
|
||||
|
||||
use phpseclib3\Net\SFTP;
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
http_response_code(400);
|
||||
exit('No files received or invalid request.');
|
||||
}
|
||||
|
||||
$currentPath = requireRemotePath($_POST['currentPath'] ?? $defaultPath, $defaultPath);
|
||||
$sftp = initializeSFTP();
|
||||
|
||||
$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 = remoteDirname($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'])) {
|
||||
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.";
|
||||
if (!is_array($directories)) {
|
||||
http_response_code(400);
|
||||
exit;
|
||||
exit('Invalid directory list.');
|
||||
}
|
||||
|
||||
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;
|
||||
foreach ($directories as $directory) {
|
||||
$directoryPath = requireRemotePath(rtrim($currentPath, '/') . '/' . $directory, $currentPath);
|
||||
if (!$sftp->is_dir($directoryPath) && !$sftp->mkdir($directoryPath, 0755, true)) {
|
||||
http_response_code(500);
|
||||
exit('Failed to create upload directory.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo $errorMessage;
|
||||
$uploadedFiles = $_FILES['files'] ?? null;
|
||||
if (!$uploadedFiles || empty($uploadedFiles['name'][0])) {
|
||||
http_response_code(400);
|
||||
exit;
|
||||
exit('No files received or file size exceeds PHP limits.');
|
||||
}
|
||||
|
||||
$uploadErrors = [
|
||||
UPLOAD_ERR_INI_SIZE => 'File exceeds upload_max_filesize directive in php.ini',
|
||||
UPLOAD_ERR_FORM_SIZE => 'File exceeds MAX_FILE_SIZE directive in the HTML form',
|
||||
UPLOAD_ERR_PARTIAL => 'File was only partially uploaded',
|
||||
UPLOAD_ERR_NO_FILE => 'No file was uploaded',
|
||||
UPLOAD_ERR_NO_TMP_DIR => 'Missing a temporary folder',
|
||||
UPLOAD_ERR_CANT_WRITE => 'Failed to write file to disk',
|
||||
UPLOAD_ERR_EXTENSION => 'A PHP extension stopped the file upload',
|
||||
];
|
||||
$uploadMessages = [];
|
||||
$anySuccess = false;
|
||||
$failureStatus = 500;
|
||||
|
||||
foreach ($uploadedFiles['tmp_name'] as $index => $temporaryPath) {
|
||||
$fileName = $uploadedFiles['name'][$index];
|
||||
$relativePath = $_POST['paths'][$index] ?? '';
|
||||
$uploadError = $uploadedFiles['error'][$index];
|
||||
if ($uploadError !== UPLOAD_ERR_OK) {
|
||||
$failureStatus = 400;
|
||||
$uploadMessages[] = $fileName . ': ' . ($uploadErrors[$uploadError] ?? 'Unknown upload error');
|
||||
continue;
|
||||
}
|
||||
|
||||
$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 = remoteDirname($relativePath);
|
||||
if ($dirPart !== '.' && $dirPart !== '') {
|
||||
$remoteDirPath = $currentPath . '/' . $dirPart;
|
||||
|
||||
if (!$sftp->is_dir($remoteDirPath)) {
|
||||
createDirectoryRecursive($sftp, $remoteDirPath);
|
||||
$remotePath = requireRemotePath(rtrim($currentPath, '/') . '/' . ($relativePath !== '' ? $relativePath : $fileName), $currentPath);
|
||||
if ($remotePath === $currentPath) {
|
||||
http_response_code(400);
|
||||
exit('Invalid upload path.');
|
||||
}
|
||||
$directoryPath = remoteDirname($remotePath);
|
||||
if (!$sftp->is_dir($directoryPath) && !$sftp->mkdir($directoryPath, 0755, true)) {
|
||||
$uploadMessages[] = 'Failed to create directory for: ' . $fileName;
|
||||
continue;
|
||||
}
|
||||
|
||||
$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);
|
||||
if ($sftp->put($remotePath, $temporaryPath, SFTP::SOURCE_LOCAL_FILE)) {
|
||||
$uploadMessages[] = '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;
|
||||
$uploadMessages[] = 'Failed to upload: ' . ($relativePath !== '' ? $relativePath : $fileName);
|
||||
}
|
||||
}
|
||||
|
||||
echo "No files received or invalid request.";
|
||||
http_response_code(400);
|
||||
exit;
|
||||
if (!$anySuccess) {
|
||||
http_response_code($failureStatus);
|
||||
}
|
||||
header('Content-Type: text/plain; charset=UTF-8');
|
||||
echo implode("\n", $uploadMessages);
|
||||
|
||||
+23
-34
@@ -1,16 +1,7 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once __DIR__ . '/../../includes/auth.php';
|
||||
requireLogin('../../index.php');
|
||||
|
||||
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_once __DIR__ . '/../../config/config.php';
|
||||
@@ -24,7 +15,7 @@ if (!isset($_GET['file'])) {
|
||||
if (isset($_POST['createShare'])) {
|
||||
$duration = intval($_POST['duration']);
|
||||
$downloadAllowed = isset($_POST['downloadAllowed']) ? 1 : 0;
|
||||
$filePath = $_POST['filePath'];
|
||||
$filePath = requireRemotePath($_POST['filePath'], $defaultPath);
|
||||
|
||||
$token = bin2hex(random_bytes(16));
|
||||
|
||||
@@ -32,11 +23,15 @@ if (isset($_POST['createShare'])) {
|
||||
|
||||
$expiration = date('Y-m-d H:i:s', strtotime("+$duration hours"));
|
||||
|
||||
$conn = getDatabaseConnection();
|
||||
$stmt = $conn->prepare("INSERT INTO share (token, file_path, expiration, download_allowed) VALUES (?, ?, ?, ?)");
|
||||
$stmt->bind_param("sssi", $token, $filePath, $expiration, $downloadAllowed);
|
||||
$database = getDatabaseConnection();
|
||||
$statement = $database->prepare("INSERT INTO share (token, file_path, expiration, download_allowed) VALUES (?, ?, ?, ?)");
|
||||
$statement->bind_param("sssi", $token, $filePath, $expiration, $downloadAllowed);
|
||||
|
||||
if ($stmt->execute()) {
|
||||
$shareSaved = $statement->execute();
|
||||
$statement->close();
|
||||
$database->close();
|
||||
|
||||
if ($shareSaved) {
|
||||
$shareUrl = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://$_SERVER[HTTP_HOST]" .
|
||||
remoteDirname($_SERVER['PHP_SELF']) . "/shared.php?token=$token";
|
||||
|
||||
@@ -47,13 +42,11 @@ if (isset($_POST['createShare'])) {
|
||||
header("Location: view.php?file=" . urlencode($filePath) . "&shared=1");
|
||||
exit;
|
||||
} else {
|
||||
$_SESSION['shareError'] = "Failed to create share: " . $conn->error;
|
||||
$_SESSION['shareError'] = 'Failed to create share. Please try again.';
|
||||
header("Location: view.php?file=" . urlencode($filePath) . "&shared=0");
|
||||
exit;
|
||||
}
|
||||
|
||||
$stmt->close();
|
||||
$conn->close();
|
||||
}
|
||||
|
||||
$shareCreated = false;
|
||||
@@ -71,7 +64,7 @@ if (isset($_GET['shared'])) {
|
||||
}
|
||||
}
|
||||
|
||||
$filePath = $_GET['file'];
|
||||
$filePath = requireRemotePath($_GET['file'], $defaultPath);
|
||||
$fileName = basename($filePath);
|
||||
$fileExtension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
|
||||
|
||||
@@ -79,13 +72,13 @@ 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'];
|
||||
$imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg'];
|
||||
$videoExtensions = ['mp4', 'webm', 'ogg', 'mov', 'avi', 'mkv'];
|
||||
$audioExtensions = ['mp3', 'wav', 'ogg', 'm4a', 'flac', 'aac'];
|
||||
|
||||
$isImage = in_array($fileExtension, $imageTypes);
|
||||
$isVideo = in_array($fileExtension, $videoTypes);
|
||||
$isAudio = in_array($fileExtension, $audioTypes);
|
||||
$isImage = in_array($fileExtension, $imageExtensions);
|
||||
$isVideo = in_array($fileExtension, $videoExtensions);
|
||||
$isAudio = in_array($fileExtension, $audioExtensions);
|
||||
|
||||
if (!$isImage && !$isVideo && !$isAudio) {
|
||||
die("Unsupported file type");
|
||||
@@ -238,10 +231,10 @@ if (isset($_GET['stream'])) {
|
||||
// Throttle to prevent CPU overload
|
||||
usleep(100000); // 100ms
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
error_log("Streaming error: " . $e->getMessage());
|
||||
} catch (Exception $exception) {
|
||||
error_log("Streaming error: " . $exception->getMessage());
|
||||
if (isset($_GET['debug'])) {
|
||||
header("X-Stream-Error: " . $e->getMessage());
|
||||
header("X-Stream-Error: " . $exception->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,7 +245,6 @@ if (isset($_GET['stream'])) {
|
||||
}
|
||||
?>
|
||||
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -278,7 +270,6 @@ if (isset($_GET['stream'])) {
|
||||
'class' => 'media-page',
|
||||
]); ?>
|
||||
|
||||
|
||||
<section class="content-panel media-panel">
|
||||
<article class="col-12">
|
||||
<div class="media-container position-relative">
|
||||
@@ -301,7 +292,7 @@ if (isset($_GET['stream'])) {
|
||||
</audio>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($_SESSION["downPer"]) && $_SESSION["downPer"] == true) : ?>
|
||||
<?php if (hasPermission('downPer')) : ?>
|
||||
<a href="download.php?file=<?= urlencode($filePath) ?>" class="btn btn-success m-3">Download</a>
|
||||
<?php endif; ?>
|
||||
|
||||
@@ -412,8 +403,6 @@ function copyShareUrl() {
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const mediaElement = document.querySelector('video, audio');
|
||||
|
||||
+39
-71
@@ -1,83 +1,51 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
if (isset($_POST['uname'], $_POST['pswd'])) {
|
||||
require_once __DIR__ . '/../config/config.php';
|
||||
// $username = "UNAME";
|
||||
// $password = "PSWD";
|
||||
// $db = "usbraidlogin";
|
||||
|
||||
$uname = htmlspecialchars($_POST['uname']);
|
||||
$pswd = htmlspecialchars($_POST['pswd']);
|
||||
if (!isset($_POST['uname'], $_POST['pswd'])
|
||||
|| !is_string($_POST['uname']) || !is_string($_POST['pswd'])) {
|
||||
$_SESSION['login_error'] = 'Please fill in all fields.';
|
||||
header('Location: ../index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$conn = getDatabaseConnection();
|
||||
// charset already set in getDatabaseConnection()
|
||||
$username = trim($_POST['uname']);
|
||||
$password = $_POST['pswd'];
|
||||
$loginSuccessful = false;
|
||||
$database = getDatabaseConnection();
|
||||
|
||||
if ($conn->connect_error) {
|
||||
die("Connection failed: " . $conn->connect_error);
|
||||
try {
|
||||
$statement = $database->prepare('SELECT uname, pswd, admin, defPath, downPer, delPer, upPer FROM users WHERE uname = ?');
|
||||
$statement->bind_param('s', $username);
|
||||
$statement->execute();
|
||||
$result = $statement->get_result();
|
||||
|
||||
while ($user = $result->fetch_assoc()) {
|
||||
if (!password_verify($password, $user['pswd'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$sql = "SELECT * FROM users WHERE uname=?";
|
||||
$stmt = $conn->prepare($sql);
|
||||
if (!$stmt) {
|
||||
$_SESSION['login_error'] = "Database error. Please try again.";
|
||||
$conn->close();
|
||||
header("Location: ../index.php");
|
||||
exit();
|
||||
session_regenerate_id(true);
|
||||
$_SESSION['uname'] = $user['uname'];
|
||||
$_SESSION['admin'] = (bool) $user['admin'];
|
||||
$_SESSION['defPath'] = $user['defPath'];
|
||||
foreach (['downPer', 'delPer', 'upPer'] as $permission) {
|
||||
$_SESSION[$permission] = (bool) $user[$permission];
|
||||
}
|
||||
|
||||
$stmt->bind_param("s", $uname);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
|
||||
if ($result && $result->num_rows > 0) {
|
||||
$login_successful = false;
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$hash = $row["pswd"];
|
||||
$admin = (bool)$row["admin"];
|
||||
$defPath = $row["defPath"];
|
||||
$delPer = $row["delPer"];
|
||||
$downPer = $row["downPer"];
|
||||
$upPer = $row["upPer"];
|
||||
|
||||
if (password_verify($pswd, $hash)) {
|
||||
// Login successful - store info in session
|
||||
$_SESSION['uname'] = $uname;
|
||||
$_SESSION['admin'] = $admin;
|
||||
$_SESSION['defPath'] = $defPath;
|
||||
$_SESSION['downPer'] = $downPer;
|
||||
$_SESSION['delPer'] = $delPer;
|
||||
$_SESSION['upPer'] = $upPer;
|
||||
$login_successful = true;
|
||||
unset($_SESSION['login_error']);
|
||||
$loginSuccessful = true;
|
||||
break;
|
||||
}
|
||||
$result->free();
|
||||
$statement->close();
|
||||
if (!$loginSuccessful) {
|
||||
$_SESSION['login_error'] = 'Invalid username or password.';
|
||||
}
|
||||
$result->free_result();
|
||||
$stmt->close();
|
||||
$conn->close();
|
||||
|
||||
if ($login_successful) {
|
||||
header("Location: ftp/index.php");
|
||||
exit();
|
||||
} else {
|
||||
// If password verification fails
|
||||
$_SESSION['login_error'] = "Invalid username or password.";
|
||||
header("Location: ../index.php");
|
||||
exit();
|
||||
}
|
||||
} else {
|
||||
// If no user is found
|
||||
$result->free_result();
|
||||
$stmt->close();
|
||||
$conn->close();
|
||||
$_SESSION['login_error'] = "Invalid username or password.";
|
||||
header("Location: ../index.php");
|
||||
exit();
|
||||
}
|
||||
} else {
|
||||
// If form fields are not set
|
||||
$_SESSION['login_error'] = "Please fill in all fields.";
|
||||
header("Location: ../index.php");
|
||||
exit();
|
||||
} catch (mysqli_sql_exception $exception) {
|
||||
error_log($exception->getMessage());
|
||||
$_SESSION['login_error'] = 'Database error. Please try again.';
|
||||
} finally {
|
||||
$database->close();
|
||||
}
|
||||
?>
|
||||
|
||||
header('Location: ' . ($loginSuccessful ? 'ftp/index.php' : '../index.php'));
|
||||
exit;
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/** Shared session checks. Database, form and session keys retain their legacy names. */
|
||||
function requireLogin(string $loginUrl): void {
|
||||
if (session_status() !== PHP_SESSION_ACTIVE) {
|
||||
session_start();
|
||||
}
|
||||
if (!isset($_SESSION['uname'])) {
|
||||
header('Location: ' . $loginUrl);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
function hasPermission(string $permission): bool {
|
||||
return !empty($_SESSION[$permission]);
|
||||
}
|
||||
|
||||
function requirePermission(string $permission, string $message): void {
|
||||
if (!hasPermission($permission)) {
|
||||
http_response_code(403);
|
||||
exit($message);
|
||||
}
|
||||
}
|
||||
|
||||
function isPasswordValid(string $password): bool {
|
||||
return strlen($password) >= 8
|
||||
&& preg_match('/[0-9]/', $password) === 1
|
||||
&& preg_match('/[A-Z]/', $password) === 1;
|
||||
}
|
||||
@@ -22,3 +22,25 @@ function remoteDirname(string $path): string {
|
||||
$parent = rtrim(substr($path, 0, $separator), '/');
|
||||
return $parent === '' ? '/' : $parent;
|
||||
}
|
||||
|
||||
/** Compare directory boundaries, so /data-other is not considered part of /data. */
|
||||
function isRemotePathWithin(string $path, string $basePath): bool {
|
||||
$path = normalizePath($path);
|
||||
$basePath = normalizePath($basePath);
|
||||
return $path === $basePath || strpos($path, rtrim($basePath, '/') . '/') === 0;
|
||||
}
|
||||
|
||||
/** Validate a single POSIX filename before joining it to a directory. */
|
||||
function isRemoteNameValid(string $name): bool {
|
||||
return $name !== '' && $name !== '.' && $name !== '..'
|
||||
&& strpos($name, '/') === false && strpos($name, "\0") === false;
|
||||
}
|
||||
|
||||
/** Resolve a request path inside the user's configured workspace. */
|
||||
function requireRemotePath(string $path, string $basePath): string {
|
||||
if (strpos($path, "\0") !== false || !isRemotePathWithin($path, $basePath)) {
|
||||
http_response_code(403);
|
||||
exit('Path is outside your workspace.');
|
||||
}
|
||||
return normalizePath($path);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../Web/includes/auth.php';
|
||||
|
||||
function expectBackend(bool $condition, string $message): void {
|
||||
if (!$condition) throw new RuntimeException($message);
|
||||
}
|
||||
|
||||
// Run handlers in separate processes because redirects and permission checks exit.
|
||||
if (($argv[1] ?? '') === '--handler') {
|
||||
session_set_save_handler(new class implements SessionHandlerInterface {
|
||||
public function open(string $path, string $name): bool { return true; }
|
||||
public function close(): bool { return true; }
|
||||
public function read(string $id): string|false { return ''; }
|
||||
public function write(string $id, string $data): bool { return true; }
|
||||
public function destroy(string $id): bool { return true; }
|
||||
public function gc(int $maxLifetime): int|false { return 0; }
|
||||
}, true);
|
||||
session_start(['use_cookies' => false, 'cache_limiter' => '']);
|
||||
http_response_code(200);
|
||||
$scenario = json_decode($argv[3], true, 512, JSON_THROW_ON_ERROR);
|
||||
$_SESSION = $scenario['session'] ?? [];
|
||||
$_POST = $scenario['post'] ?? [];
|
||||
$_GET = $scenario['get'] ?? [];
|
||||
$_FILES = $scenario['files'] ?? [];
|
||||
$_SERVER['REQUEST_METHOD'] = $scenario['method'] ?? 'POST';
|
||||
$defaultPath = '/mnt/raid';
|
||||
$calls = [];
|
||||
|
||||
function initializeSFTP() {
|
||||
global $calls;
|
||||
$calls[] = ['connect'];
|
||||
return new class {
|
||||
function put($path, $data, $mode = null) {
|
||||
global $calls;
|
||||
$calls[] = $mode === null ? ['put', $path, $data] : ['put', $path, $data, $mode];
|
||||
return true;
|
||||
}
|
||||
function is_dir($path) { return true; }
|
||||
function mkdir($path) {
|
||||
global $calls;
|
||||
$calls[] = ['mkdir', $path];
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Minimal database double exercises login without a live database or network.
|
||||
function getDatabaseConnection() {
|
||||
return new class {
|
||||
function prepare($sql) {
|
||||
return new class {
|
||||
function bind_param($types, &...$values) {}
|
||||
function execute() {}
|
||||
function get_result() {
|
||||
return new class {
|
||||
private $fetched = false;
|
||||
function fetch_assoc() {
|
||||
if ($this->fetched) return null;
|
||||
$this->fetched = true;
|
||||
return ['uname' => 'member', 'pswd' => password_hash(' Secret1<&" ', PASSWORD_BCRYPT),
|
||||
'admin' => 0, 'defPath' => '/mnt/raid', 'downPer' => 1, 'delPer' => 0, 'upPer' => 1];
|
||||
}
|
||||
function free() {}
|
||||
};
|
||||
}
|
||||
function close() {}
|
||||
};
|
||||
}
|
||||
function close() {}
|
||||
};
|
||||
}
|
||||
|
||||
register_shutdown_function(function () use (&$calls) {
|
||||
echo "\n__RESULT__" . json_encode(['status' => http_response_code(), 'calls' => $calls, 'session' => $_SESSION]);
|
||||
});
|
||||
$path = realpath(__DIR__ . '/../Web/content/' . $argv[2] . '.php');
|
||||
$source = file_get_contents($path);
|
||||
// Replace only configuration loading, retaining the real guards and handlers.
|
||||
$source = preg_replace("~require_once __DIR__ \\. '/(?:\\.\\./)+config/config\\.php';~", '', $source);
|
||||
$source = str_replace('__DIR__', var_export(dirname($path), true), $source);
|
||||
$source = str_replace('session_start();', '', $source);
|
||||
require_once __DIR__ . '/../Web/includes/remotepaths.php';
|
||||
require_once __DIR__ . '/../Web/vendor/autoload.php';
|
||||
eval('?>' . $source);
|
||||
exit;
|
||||
}
|
||||
|
||||
function runBackendHandler(string $handler, array $scenario): array {
|
||||
$process = proc_open([PHP_BINARY, __FILE__, '--handler', $handler, json_encode($scenario)],
|
||||
[0 => ['pipe', 'r'], 1 => ['pipe', 'w'], 2 => ['pipe', 'w']], $pipes);
|
||||
fclose($pipes[0]);
|
||||
$output = stream_get_contents($pipes[1]);
|
||||
$errors = stream_get_contents($pipes[2]);
|
||||
fclose($pipes[1]);
|
||||
fclose($pipes[2]);
|
||||
$exitCode = proc_close($process);
|
||||
expectBackend($exitCode === 0 && $errors === '', $handler . ' failed: ' . $errors . $output);
|
||||
[$body, $result] = explode("\n__RESULT__", $output, 2);
|
||||
return ['body' => $body] + json_decode($result, true, 512, JSON_THROW_ON_ERROR);
|
||||
}
|
||||
|
||||
foreach (['short1A' => false, 'lowercase1' => false, 'UppercaseOnly' => false, 'Password1' => true, ' Secret1<&" ' => true] as $password => $expected) {
|
||||
expectBackend(isPasswordValid($password) === $expected, 'Incorrect password validation');
|
||||
}
|
||||
|
||||
$member = ['uname' => 'member', 'upPer' => true, 'downPer' => true, 'delPer' => true];
|
||||
foreach (['upload' => 'upPer', 'createdir' => 'upPer', 'createfile' => 'upPer', 'rename' => 'upPer', 'extract' => 'upPer', 'delete' => 'delPer', 'download' => 'downPer'] as $handler => $permission) {
|
||||
$result = runBackendHandler('ftp/' . $handler, []);
|
||||
expectBackend($result['calls'] === [] && $result['body'] === '', $handler . ' must require login before connecting');
|
||||
$result = runBackendHandler('ftp/' . $handler, ['session' => ['uname' => 'member', $permission => false]]);
|
||||
expectBackend($result['status'] === 403 && $result['calls'] === [], $handler . ' must enforce permissions before connecting');
|
||||
}
|
||||
|
||||
$result = runBackendHandler('ftp/createfile', ['session' => $member,
|
||||
'post' => ['action' => 'createFile', 'currentPath' => '/mnt/raid', 'fileName' => 'empty.txt']]);
|
||||
expectBackend($result['status'] === 200 && $result['calls'][1] === ['put', '/mnt/raid/empty.txt', ''], 'New files must contain zero bytes');
|
||||
expectBackend(json_decode($result['body'], true)['success'] === true, 'Create file response must retain its API');
|
||||
|
||||
foreach (['/mnt/raid-other', '/mnt/raid/../private'] as $path) {
|
||||
$result = runBackendHandler('ftp/createdir', ['session' => $member,
|
||||
'post' => ['action' => 'createDir', 'currentPath' => $path, 'dirName' => 'new']]);
|
||||
expectBackend($result['status'] === 403 && count($result['calls']) === 1, 'Reject writes outside workspace');
|
||||
}
|
||||
$result = runBackendHandler('ftp/createdir', ['session' => $member, 'post' => ['action' => 'createDir', 'dirName' => '..']]);
|
||||
expectBackend($result['status'] === 400 && count($result['calls']) === 1, 'Reject parent directory names');
|
||||
$result = runBackendHandler('ftp/delete', ['session' => $member, 'post' => ['delete' => '/mnt/raid']]);
|
||||
expectBackend($result['status'] === 400 && count($result['calls']) === 1, 'Protect workspace root from deletion');
|
||||
$result = runBackendHandler('ftp/rename', ['session' => $member, 'post' => ['path' => '/mnt/raid/item', 'newName' => '../escape']]);
|
||||
expectBackend($result['status'] === 400 && count($result['calls']) === 1, 'Rename must accept a single filename');
|
||||
|
||||
$upload = ['session' => $member, 'post' => ['paths' => ['folder/ok.txt', 'bad.txt']],
|
||||
'files' => ['files' => ['name' => ['ok.txt', 'bad.txt'], 'tmp_name' => ['local-upload', ''],
|
||||
'error' => [UPLOAD_ERR_OK, UPLOAD_ERR_PARTIAL]]]];
|
||||
$result = runBackendHandler('ftp/upload', $upload);
|
||||
expectBackend($result['status'] === 200 && $result['calls'][1] === ['put', '/mnt/raid/folder/ok.txt', 'local-upload', 1], 'Upload must use local file mode and preserve relative directories');
|
||||
expectBackend(strpos($result['body'], 'File was only partially uploaded') !== false, 'Report errors for each uploaded file');
|
||||
$upload['post']['paths'][0] = '../private.txt';
|
||||
$result = runBackendHandler('ftp/upload', $upload);
|
||||
expectBackend($result['status'] === 403 && count($result['calls']) === 1, 'Upload paths must stay in the selected directory');
|
||||
|
||||
$result = runBackendHandler('login', ['post' => ['uname' => 'member', 'pswd' => ' Secret1<&" ']]);
|
||||
expectBackend(($result['session']['uname'] ?? '') === 'member', 'Login must preserve special characters and whitespace in passwords');
|
||||
expectBackend($result['session']['admin'] === false && $result['session']['delPer'] === false && $result['session']['upPer'] === true, 'Login must retain boolean permissions');
|
||||
$result = runBackendHandler('login', ['post' => ['uname' => 'member', 'pswd' => 'wrong']]);
|
||||
expectBackend(!isset($result['session']['uname']) && isset($result['session']['login_error']), 'Reject incorrect passwords');
|
||||
|
||||
echo "Backend: handler permissions, path boundaries, empty files and login checks passed.\n";
|
||||
@@ -28,4 +28,18 @@ foreach (['/etc' => '/etc/config.txt', '/mnt/raid' => '/mnt/raid/config.txt'] as
|
||||
$relative = substr($file, strlen(rtrim(remoteDirname($base), '/')) + 1);
|
||||
if ($relative !== ($base === '/etc' ? 'etc/config.txt' : 'raid/config.txt')) throw new RuntimeException('Incorrect ZIP member path');
|
||||
}
|
||||
echo "Remote paths: POSIX parents, normalization, root navigation and ZIP paths passed.\n";
|
||||
foreach ([
|
||||
['/mnt/raid', '/mnt/raid', true],
|
||||
['/mnt/raid/folder', '/mnt/raid/', true],
|
||||
['/mnt/raid-other/file', '/mnt/raid', false],
|
||||
['/mnt/raid/../private', '/mnt/raid', false],
|
||||
['/mnt/raid/sub/../../private', '/mnt/raid', false],
|
||||
['/mnt/raid/sub/../file', '/mnt/raid', true],
|
||||
['/etc', '/', true],
|
||||
] as [$path, $basePath, $expected]) {
|
||||
if (isRemotePathWithin($path, $basePath) !== $expected) throw new RuntimeException('Incorrect path boundary: ' . $path);
|
||||
}
|
||||
foreach (['' => false, '.' => false, '..' => false, 'a/b' => false, "a\0b" => false, 'file.txt' => true, '0' => true, 'a\\b' => true] as $name => $expected) {
|
||||
if (isRemoteNameValid((string) $name) !== $expected) throw new RuntimeException('Incorrect filename validation');
|
||||
}
|
||||
echo "Remote paths: POSIX parents, normalization, workspace boundaries, names and ZIP paths passed.\n";
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../Web/includes/workspace.php';
|
||||
require_once __DIR__ . '/../Web/includes/auth.php';
|
||||
require_once __DIR__ . '/../Web/includes/remotepaths.php';
|
||||
require_once __DIR__ . '/../Web/content/ftp/lib/serverstats.php';
|
||||
set_error_handler(function ($severity, $message, $file, $line) {
|
||||
@@ -11,11 +12,11 @@ function expectWorkspace($condition, $message) {
|
||||
|
||||
function renderWorkspaceFixture($name, $admin = true, $defaultPath = '/mnt/raid', $currentPath = '/mnt/raid/Workspace') {
|
||||
$_SESSION = ['uname' => 'michal', 'admin' => $admin, 'upPer' => true, 'downPer' => true, 'delPer' => true];
|
||||
$filePath = $currentPath . '/Example.pdf'; $fileName = basename($filePath); $parentDir = $currentPath;
|
||||
$filePath = $currentPath . '/Example.pdf'; $fileName = basename($filePath); $parentPath = $currentPath;
|
||||
$fileExtension = 'png'; $fileSize = 245760; $mimeType = 'image/png';
|
||||
$isImage = true; $isVideo = false; $isAudio = false;
|
||||
$editable = true; $showLineNumbers = true; $content = "{\n \"workspace\": \"USB RAID\",\n \"status\": \"connected\"\n}\n";
|
||||
$message = ''; $messageType = ''; $message_type = ''; $shareCreated = false; $shareError = '';
|
||||
$message = ''; $messageType = ''; $shareCreated = false; $shareError = '';
|
||||
$share = ['expiration' => '2030-01-01']; $downloadAllowed = false; $token = 'test-only';
|
||||
$items = ['Documents', 'Photos', 'README.md', 'Example.pdf'];
|
||||
$imageExtensions = ['png']; $videoExtensions = []; $audioExtensions = []; $editableExtensions = ['md']; $archiveExtensions = [];
|
||||
|
||||
Reference in New Issue
Block a user