mirror of
https://github.com/michalcz10/USB-RAID-Array.git
synced 2026-11-11 16:09:03 +00:00
revise backend
This commit is contained in:
@@ -129,6 +129,7 @@ Z kořene repozitáře:
|
|||||||
php tests/workspace_test.php
|
php tests/workspace_test.php
|
||||||
php tests/serverstats_test.php
|
php tests/serverstats_test.php
|
||||||
php tests/remotepaths_test.php
|
php tests/remotepaths_test.php
|
||||||
|
php tests/backend_test.php
|
||||||
node --check Web/js/theme.js
|
node --check Web/js/theme.js
|
||||||
node --check Web/js/ui.js
|
node --check Web/js/ui.js
|
||||||
node --check Web/content/ftp/js/index.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.
|
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
|
## Časté problémy
|
||||||
|
|
||||||
| Problém | Co zkontrolovat |
|
| Problém | Co zkontrolovat |
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ $SFTP_HOST = '172.16.1.11';
|
|||||||
$SFTP_USER = 'usbraid';
|
$SFTP_USER = 'usbraid';
|
||||||
$SFTP_PASS = 'usbraid';
|
$SFTP_PASS = 'usbraid';
|
||||||
// Default remote path – uses session value if set
|
// Default remote path – uses session value if set
|
||||||
$defaultPath = $_SESSION['defPath'] ?? '/';
|
$defaultPath = normalizePath($_SESSION['defPath'] ?? '/');
|
||||||
|
|
||||||
// SSH credentials (placeholders)
|
// SSH credentials (placeholders)
|
||||||
$SSH_HOST = '172.16.1.11';
|
$SSH_HOST = '172.16.1.11';
|
||||||
@@ -29,12 +29,12 @@ $SSH_PASS = 'usbraid';
|
|||||||
/** Helper: Get a MySQLi connection */
|
/** Helper: Get a MySQLi connection */
|
||||||
function getDatabaseConnection(): mysqli {
|
function getDatabaseConnection(): mysqli {
|
||||||
global $DB_HOST, $DB_USER, $DB_PASS, $DB_NAME;
|
global $DB_HOST, $DB_USER, $DB_PASS, $DB_NAME;
|
||||||
$conn = new mysqli($DB_HOST, $DB_USER, $DB_PASS, $DB_NAME);
|
$database = new mysqli($DB_HOST, $DB_USER, $DB_PASS, $DB_NAME);
|
||||||
if ($conn->connect_error) {
|
if ($database->connect_error) {
|
||||||
die('Connection failed: ' . $conn->connect_error);
|
die('Connection failed: ' . $database->connect_error);
|
||||||
}
|
}
|
||||||
$conn->set_charset('utf8');
|
$database->set_charset('utf8');
|
||||||
return $conn;
|
return $database;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Helper: Initialise SFTP connection */
|
/** Helper: Initialise SFTP connection */
|
||||||
|
|||||||
+59
-118
@@ -1,139 +1,80 @@
|
|||||||
<?php
|
<?php
|
||||||
session_start();
|
require_once __DIR__ . '/../includes/auth.php';
|
||||||
if (empty($_SESSION['admin']) || $_SESSION['admin'] !== true) {
|
requireLogin('../index.php');
|
||||||
session_destroy();
|
requirePermission('admin', 'You do not have permission to manage users.');
|
||||||
header("location: ../index.php");
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
ini_set('display_errors', 1);
|
|
||||||
error_reporting(E_ALL);
|
|
||||||
|
|
||||||
require_once __DIR__ . '/../config/config.php';
|
require_once __DIR__ . '/../config/config.php';
|
||||||
require_once __DIR__ . '/../includes/workspace.php';
|
require_once __DIR__ . '/../includes/workspace.php';
|
||||||
// $username = "UNAME";
|
|
||||||
// $password = "PSWD";
|
|
||||||
// $db = "usbraidlogin";
|
|
||||||
|
|
||||||
$conn = getDatabaseConnection();
|
$database = getDatabaseConnection();
|
||||||
// charset already set in getDatabaseConnection()
|
|
||||||
|
|
||||||
if ($conn->connect_error) {
|
|
||||||
die("Database connection failed: " . $conn->connect_error);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
if (isset($_POST['uname']) && isset($_POST['pswd'])) {
|
$username = is_string($_POST['uname'] ?? null) ? trim($_POST['uname']) : '';
|
||||||
$uname = trim($_POST['uname']);
|
$password = is_string($_POST['pswd'] ?? null) ? $_POST['pswd'] : '';
|
||||||
$pswd = trim($_POST['pswd']);
|
$defaultPath = is_string($_POST['defPath'] ?? null) ? normalizePath(trim($_POST['defPath'])) : '/';
|
||||||
$is_admin = isset($_POST['admin']) ? 1 : 0;
|
$isAdmin = isset($_POST['admin']) ? 1 : 0;
|
||||||
$defPath = isset($_POST['defPath']) ? trim($_POST['defPath']) : '';
|
$canDelete = isset($_POST['delPer']) ? 1 : 0;
|
||||||
$delPer = isset($_POST['delPer']) ? (int)$_POST['delPer'] : 0;
|
$canDownload = isset($_POST['downPer']) ? 1 : 0;
|
||||||
$dowPer = isset($_POST['downPer']) ? (int)$_POST['downPer'] : 0;
|
$canUpload = isset($_POST['upPer']) ? 1 : 0;
|
||||||
$upPer = isset($_POST['upPer']) ? (int)$_POST['upPer'] : 0;
|
$_SESSION['message_type'] = 'error';
|
||||||
|
|
||||||
if (empty($uname) || empty($pswd)) {
|
if ($username === '' || $password === '') {
|
||||||
$_SESSION['message'] = 'Error: Username and password are required!';
|
$_SESSION['message'] = 'Error: Username and password are required!';
|
||||||
$_SESSION['message_type'] = 'error';
|
} elseif (!isPasswordValid($password)) {
|
||||||
} else if (!CheckPassword($pswd)) {
|
|
||||||
$_SESSION['message'] = 'Error: Password must be at least 8 characters long, contain at least one number and one uppercase letter!';
|
$_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 {
|
} else {
|
||||||
$sql_check = "SELECT * FROM users WHERE uname = ?";
|
try {
|
||||||
$stmt_check = $conn->prepare($sql_check);
|
$statement = $database->prepare('SELECT uname FROM users WHERE uname = ?');
|
||||||
if (!$stmt_check) {
|
$statement->bind_param('s', $username);
|
||||||
$_SESSION['message'] = 'Error: Database preparation failed.';
|
$statement->execute();
|
||||||
$_SESSION['message_type'] = 'error';
|
$result = $statement->get_result();
|
||||||
} else {
|
$userExists = $result->num_rows > 0;
|
||||||
$stmt_check->bind_param("s", $uname);
|
$result->free();
|
||||||
$stmt_check->execute();
|
$statement->close();
|
||||||
$result_check = $stmt_check->get_result();
|
|
||||||
|
|
||||||
if ($result_check->num_rows > 0) {
|
if ($userExists) {
|
||||||
$_SESSION['message'] = 'Error: Username already exists!';
|
$_SESSION['message'] = 'Error: Username already exists!';
|
||||||
$_SESSION['message_type'] = 'error';
|
|
||||||
} else {
|
} else {
|
||||||
$sql_insert = "INSERT INTO users (uname, pswd, admin, defPath, delPer, downPer, upPer) VALUES (?, ?, ?, ?, ?, ?, ?)";
|
$passwordHash = password_hash($password, PASSWORD_BCRYPT);
|
||||||
$stmt_insert = $conn->prepare($sql_insert);
|
$statement = $database->prepare('INSERT INTO users (uname, pswd, admin, defPath, delPer, downPer, upPer) VALUES (?, ?, ?, ?, ?, ?, ?)');
|
||||||
if (!$stmt_insert) {
|
$statement->bind_param('ssisiii', $username, $passwordHash, $isAdmin, $defaultPath, $canDelete, $canDownload, $canUpload);
|
||||||
$_SESSION['message'] = 'Error: Database preparation failed.';
|
$statement->execute();
|
||||||
$_SESSION['message_type'] = 'error';
|
$statement->close();
|
||||||
} 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()) {
|
|
||||||
$_SESSION['message'] = 'User added successfully!';
|
$_SESSION['message'] = 'User added successfully!';
|
||||||
$_SESSION['message_type'] = 'success';
|
$_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'] = 'Error: Failed to add user. Please try again later.';
|
||||||
$_SESSION['message_type'] = 'error';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$stmt_check->close();
|
|
||||||
if (isset($stmt_insert)) {
|
|
||||||
$stmt_insert->close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
$database->close();
|
||||||
|
header('Location: adminpanel.php');
|
||||||
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
header("Location: adminpanel.php");
|
if (isset($_GET['delete']) && is_string($_GET['delete'])) {
|
||||||
exit();
|
$usernameToDelete = $_GET['delete'];
|
||||||
} else {
|
try {
|
||||||
$_SESSION['message'] = 'Error: Missing form data!';
|
$statement = $database->prepare('DELETE FROM users WHERE uname = ?');
|
||||||
$_SESSION['message_type'] = 'error';
|
$statement->bind_param('s', $usernameToDelete);
|
||||||
header("Location: adminpanel.php");
|
$statement->execute();
|
||||||
exit();
|
$statement->close();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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()) {
|
|
||||||
$_SESSION['message'] = 'User deleted successfully!';
|
$_SESSION['message'] = 'User deleted successfully!';
|
||||||
$_SESSION['message_type'] = 'success';
|
$_SESSION['message_type'] = 'success';
|
||||||
} else {
|
} catch (mysqli_sql_exception $exception) {
|
||||||
|
error_log($exception->getMessage());
|
||||||
$_SESSION['message'] = 'Error: Failed to delete user.';
|
$_SESSION['message'] = 'Error: Failed to delete user.';
|
||||||
$_SESSION['message_type'] = 'error';
|
$_SESSION['message_type'] = 'error';
|
||||||
}
|
}
|
||||||
$stmt->close();
|
$database->close();
|
||||||
} else {
|
header('Location: adminpanel.php');
|
||||||
$_SESSION['message'] = 'Error: Database preparation failed.';
|
exit;
|
||||||
$_SESSION['message_type'] = 'error';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
header("Location: adminpanel.php");
|
$result = $database->query('SELECT uname, admin, defPath, delPer, downPer, upPer FROM users');
|
||||||
exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
$result = $conn->query("SELECT uname, admin, defPath, delPer, downPer, upPer FROM users");
|
|
||||||
|
|
||||||
$message = $_SESSION['message'] ?? '';
|
$message = $_SESSION['message'] ?? '';
|
||||||
$message_type = $_SESSION['message_type'] ?? '';
|
$messageType = $_SESSION['message_type'] ?? '';
|
||||||
unset($_SESSION['message']);
|
unset($_SESSION['message'], $_SESSION['message_type']);
|
||||||
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;
|
|
||||||
}
|
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
@@ -169,7 +110,7 @@ function CheckPassword($password) {
|
|||||||
<section class="users-layout">
|
<section class="users-layout">
|
||||||
|
|
||||||
<?php if ($message): ?>
|
<?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); ?>
|
<?php echo htmlspecialchars($message); ?>
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
@@ -223,16 +164,16 @@ function CheckPassword($password) {
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<?php while ($row = $result->fetch_assoc()) { ?>
|
<?php while ($user = $result->fetch_assoc()) { ?>
|
||||||
<tr>
|
<tr>
|
||||||
<td><?php echo htmlspecialchars($row['uname']); ?></td>
|
<td><?php echo htmlspecialchars($user['uname']); ?></td>
|
||||||
<td><?php echo $row['admin'] ? 'Yes' : 'No'; ?></td>
|
<td><?php echo $user['admin'] ? 'Yes' : 'No'; ?></td>
|
||||||
<td><?php echo htmlspecialchars($row['defPath']); ?></td>
|
<td><?php echo htmlspecialchars($user['defPath']); ?></td>
|
||||||
<td><?php echo $row['delPer'] ? 'Yes' : 'No'; ?></td>
|
<td><?php echo $user['delPer'] ? 'Yes' : 'No'; ?></td>
|
||||||
<td><?php echo $row['downPer'] ? 'Yes' : 'No'; ?></td>
|
<td><?php echo $user['downPer'] ? 'Yes' : 'No'; ?></td>
|
||||||
<td><?php echo $row['upPer'] ? 'Yes' : 'No'; ?></td>
|
<td><?php echo $user['upPer'] ? 'Yes' : 'No'; ?></td>
|
||||||
<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>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
@@ -244,5 +185,5 @@ function CheckPassword($password) {
|
|||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
<?php
|
<?php
|
||||||
$conn->close();
|
$database->close();
|
||||||
?>
|
?>
|
||||||
|
|||||||
@@ -1,95 +1,56 @@
|
|||||||
<?php
|
<?php
|
||||||
session_start();
|
require_once __DIR__ . '/../includes/auth.php';
|
||||||
|
requireLogin('../index.php');
|
||||||
if (!isset($_SESSION['uname'])) {
|
|
||||||
header("location: ../index.php");
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
require_once __DIR__ . '/../config/config.php';
|
require_once __DIR__ . '/../config/config.php';
|
||||||
require_once __DIR__ . '/../includes/workspace.php';
|
require_once __DIR__ . '/../includes/workspace.php';
|
||||||
// $username = "UNAME";
|
|
||||||
// $password = "PSWD";
|
|
||||||
// $db = "usbraidlogin";
|
|
||||||
|
|
||||||
$message = "";
|
$message = '';
|
||||||
$messageType = "";
|
$messageType = '';
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
if (isset($_POST['current_password'], $_POST['new_password'], $_POST['confirm_password'])) {
|
$messageType = 'danger';
|
||||||
$current_password = htmlspecialchars($_POST['current_password']);
|
$currentPassword = $_POST['current_password'] ?? null;
|
||||||
$new_password = htmlspecialchars($_POST['new_password']);
|
$newPassword = $_POST['new_password'] ?? null;
|
||||||
$confirm_password = htmlspecialchars($_POST['confirm_password']);
|
$confirmPassword = $_POST['confirm_password'] ?? null;
|
||||||
$uname = $_SESSION['uname'];
|
|
||||||
|
|
||||||
if(!CheckPassword($new_password)) {
|
if (!is_string($currentPassword) || !is_string($newPassword) || !is_string($confirmPassword)) {
|
||||||
$message = "New password must be at least 8 characters long, contain at least one number and one uppercase letter!";
|
$message = 'All fields are required!';
|
||||||
$messageType = "danger";
|
} elseif (!isPasswordValid($newPassword)) {
|
||||||
} else if ($new_password !== $confirm_password) {
|
$message = 'New password must be at least 8 characters long, contain at least one number and one uppercase letter!';
|
||||||
$message = "New passwords do not match!";
|
} elseif ($newPassword !== $confirmPassword) {
|
||||||
$messageType = "danger";
|
$message = 'New passwords do not match!';
|
||||||
} else {
|
} else {
|
||||||
$conn = getDatabaseConnection();
|
$database = getDatabaseConnection();
|
||||||
// charset already set in 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) {
|
if (!$user) {
|
||||||
$message = "Database connection failed!";
|
$message = 'User not found!';
|
||||||
$messageType = "danger";
|
} elseif (!password_verify($currentPassword, $user['pswd'])) {
|
||||||
|
$message = 'Current password is incorrect!';
|
||||||
} else {
|
} else {
|
||||||
$sql = "SELECT pswd FROM users WHERE uname=?";
|
$passwordHash = password_hash($newPassword, PASSWORD_BCRYPT);
|
||||||
$stmt = $conn->prepare($sql);
|
$statement = $database->prepare('UPDATE users SET pswd = ? WHERE uname = ?');
|
||||||
$stmt->bind_param("s", $uname);
|
$statement->bind_param('ss', $passwordHash, $username);
|
||||||
$stmt->execute();
|
$statement->execute();
|
||||||
$result = $stmt->get_result();
|
$statement->close();
|
||||||
|
$message = 'Password changed successfully!';
|
||||||
if ($result && $result->num_rows > 0) {
|
$messageType = 'success';
|
||||||
$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";
|
|
||||||
}
|
}
|
||||||
$update_stmt->close();
|
} catch (mysqli_sql_exception $exception) {
|
||||||
} else {
|
error_log($exception->getMessage());
|
||||||
$message = "Current password is incorrect!";
|
$message = 'Unable to change password. Please try again.';
|
||||||
$messageType = "danger";
|
} finally {
|
||||||
}
|
$database->close();
|
||||||
} else {
|
|
||||||
$message = "User not found!";
|
|
||||||
$messageType = "danger";
|
|
||||||
}
|
|
||||||
|
|
||||||
$stmt->close();
|
|
||||||
$conn->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
|
<?php
|
||||||
// Include this at the top to see potential errors
|
require_once __DIR__ . '/../../includes/auth.php';
|
||||||
// Comment out in production
|
requireLogin('../../index.php');
|
||||||
ini_set('display_errors', 1);
|
requirePermission('upPer', 'You do not have permission to perform this operation.');
|
||||||
error_reporting(E_ALL);
|
|
||||||
|
|
||||||
require_once __DIR__ . '/../../config/config.php';
|
require_once __DIR__ . '/../../config/config.php';
|
||||||
|
|
||||||
$sftp = initializeSFTP();
|
$sftp = initializeSFTP();
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'createDir') {
|
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'])) {
|
if (!isset($_POST['dirName'])) {
|
||||||
http_response_code(400);
|
http_response_code(400);
|
||||||
@@ -17,17 +16,18 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$dirName = $_POST['dirName'];
|
$directoryName = $_POST['dirName'];
|
||||||
|
|
||||||
$dirName = preg_replace('/[^\w\-\.]/', '_', $dirName);
|
$directoryName = preg_replace('/[^\w\-\.]/', '_', $directoryName);
|
||||||
|
|
||||||
if (strpos($currentPath, $defaultPath) !== 0) {
|
if (!isRemoteNameValid($directoryName)) {
|
||||||
$currentPath = $defaultPath;
|
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!";
|
echo "Directory created successfully!";
|
||||||
} else {
|
} else {
|
||||||
http_response_code(500);
|
http_response_code(500);
|
||||||
|
|||||||
@@ -1,29 +1,14 @@
|
|||||||
<?php
|
<?php
|
||||||
// Include this at the top to see potential errors
|
require_once __DIR__ . '/../../includes/auth.php';
|
||||||
// Comment out in production
|
requireLogin('../../index.php');
|
||||||
ini_set('display_errors', 1);
|
requirePermission('upPer', 'You do not have permission to perform this operation.');
|
||||||
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__ . '/../../config/config.php';
|
require_once __DIR__ . '/../../config/config.php';
|
||||||
|
|
||||||
$sftp = initializeSFTP();
|
$sftp = initializeSFTP();
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'createFile') {
|
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'])) {
|
if (!isset($_POST['fileName'])) {
|
||||||
http_response_code(400);
|
http_response_code(400);
|
||||||
@@ -41,22 +26,16 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['
|
|||||||
|
|
||||||
$fileName = preg_replace('/[^\w\-\.]/', '_', $fileName);
|
$fileName = preg_replace('/[^\w\-\.]/', '_', $fileName);
|
||||||
|
|
||||||
if (strpos($currentPath, $defaultPath) !== 0) {
|
if (!isRemoteNameValid($fileName)) {
|
||||||
$currentPath = $defaultPath;
|
http_response_code(400);
|
||||||
|
exit('Invalid file name.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$newFilePath = $currentPath . '/' . $fileName;
|
$newFilePath = rtrim($currentPath, '/') . '/' . $fileName;
|
||||||
|
header('Content-Type: application/json; charset=UTF-8');
|
||||||
$tempFile = tempnam(sys_get_temp_dir(), 'new_file_');
|
if ($sftp->put($newFilePath, '')) {
|
||||||
file_put_contents($tempFile, '');
|
|
||||||
|
|
||||||
if ($sftp->put($newFilePath, $tempFile)) {
|
|
||||||
@unlink($tempFile);
|
|
||||||
|
|
||||||
echo json_encode(['success' => true, 'filePath' => $newFilePath]);
|
echo json_encode(['success' => true, 'filePath' => $newFilePath]);
|
||||||
} else {
|
} else {
|
||||||
@unlink($tempFile);
|
|
||||||
|
|
||||||
http_response_code(500);
|
http_response_code(500);
|
||||||
echo json_encode(['success' => false, 'message' => 'Failed to create file.']);
|
echo json_encode(['success' => false, 'message' => 'Failed to create file.']);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,35 @@
|
|||||||
<?php
|
<?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';
|
require_once __DIR__ . '/../../config/config.php';
|
||||||
|
|
||||||
$sftp = initializeSFTP();
|
$sftp = initializeSFTP();
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete'])) {
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete'])) {
|
||||||
$itemToDelete = $_POST['delete'];
|
$itemToDelete = requireRemotePath($_POST['delete'], $defaultPath);
|
||||||
$parentDir = remoteDirname($itemToDelete);
|
if ($itemToDelete === $defaultPath) {
|
||||||
|
http_response_code(400);
|
||||||
|
exit('Cannot delete the workspace root.');
|
||||||
|
}
|
||||||
|
$parentPath = remoteDirname($itemToDelete);
|
||||||
|
|
||||||
if ($parentDir === '/' || $parentDir === '.') {
|
if ($parentPath === '/' || $parentPath === '.') {
|
||||||
$parentDir = $defaultPath;
|
$parentPath = $defaultPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($sftp->is_dir($itemToDelete)) {
|
if ($sftp->is_dir($itemToDelete)) {
|
||||||
function deleteFolder($sftp, $folderPath) {
|
function deleteFolder($sftp, $folderPath) {
|
||||||
$items = $sftp->nlist($folderPath);
|
$items = $sftp->nlist($folderPath);
|
||||||
|
if ($items === false) return false;
|
||||||
foreach ($items as $item) {
|
foreach ($items as $item) {
|
||||||
if ($item === '.' || $item === '..') continue;
|
if ($item === '.' || $item === '..') continue;
|
||||||
$itemPath = $folderPath . '/' . $item;
|
$itemPath = $folderPath . '/' . $item;
|
||||||
if ($sftp->is_dir($itemPath)) {
|
if ($sftp->is_dir($itemPath)) {
|
||||||
deleteFolder($sftp, $itemPath);
|
if (!deleteFolder($sftp, $itemPath)) return false;
|
||||||
} else {
|
} else {
|
||||||
$sftp->delete($itemPath);
|
if (!$sftp->delete($itemPath)) return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $sftp->rmdir($folderPath);
|
return $sftp->rmdir($folderPath);
|
||||||
@@ -31,7 +40,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete'])) {
|
|||||||
$success = $sftp->delete($itemToDelete);
|
$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;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
<?php
|
<?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
|
set_time_limit(900); // 15 minutes max execution time
|
||||||
// Comment out in production
|
|
||||||
ini_set('display_errors', 0);
|
|
||||||
error_reporting(0);
|
|
||||||
|
|
||||||
ob_start();
|
ob_start();
|
||||||
|
|
||||||
@@ -18,12 +18,12 @@ try {
|
|||||||
try {
|
try {
|
||||||
$sftp = initializeSFTP();
|
$sftp = initializeSFTP();
|
||||||
file_put_contents($logFile, "SFTP connection established\n", FILE_APPEND);
|
file_put_contents($logFile, "SFTP connection established\n", FILE_APPEND);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $exception) {
|
||||||
file_put_contents($logFile, "SFTP connection failed: " . $e->getMessage() . "\n", FILE_APPEND);
|
file_put_contents($logFile, "SFTP connection failed: " . $exception->getMessage() . "\n", FILE_APPEND);
|
||||||
throw new Exception("Failed to connect to SFTP server: " . $e->getMessage());
|
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);
|
file_put_contents($logFile, "Processing directory: $currentPath\n", FILE_APPEND);
|
||||||
|
|
||||||
$files = $sftp->nlist($currentPath);
|
$files = $sftp->nlist($currentPath);
|
||||||
@@ -35,36 +35,35 @@ try {
|
|||||||
|
|
||||||
file_put_contents($logFile, "Found " . count($files) . " items in $currentPath\n", FILE_APPEND);
|
file_put_contents($logFile, "Found " . count($files) . " items in $currentPath\n", FILE_APPEND);
|
||||||
|
|
||||||
foreach ($files as $file) {
|
foreach ($files as $fileName) {
|
||||||
if ($file == '.' || $file == '..') continue;
|
if ($fileName == '.' || $fileName == '..') continue;
|
||||||
|
|
||||||
$fullRemotePath = rtrim($currentPath, '/') . '/' . $file;
|
$remotePath = rtrim($currentPath, '/') . '/' . $fileName;
|
||||||
|
|
||||||
$baseDirName = basename($remoteBasePath);
|
$relativePath = substr($remotePath, strlen(rtrim(remoteDirname($remoteBasePath), '/')) + 1);
|
||||||
$relPathFromBase = substr($fullRemotePath, strlen(rtrim(remoteDirname($remoteBasePath), '/')) + 1);
|
file_put_contents($logFile, "Processing: $remotePath (relative: $relativePath)\n", FILE_APPEND);
|
||||||
file_put_contents($logFile, "Processing: $fullRemotePath (relative: $relPathFromBase)\n", FILE_APPEND);
|
|
||||||
|
|
||||||
$isDir = $sftp->is_dir($fullRemotePath);
|
$isDirectory = $sftp->is_dir($remotePath);
|
||||||
|
|
||||||
if ($isDir) {
|
if ($isDirectory) {
|
||||||
file_put_contents($logFile, "Found subdirectory: $fullRemotePath\n", FILE_APPEND);
|
file_put_contents($logFile, "Found subdirectory: $remotePath\n", FILE_APPEND);
|
||||||
$zip->addEmptyDir($relPathFromBase);
|
$zip->addEmptyDir($relativePath);
|
||||||
|
|
||||||
zipFolderRecursive($sftp, $remoteBasePath, $fullRemotePath, $zip, $logFile, $tempFiles);
|
zipFolderRecursive($sftp, $remoteBasePath, $remotePath, $zip, $logFile, $temporaryFiles);
|
||||||
} else {
|
} else {
|
||||||
$localTempFile = tempnam(sys_get_temp_dir(), 'sftp');
|
$temporaryFile = tempnam(sys_get_temp_dir(), 'sftp');
|
||||||
$tempFiles[] = $localTempFile;
|
$temporaryFiles[] = $temporaryFile;
|
||||||
file_put_contents($logFile, "Downloading to temp file: $localTempFile\n", FILE_APPEND);
|
file_put_contents($logFile, "Downloading to temp file: $temporaryFile\n", FILE_APPEND);
|
||||||
|
|
||||||
$downloadStart = time();
|
$downloadStart = time();
|
||||||
$downloadSuccess = $sftp->get($fullRemotePath, $localTempFile);
|
$downloadSuccess = $sftp->get($remotePath, $temporaryFile);
|
||||||
$downloadTime = time() - $downloadStart;
|
$downloadTime = time() - $downloadStart;
|
||||||
|
|
||||||
if ($downloadSuccess) {
|
if ($downloadSuccess) {
|
||||||
$fileSize = filesize($localTempFile);
|
$fileSize = filesize($temporaryFile);
|
||||||
file_put_contents($logFile, "Download successful ($downloadTime seconds), size: $fileSize bytes\n", FILE_APPEND);
|
file_put_contents($logFile, "Download successful ($downloadTime seconds), size: $fileSize bytes\n", FILE_APPEND);
|
||||||
|
|
||||||
$zip->addFile($localTempFile, $relPathFromBase);
|
$zip->addFile($temporaryFile, $relativePath);
|
||||||
} else {
|
} else {
|
||||||
file_put_contents($logFile, "Failed to download file after $downloadTime seconds\n", FILE_APPEND);
|
file_put_contents($logFile, "Failed to download file after $downloadTime seconds\n", FILE_APPEND);
|
||||||
}
|
}
|
||||||
@@ -83,17 +82,17 @@ try {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$tempFiles = [];
|
$temporaryFiles = [];
|
||||||
|
|
||||||
try {
|
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);
|
file_put_contents($logFile, "Closing zip file\n", FILE_APPEND);
|
||||||
$zipSuccess = $zip->close();
|
$zipSuccess = $zip->close();
|
||||||
|
|
||||||
foreach ($tempFiles as $tempFile) {
|
foreach ($temporaryFiles as $temporaryFile) {
|
||||||
if (file_exists($tempFile)) {
|
if (file_exists($temporaryFile)) {
|
||||||
@unlink($tempFile);
|
@unlink($temporaryFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,39 +103,39 @@ try {
|
|||||||
file_put_contents($logFile, "Failed to create zip\n", FILE_APPEND);
|
file_put_contents($logFile, "Failed to create zip\n", FILE_APPEND);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} catch (Exception $e) {
|
} catch (Exception $exception) {
|
||||||
foreach ($tempFiles as $tempFile) {
|
foreach ($temporaryFiles as $temporaryFile) {
|
||||||
if (file_exists($tempFile)) {
|
if (file_exists($temporaryFile)) {
|
||||||
@unlink($tempFile);
|
@unlink($temporaryFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
file_put_contents($logFile, "Error during zip creation: " . $e->getMessage() . "\n", FILE_APPEND);
|
file_put_contents($logFile, "Error during zip creation: " . $exception->getMessage() . "\n", FILE_APPEND);
|
||||||
throw $e;
|
throw $exception;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['file'])) ||
|
if (($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['file'])) ||
|
||||||
($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['file']))) {
|
($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['file']))) {
|
||||||
|
|
||||||
$file = $_SERVER['REQUEST_METHOD'] === 'POST' ? $_POST['file'] : $_GET['file'];
|
$filePath = requireRemotePath($_SERVER['REQUEST_METHOD'] === 'POST' ? $_POST['file'] : $_GET['file'], $defaultPath);
|
||||||
file_put_contents($logFile, "Requested file: $file\n", FILE_APPEND);
|
file_put_contents($logFile, "Requested file: $filePath\n", FILE_APPEND);
|
||||||
|
|
||||||
$fileExists = $sftp->file_exists($file);
|
$fileExists = $sftp->file_exists($filePath);
|
||||||
$isDir = $sftp->is_dir($file);
|
$isDirectory = $sftp->is_dir($filePath);
|
||||||
|
|
||||||
file_put_contents($logFile, "File exists: " . ($fileExists ? "Yes" : "No") . "\n", FILE_APPEND);
|
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) {
|
if (!$fileExists && !$isDirectory) {
|
||||||
throw new Exception("File not found: $file");
|
throw new Exception("File not found: $filePath");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($isDir) {
|
if ($isDirectory) {
|
||||||
$zipFilePath = tempnam(sys_get_temp_dir(), 'folder_') . '.zip';
|
$zipFilePath = tempnam(sys_get_temp_dir(), 'folder_') . '.zip';
|
||||||
file_put_contents($logFile, "Creating zip at: $zipFilePath\n", FILE_APPEND);
|
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) {
|
if (file_exists($zipFilePath) && filesize($zipFilePath) > 0) {
|
||||||
file_put_contents($logFile, "Sending zip file to browser, size: " . filesize($zipFilePath) . " bytes\n", FILE_APPEND);
|
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-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('Content-Length: ' . filesize($zipFilePath));
|
||||||
header('Cache-Control: no-cache, must-revalidate');
|
header('Cache-Control: no-cache, must-revalidate');
|
||||||
header('Pragma: no-cache');
|
header('Pragma: no-cache');
|
||||||
@@ -161,19 +160,19 @@ try {
|
|||||||
throw new Exception("Failed to create zip archive");
|
throw new Exception("Failed to create zip archive");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$localFile = basename($file);
|
$fileName = basename($filePath);
|
||||||
file_put_contents($logFile, "Downloading single file: $localFile\n", FILE_APPEND);
|
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();
|
$downloadStart = time();
|
||||||
$downloadSuccess = $sftp->get($file, $tempFile);
|
$downloadSuccess = $sftp->get($filePath, $temporaryFile);
|
||||||
$downloadTime = time() - $downloadStart;
|
$downloadTime = time() - $downloadStart;
|
||||||
|
|
||||||
file_put_contents($logFile, "Download " . ($downloadSuccess ? "successful" : "failed") . " ($downloadTime seconds)\n", FILE_APPEND);
|
file_put_contents($logFile, "Download " . ($downloadSuccess ? "successful" : "failed") . " ($downloadTime seconds)\n", FILE_APPEND);
|
||||||
|
|
||||||
if ($downloadSuccess) {
|
if ($downloadSuccess) {
|
||||||
$fileSize = filesize($tempFile);
|
$fileSize = filesize($temporaryFile);
|
||||||
file_put_contents($logFile, "Downloaded file size: $fileSize bytes\n", FILE_APPEND);
|
file_put_contents($logFile, "Downloaded file size: $fileSize bytes\n", FILE_APPEND);
|
||||||
|
|
||||||
if ($fileSize > 0) {
|
if ($fileSize > 0) {
|
||||||
@@ -182,29 +181,29 @@ try {
|
|||||||
}
|
}
|
||||||
|
|
||||||
header('Content-Type: application/octet-stream');
|
header('Content-Type: application/octet-stream');
|
||||||
header('Content-Disposition: attachment; filename="' . $localFile . '"');
|
header('Content-Disposition: attachment; filename="' . $fileName . '"');
|
||||||
header('Content-Length: ' . $fileSize);
|
header('Content-Length: ' . $fileSize);
|
||||||
header('Cache-Control: no-cache, must-revalidate');
|
header('Cache-Control: no-cache, must-revalidate');
|
||||||
header('Pragma: no-cache');
|
header('Pragma: no-cache');
|
||||||
|
|
||||||
readfile($tempFile);
|
readfile($temporaryFile);
|
||||||
file_put_contents($logFile, "Download completed\n", FILE_APPEND);
|
file_put_contents($logFile, "Download completed\n", FILE_APPEND);
|
||||||
@unlink($tempFile);
|
@unlink($temporaryFile);
|
||||||
exit;
|
exit;
|
||||||
} else {
|
} else {
|
||||||
@unlink($tempFile);
|
@unlink($temporaryFile);
|
||||||
throw new Exception("Downloaded file is empty");
|
throw new Exception("Downloaded file is empty");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@unlink($tempFile);
|
@unlink($temporaryFile);
|
||||||
throw new Exception("Failed to download file from SFTP server");
|
throw new Exception("Failed to download file from SFTP server");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Exception("Invalid request method or missing file parameter");
|
throw new Exception("Invalid request method or missing file parameter");
|
||||||
}
|
}
|
||||||
} catch (Exception $e) {
|
} catch (Exception $exception) {
|
||||||
$errorMessage = "Error: " . $e->getMessage();
|
$errorMessage = "Error: " . $exception->getMessage();
|
||||||
file_put_contents($logFile, $errorMessage . "\n", FILE_APPEND);
|
file_put_contents($logFile, $errorMessage . "\n", FILE_APPEND);
|
||||||
|
|
||||||
while (ob_get_level()) {
|
while (ob_get_level()) {
|
||||||
|
|||||||
+24
-37
@@ -1,20 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
session_start();
|
require_once __DIR__ . '/../../includes/auth.php';
|
||||||
|
requireLogin('../../index.php');
|
||||||
if(!isset($_SESSION['uname'])){
|
requirePermission('upPer', 'You do not have permission to perform this operation.');
|
||||||
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__ . '/../../config/config.php';
|
require_once __DIR__ . '/../../config/config.php';
|
||||||
use phpseclib3\Net\SFTP;
|
use phpseclib3\Net\SFTP;
|
||||||
@@ -23,21 +10,21 @@ $sftp = initializeSFTP();
|
|||||||
|
|
||||||
// Process extraction request
|
// Process extraction request
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['archive'])) {
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['archive'])) {
|
||||||
$archivePath = $_POST['archive'];
|
$archivePath = requireRemotePath($_POST['archive'], $defaultPath);
|
||||||
$dirPath = remoteDirname($archivePath);
|
$directoryPath = remoteDirname($archivePath);
|
||||||
$archiveName = basename($archivePath);
|
$archiveName = basename($archivePath);
|
||||||
$extension = pathinfo($archiveName, PATHINFO_EXTENSION);
|
$extension = pathinfo($archiveName, PATHINFO_EXTENSION);
|
||||||
|
|
||||||
// Create a temporary local directory for extraction
|
// Create a temporary local directory for extraction
|
||||||
$tempDir = sys_get_temp_dir() . '/sftp_extract_' . time();
|
$temporaryDirectory = sys_get_temp_dir() . '/sftp_extract_' . time();
|
||||||
if (!mkdir($tempDir, 0777, true)) {
|
if (!mkdir($temporaryDirectory, 0777, true)) {
|
||||||
die("Failed to create temporary directory");
|
die("Failed to create temporary directory");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Download the archive to the temp directory
|
// Download the archive to the temp directory
|
||||||
$localArchivePath = $tempDir . '/' . $archiveName;
|
$localArchivePath = $temporaryDirectory . '/' . $archiveName;
|
||||||
if (!$sftp->get($archivePath, $localArchivePath)) {
|
if (!$sftp->get($archivePath, $localArchivePath)) {
|
||||||
rmdir($tempDir);
|
rmdir($temporaryDirectory);
|
||||||
die("Failed to download the archive");
|
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
|
// Determine the extraction method based on file extension
|
||||||
switch (strtolower($extension)) {
|
switch (strtolower($extension)) {
|
||||||
case 'zip':
|
case 'zip':
|
||||||
$extractionSuccess = extractZip($localArchivePath, $tempDir);
|
$extractionSuccess = extractZip($localArchivePath, $temporaryDirectory);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'rar':
|
case 'rar':
|
||||||
$extractionSuccess = extractRar($localArchivePath, $tempDir);
|
$extractionSuccess = extractRar($localArchivePath, $temporaryDirectory);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'tar':
|
case 'tar':
|
||||||
@@ -60,7 +47,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['archive'])) {
|
|||||||
case 'bz2':
|
case 'bz2':
|
||||||
case 'xz':
|
case 'xz':
|
||||||
case '7z':
|
case '7z':
|
||||||
$extractionSuccess = extractArchive($localArchivePath, $tempDir);
|
$extractionSuccess = extractArchive($localArchivePath, $temporaryDirectory);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -69,20 +56,20 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['archive'])) {
|
|||||||
|
|
||||||
if ($extractionSuccess) {
|
if ($extractionSuccess) {
|
||||||
// Upload extracted files back to the server
|
// Upload extracted files back to the server
|
||||||
uploadExtractedFiles($sftp, $tempDir, $dirPath);
|
uploadExtractedFiles($sftp, $temporaryDirectory, $directoryPath);
|
||||||
|
|
||||||
// Clean up temporary directory
|
// Clean up temporary directory
|
||||||
deleteDirectory($tempDir);
|
deleteDirectory($temporaryDirectory);
|
||||||
|
|
||||||
// Redirect back to the file listing
|
// Redirect back to the file listing
|
||||||
header("Location: index.php?path=" . urlencode($dirPath));
|
header("Location: index.php?path=" . urlencode($directoryPath));
|
||||||
exit;
|
exit;
|
||||||
} else {
|
} else {
|
||||||
die("Failed to extract the archive");
|
die("Failed to extract the archive");
|
||||||
}
|
}
|
||||||
} catch (Exception $e) {
|
} catch (Exception $exception) {
|
||||||
deleteDirectory($tempDir);
|
deleteDirectory($temporaryDirectory);
|
||||||
die("Error during extraction: " . $e->getMessage());
|
die("Error during extraction: " . $exception->getMessage());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
header("Location: index.php");
|
header("Location: index.php");
|
||||||
@@ -140,7 +127,7 @@ function extractArchive($archivePath, $destination) {
|
|||||||
$command = '';
|
$command = '';
|
||||||
|
|
||||||
// Change to the destination directory
|
// Change to the destination directory
|
||||||
$currentDir = getcwd();
|
$originalDirectory = getcwd();
|
||||||
chdir($destination);
|
chdir($destination);
|
||||||
|
|
||||||
if (PHP_OS_FAMILY === 'Windows') {
|
if (PHP_OS_FAMILY === 'Windows') {
|
||||||
@@ -189,20 +176,20 @@ function extractArchive($archivePath, $destination) {
|
|||||||
exec($command, $output, $returnCode);
|
exec($command, $output, $returnCode);
|
||||||
|
|
||||||
// Change back to the original directory
|
// Change back to the original directory
|
||||||
chdir($currentDir);
|
chdir($originalDirectory);
|
||||||
|
|
||||||
return $returnCode === 0;
|
return $returnCode === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upload extracted files back to the SFTP server
|
// Upload extracted files back to the SFTP server
|
||||||
function uploadExtractedFiles($sftp, $localDir, $remoteDir) {
|
function uploadExtractedFiles($sftp, $localDirectory, $remoteDirectory) {
|
||||||
$items = scandir($localDir);
|
$items = scandir($localDirectory);
|
||||||
|
|
||||||
foreach ($items as $item) {
|
foreach ($items as $item) {
|
||||||
if ($item === '.' || $item === '..') continue;
|
if ($item === '.' || $item === '..') continue;
|
||||||
|
|
||||||
$localPath = $localDir . '/' . $item;
|
$localPath = $localDirectory . '/' . $item;
|
||||||
$remotePath = $remoteDir . '/' . $item;
|
$remotePath = $remoteDirectory . '/' . $item;
|
||||||
|
|
||||||
if (is_dir($localPath)) {
|
if (is_dir($localPath)) {
|
||||||
// Create the directory on the remote server
|
// Create the directory on the remote server
|
||||||
|
|||||||
+19
-26
@@ -1,24 +1,13 @@
|
|||||||
<?php
|
<?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);
|
|
||||||
|
|
||||||
require_once __DIR__ . '/../../config/config.php';
|
require_once __DIR__ . '/../../config/config.php';
|
||||||
require_once __DIR__ . '/../../includes/workspace.php';
|
require_once __DIR__ . '/../../includes/workspace.php';
|
||||||
$sftp = initializeSFTP();
|
$sftp = initializeSFTP();
|
||||||
|
|
||||||
$defaultPath = normalizePath($defaultPath);
|
|
||||||
$currentPath = isset($_GET['path']) ? normalizePath($_GET['path']) : $defaultPath;
|
$currentPath = isset($_GET['path']) ? normalizePath($_GET['path']) : $defaultPath;
|
||||||
if ($currentPath !== $defaultPath && strpos($currentPath, rtrim($defaultPath, '/') . '/') !== 0) {
|
if (!isRemotePathWithin($currentPath, $defaultPath)) {
|
||||||
$currentPath = $defaultPath;
|
$currentPath = $defaultPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,6 +16,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
$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'];
|
$imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg'];
|
||||||
$videoExtensions = ['mp4', 'webm', 'ogg', 'mov', 'avi', 'mkv'];
|
$videoExtensions = ['mp4', 'webm', 'ogg', 'mov', 'avi', 'mkv'];
|
||||||
@@ -67,7 +60,7 @@
|
|||||||
<div class="file-content">
|
<div class="file-content">
|
||||||
<!-- Action Buttons -->
|
<!-- Action Buttons -->
|
||||||
<div class="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" 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 btn-primary" onclick="document.getElementById('fileInput').click()">↑ Upload files</button>
|
||||||
<button type="button" class="btn" data-dialog="createDirModal">+ New folder</button>
|
<button type="button" class="btn" data-dialog="createDirModal">+ New folder</button>
|
||||||
@@ -154,7 +147,7 @@
|
|||||||
</dialog>
|
</dialog>
|
||||||
<!-- End of Modal Forms -->
|
<!-- 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)">
|
<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>
|
<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>
|
</div>
|
||||||
@@ -198,20 +191,20 @@
|
|||||||
<td><span class="file-icon folder-icon" aria-hidden="true">▰</span>
|
<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>
|
<a href="?path=<?= urlencode(rtrim($currentPath, '/') . '/' . $directory) ?>"><?= htmlspecialchars($directory) ?></a></td><td class="size-cell">—</td>
|
||||||
<td>
|
<td>
|
||||||
<?php if (isset($_SESSION["delPer"]) && $_SESSION["delPer"] == true) : ?>
|
<?php if (hasPermission('delPer')) : ?>
|
||||||
<form method="POST" action="delete.php" style="display:inline;">
|
<form method="POST" action="delete.php" style="display:inline;">
|
||||||
<input type="hidden" name="delete" value="<?= htmlspecialchars(rtrim($currentPath, '/') . '/' . $directory) ?>">
|
<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>
|
<button type="submit" class="btn btn-danger mt-1 mb-1" onclick="confirmDelete(event)">Delete</button>
|
||||||
</form>
|
</form>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<?php if (isset($_SESSION["downPer"]) && $_SESSION["downPer"] == true) : ?>
|
<?php if (hasPermission('downPer')) : ?>
|
||||||
<form method="POST" action="download.php" style="display:inline;">
|
<form method="POST" action="download.php" style="display:inline;">
|
||||||
<input type="hidden" name="file" value="<?= htmlspecialchars(rtrim($currentPath, '/') . '/' . $directory) ?>">
|
<input type="hidden" name="file" value="<?= htmlspecialchars(rtrim($currentPath, '/') . '/' . $directory) ?>">
|
||||||
<button type="submit" class="btn btn-success mt-1 mb-1">Download</button>
|
<button type="submit" class="btn btn-success mt-1 mb-1">Download</button>
|
||||||
</form>
|
</form>
|
||||||
<?php endif; ?>
|
<?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>
|
<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; ?>
|
<?php endif; ?>
|
||||||
</td>
|
</td>
|
||||||
@@ -221,8 +214,8 @@
|
|||||||
foreach ($files as $file) : ?>
|
foreach ($files as $file) : ?>
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
$fileExtension = pathinfo($file, PATHINFO_EXTENSION);
|
$fileExtension = strtolower(pathinfo($file, PATHINFO_EXTENSION));
|
||||||
$isPDF = strtolower($fileExtension) === 'pdf';
|
$isPdf = $fileExtension === 'pdf';
|
||||||
$isImage = in_array($fileExtension, $imageExtensions);
|
$isImage = in_array($fileExtension, $imageExtensions);
|
||||||
$isVideo = in_array($fileExtension, $videoExtensions);
|
$isVideo = in_array($fileExtension, $videoExtensions);
|
||||||
$isAudio = in_array($fileExtension, $audioExtensions);
|
$isAudio = in_array($fileExtension, $audioExtensions);
|
||||||
@@ -251,7 +244,7 @@
|
|||||||
<?= htmlspecialchars($file) ?>
|
<?= htmlspecialchars($file) ?>
|
||||||
</a>
|
</a>
|
||||||
<span class="badge bg-secondary rounded-pill"><?= htmlspecialchars($fileExtension) ?></span>
|
<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">
|
<a class="text-info-emphasis" href="pdf.php?file=<?= urlencode(rtrim($currentPath, '/') . '/' . $file) ?>&type=pdf">
|
||||||
<?= htmlspecialchars($file) ?>
|
<?= htmlspecialchars($file) ?>
|
||||||
</a>
|
</a>
|
||||||
@@ -265,23 +258,23 @@
|
|||||||
<span class="text"><?= formatBytes($sftp->stat(rtrim($currentPath, '/') . '/' . $file)['size']) ?></span>
|
<span class="text"><?= formatBytes($sftp->stat(rtrim($currentPath, '/') . '/' . $file)['size']) ?></span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<?php if (isset($_SESSION["delPer"]) && $_SESSION["delPer"] == true) : ?>
|
<?php if (hasPermission('delPer')) : ?>
|
||||||
<form method="POST" action="delete.php" style="display:inline;">
|
<form method="POST" action="delete.php" style="display:inline;">
|
||||||
<input type="hidden" name="delete" value="<?= htmlspecialchars(rtrim($currentPath, '/') . '/' . $file) ?>">
|
<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>
|
<button type="submit" class="btn btn-danger mt-1 mb-1" onclick="confirmDelete(event)">Delete</button>
|
||||||
</form>
|
</form>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<?php if (isset($_SESSION["downPer"]) && $_SESSION["downPer"] == true) : ?>
|
<?php if (hasPermission('downPer')) : ?>
|
||||||
<form method="POST" action="download.php" style="display:inline;">
|
<form method="POST" action="download.php" style="display:inline;">
|
||||||
<input type="hidden" name="file" value="<?= htmlspecialchars(rtrim($currentPath, '/') . '/' . $file) ?>">
|
<input type="hidden" name="file" value="<?= htmlspecialchars(rtrim($currentPath, '/') . '/' . $file) ?>">
|
||||||
<button type="submit" class="btn btn-success mt-1 mb-1">Download</button>
|
<button type="submit" class="btn btn-success mt-1 mb-1">Download</button>
|
||||||
</form>
|
</form>
|
||||||
<?php endif; ?>
|
<?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>
|
<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 endif; ?>
|
||||||
<?php if ($isArchive && isset($_SESSION["upPer"]) && $_SESSION["upPer"] == true) : ?>
|
<?php if ($isArchive && hasPermission('upPer')) : ?>
|
||||||
<form method="POST" action="extract.php" style="display:inline;">
|
<form method="POST" action="extract.php" style="display:inline;">
|
||||||
<input type="hidden" name="archive" value="<?= htmlspecialchars(rtrim($currentPath, '/') . '/' . $file) ?>">
|
<input type="hidden" name="archive" value="<?= htmlspecialchars(rtrim($currentPath, '/') . '/' . $file) ?>">
|
||||||
<button type="submit" class="btn btn-info mt-1 mb-1">Extract</button>
|
<button type="submit" class="btn btn-info mt-1 mb-1">Extract</button>
|
||||||
|
|||||||
@@ -1,22 +1,12 @@
|
|||||||
<?php
|
<?php
|
||||||
// Include this at the top to see potential errors
|
require_once __DIR__ . '/../../includes/auth.php';
|
||||||
// Comment out in production
|
requireLogin('../../index.php');
|
||||||
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__ . '/../../config/config.php';
|
require_once __DIR__ . '/../../config/config.php';
|
||||||
require_once __DIR__ . '/../../includes/workspace.php';
|
require_once __DIR__ . '/../../includes/workspace.php';
|
||||||
$sftp = initializeSFTP();
|
$sftp = initializeSFTP();
|
||||||
|
|
||||||
$filePath = isset($_GET['file']) ? $_GET['file'] : '';
|
$filePath = requireRemotePath($_GET['file'] ?? $defaultPath, $defaultPath);
|
||||||
|
|
||||||
$content = '';
|
$content = '';
|
||||||
$fileName = basename($filePath);
|
$fileName = basename($filePath);
|
||||||
@@ -29,7 +19,7 @@ if (!empty($filePath) && $sftp->file_exists($filePath) && !$sftp->is_dir($filePa
|
|||||||
if (in_array(strtolower($extension), $editableExtensions)) {
|
if (in_array(strtolower($extension), $editableExtensions)) {
|
||||||
$editable = true;
|
$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'];
|
$newContent = $_POST['content'];
|
||||||
|
|
||||||
if ($sftp->put($filePath, $newContent)) {
|
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']);
|
$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>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
@@ -75,7 +65,7 @@ $parentDir = remoteDirname($filePath);
|
|||||||
'title' => $fileName,
|
'title' => $fileName,
|
||||||
'description' => 'View and edit text files in your remote workspace.',
|
'description' => 'View and edit text files in your remote workspace.',
|
||||||
'connected' => true,
|
'connected' => true,
|
||||||
'back' => 'index.php?path=' . urlencode($parentDir),
|
'back' => 'index.php?path=' . urlencode($parentPath),
|
||||||
'class' => 'editor-page',
|
'class' => 'editor-page',
|
||||||
]); ?>
|
]); ?>
|
||||||
<section class="content-panel">
|
<section class="content-panel">
|
||||||
@@ -94,7 +84,7 @@ $parentDir = remoteDirname($filePath);
|
|||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||||
<label for="editor" class="form-label">File Content</label>
|
<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>
|
<span class="readonly-notice">Read-only mode (you don't have upload permissions)</span>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
@@ -102,11 +92,11 @@ $parentDir = remoteDirname($filePath);
|
|||||||
<?php if ($showLineNumbers): ?>
|
<?php if ($showLineNumbers): ?>
|
||||||
<div id="lineNumbers" class="line-numbers"></div>
|
<div id="lineNumbers" class="line-numbers"></div>
|
||||||
<?php endif; ?>
|
<?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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php if (isset($_SESSION["upPer"]) && $_SESSION["upPer"] == true): ?>
|
<?php if (hasPermission('upPer')): ?>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<button type="submit" class="btn btn-primary">Save Changes</button>
|
<button type="submit" class="btn btn-primary">Save Changes</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+6
-12
@@ -1,11 +1,6 @@
|
|||||||
<?php
|
<?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__ . '/../../config/config.php';
|
||||||
require_once __DIR__ . '/../../includes/workspace.php';
|
require_once __DIR__ . '/../../includes/workspace.php';
|
||||||
@@ -15,7 +10,7 @@ if (!isset($_GET['file'])) {
|
|||||||
die("No file specified");
|
die("No file specified");
|
||||||
}
|
}
|
||||||
|
|
||||||
$filePath = $_GET['file'];
|
$filePath = requireRemotePath($_GET['file'], $defaultPath);
|
||||||
$fileName = basename($filePath);
|
$fileName = basename($filePath);
|
||||||
$fileSize = $sftp->stat($filePath)['size'];
|
$fileSize = $sftp->stat($filePath)['size'];
|
||||||
|
|
||||||
@@ -105,8 +100,8 @@ if (isset($_GET['stream'])) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception $e) {
|
} catch (Exception $exception) {
|
||||||
error_log("Streaming error: " . $e->getMessage());
|
error_log("Streaming error: " . $exception->getMessage());
|
||||||
}
|
}
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
@@ -138,7 +133,6 @@ if (isset($_GET['stream'])) {
|
|||||||
'class' => 'pdf-page',
|
'class' => 'pdf-page',
|
||||||
]); ?>
|
]); ?>
|
||||||
|
|
||||||
|
|
||||||
<section class="content-panel pdf-workspace" aria-label="PDF document">
|
<section class="content-panel pdf-workspace" aria-label="PDF document">
|
||||||
<div id="pdfContainer" class="container-fluid position-relative">
|
<div id="pdfContainer" class="container-fluid position-relative">
|
||||||
<div id="loadingMessage" class="d-flex align-items-center justify-content-center w-100 h-100">
|
<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>
|
</div>
|
||||||
<div class="actionButton">
|
<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>
|
<a href="download.php?file=<?= urlencode($filePath) ?>" class="btn btn-success m-3">Download</a>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+11
-19
@@ -1,18 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
session_start();
|
require_once __DIR__ . '/../../includes/auth.php';
|
||||||
|
requireLogin('../../index.php');
|
||||||
if(!isset($_SESSION['uname'])){
|
requirePermission('upPer', 'You do not have permission to perform this operation.');
|
||||||
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__ . '/../../config/config.php';
|
require_once __DIR__ . '/../../config/config.php';
|
||||||
$sftp = initializeSFTP();
|
$sftp = initializeSFTP();
|
||||||
@@ -20,23 +9,26 @@ $sftp = initializeSFTP();
|
|||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
$oldPath = isset($_POST['path']) ? $_POST['path'] : '';
|
$oldPath = isset($_POST['path']) ? $_POST['path'] : '';
|
||||||
$newName = isset($_POST['newName']) ? trim($_POST['newName']) : '';
|
$newName = isset($_POST['newName']) ? trim($_POST['newName']) : '';
|
||||||
$isDirectory = isset($_POST['isDirectory']) && $_POST['isDirectory'] == '1';
|
|
||||||
|
|
||||||
if (empty($oldPath) || empty($newName)) {
|
if (empty($oldPath) || empty($newName)) {
|
||||||
die("Missing required information for renaming.");
|
die("Missing required information for renaming.");
|
||||||
}
|
}
|
||||||
|
|
||||||
$dirPath = remoteDirname($oldPath);
|
$oldPath = requireRemotePath($oldPath, $defaultPath);
|
||||||
$oldName = basename($oldPath);
|
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)) {
|
if ($sftp->file_exists($newPath) || $sftp->is_dir($newPath)) {
|
||||||
die("Error: A file or directory with this name already exists.");
|
die("Error: A file or directory with this name already exists.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($sftp->rename($oldPath, $newPath)) {
|
if ($sftp->rename($oldPath, $newPath)) {
|
||||||
header("Location: index.php?path=" . urlencode($dirPath));
|
header("Location: index.php?path=" . urlencode($directoryPath));
|
||||||
exit;
|
exit;
|
||||||
} else {
|
} else {
|
||||||
die("Failed to rename the item. Please try again.");
|
die("Failed to rename the item. Please try again.");
|
||||||
|
|||||||
@@ -1,16 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
// Include this at the top to see potential errors
|
require_once __DIR__ . '/../../includes/auth.php';
|
||||||
// Comment out in production
|
requireLogin('../../index.php');
|
||||||
ini_set('display_errors', 1);
|
|
||||||
error_reporting(E_ALL);
|
|
||||||
|
|
||||||
session_start();
|
requirePermission('admin', 'You do not have permission to view server statistics.');
|
||||||
|
|
||||||
if (empty($_SESSION['admin']) || $_SESSION['admin'] !== true) {
|
|
||||||
session_destroy();
|
|
||||||
header("location: ../../index.php");
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
require_once __DIR__ . '/../../config/config.php';
|
require_once __DIR__ . '/../../config/config.php';
|
||||||
require_once __DIR__ . '/../../includes/workspace.php';
|
require_once __DIR__ . '/../../includes/workspace.php';
|
||||||
@@ -90,7 +82,6 @@ $monitors = [
|
|||||||
'class' => 'server-page',
|
'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="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">
|
<div class="stats-grid">
|
||||||
<?php foreach ($monitors as $monitor): ?>
|
<?php foreach ($monitors as $monitor): ?>
|
||||||
|
|||||||
+16
-24
@@ -1,26 +1,19 @@
|
|||||||
<?php
|
<?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__ . '/../../config/config.php';
|
||||||
require_once __DIR__ . '/../../includes/workspace.php';
|
require_once __DIR__ . '/../../includes/workspace.php';
|
||||||
|
|
||||||
// getDatabaseConnection relocated to central config
|
|
||||||
|
|
||||||
if (!isset($_GET['token'])) {
|
if (!isset($_GET['token'])) {
|
||||||
die("Invalid request. No token provided.");
|
die("Invalid request. No token provided.");
|
||||||
}
|
}
|
||||||
|
|
||||||
$token = $_GET['token'];
|
$token = $_GET['token'];
|
||||||
$conn = getDatabaseConnection();
|
$database = getDatabaseConnection();
|
||||||
|
|
||||||
// Get share information
|
// Get share information
|
||||||
$stmt = $conn->prepare("SELECT * FROM share WHERE token = ? AND expiration > NOW()");
|
$statement = $database->prepare("SELECT * FROM share WHERE token = ? AND expiration > NOW()");
|
||||||
$stmt->bind_param("s", $token);
|
$statement->bind_param("s", $token);
|
||||||
$stmt->execute();
|
$statement->execute();
|
||||||
$result = $stmt->get_result();
|
$result = $statement->get_result();
|
||||||
|
|
||||||
if ($result->num_rows === 0) {
|
if ($result->num_rows === 0) {
|
||||||
die("Invalid or expired share link.");
|
die("Invalid or expired share link.");
|
||||||
@@ -29,7 +22,9 @@ if ($result->num_rows === 0) {
|
|||||||
$share = $result->fetch_assoc();
|
$share = $result->fetch_assoc();
|
||||||
$filePath = $share['file_path'];
|
$filePath = $share['file_path'];
|
||||||
$downloadAllowed = $share['download_allowed'] == 1;
|
$downloadAllowed = $share['download_allowed'] == 1;
|
||||||
$stmt->close();
|
$result->free();
|
||||||
|
$statement->close();
|
||||||
|
$database->close();
|
||||||
|
|
||||||
// Initialize SFTP
|
// Initialize SFTP
|
||||||
$sftp = initializeSFTP();
|
$sftp = initializeSFTP();
|
||||||
@@ -42,13 +37,13 @@ $fileName = basename($filePath);
|
|||||||
$fileExtension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
|
$fileExtension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
|
||||||
$fileSize = $sftp->stat($filePath)['size'];
|
$fileSize = $sftp->stat($filePath)['size'];
|
||||||
|
|
||||||
$imageTypes = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg'];
|
$imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg'];
|
||||||
$videoTypes = ['mp4', 'webm', 'ogg', 'mov', 'avi', 'mkv'];
|
$videoExtensions = ['mp4', 'webm', 'ogg', 'mov', 'avi', 'mkv'];
|
||||||
$audioTypes = ['mp3', 'wav', 'ogg', 'm4a', 'flac', 'aac'];
|
$audioExtensions = ['mp3', 'wav', 'ogg', 'm4a', 'flac', 'aac'];
|
||||||
|
|
||||||
$isImage = in_array($fileExtension, $imageTypes);
|
$isImage = in_array($fileExtension, $imageExtensions);
|
||||||
$isVideo = in_array($fileExtension, $videoTypes);
|
$isVideo = in_array($fileExtension, $videoExtensions);
|
||||||
$isAudio = in_array($fileExtension, $audioTypes);
|
$isAudio = in_array($fileExtension, $audioExtensions);
|
||||||
|
|
||||||
if (!$isImage && !$isVideo && !$isAudio) {
|
if (!$isImage && !$isVideo && !$isAudio) {
|
||||||
die("Unsupported file type");
|
die("Unsupported file type");
|
||||||
@@ -165,8 +160,8 @@ if (isset($_GET['stream'])) {
|
|||||||
|
|
||||||
usleep(100000);
|
usleep(100000);
|
||||||
}
|
}
|
||||||
} catch (Exception $e) {
|
} catch (Exception $exception) {
|
||||||
error_log("Shared streaming error: " . $e->getMessage());
|
error_log("Shared streaming error: " . $exception->getMessage());
|
||||||
}
|
}
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
@@ -242,7 +237,6 @@ function formatBytes($bytes, $precision = 2) {
|
|||||||
'class' => 'media-page',
|
'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>
|
<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">
|
<section class="content-panel media-panel">
|
||||||
<article class="col-12">
|
<article class="col-12">
|
||||||
@@ -293,8 +287,6 @@ function formatBytes($bytes, $precision = 2) {
|
|||||||
|
|
||||||
<?php renderWorkspaceEnd(); ?>
|
<?php renderWorkspaceEnd(); ?>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
const mediaElement = document.querySelector('video, audio');
|
const mediaElement = document.querySelector('video, audio');
|
||||||
|
|||||||
+58
-103
@@ -1,130 +1,85 @@
|
|||||||
<?php
|
<?php
|
||||||
// Include this at the top to see potential errors
|
require_once __DIR__ . '/../../includes/auth.php';
|
||||||
// Comment out in production
|
requireLogin('../../index.php');
|
||||||
ini_set('display_errors', 1);
|
requirePermission('upPer', 'You do not have permission to upload files.');
|
||||||
error_reporting(E_ALL);
|
session_write_close();
|
||||||
|
|
||||||
require_once __DIR__ . '/../../config/config.php';
|
require_once __DIR__ . '/../../config/config.php';
|
||||||
|
|
||||||
use phpseclib3\Net\SFTP;
|
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();
|
$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);
|
$directories = json_decode($_POST['create_dirs'], true);
|
||||||
|
if (!is_array($directories)) {
|
||||||
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);
|
http_response_code(400);
|
||||||
exit;
|
exit('Invalid directory list.');
|
||||||
|
}
|
||||||
|
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.');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($_FILES['files']['error'][0] !== 0) {
|
$uploadedFiles = $_FILES['files'] ?? null;
|
||||||
$error = $_FILES['files']['error'][0];
|
if (!$uploadedFiles || empty($uploadedFiles['name'][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);
|
http_response_code(400);
|
||||||
exit;
|
exit('No files received or file size exceeds PHP limits.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$uploadStatus = array();
|
$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;
|
$anySuccess = false;
|
||||||
|
$failureStatus = 500;
|
||||||
|
|
||||||
foreach ($_FILES['files']['tmp_name'] as $index => $tmpName) {
|
foreach ($uploadedFiles['tmp_name'] as $index => $temporaryPath) {
|
||||||
if (empty($tmpName)) continue;
|
$fileName = $uploadedFiles['name'][$index];
|
||||||
|
$relativePath = $_POST['paths'][$index] ?? '';
|
||||||
$fileName = $_FILES['files']['name'][$index];
|
$uploadError = $uploadedFiles['error'][$index];
|
||||||
$relativePath = isset($_POST['paths']) && isset($_POST['paths'][$index]) ? $_POST['paths'][$index] : '';
|
if ($uploadError !== UPLOAD_ERR_OK) {
|
||||||
|
$failureStatus = 400;
|
||||||
if (!empty($relativePath)) {
|
$uploadMessages[] = $fileName . ': ' . ($uploadErrors[$uploadError] ?? 'Unknown upload error');
|
||||||
$fileName = basename($relativePath);
|
continue;
|
||||||
|
|
||||||
$dirPart = remoteDirname($relativePath);
|
|
||||||
if ($dirPart !== '.' && $dirPart !== '') {
|
|
||||||
$remoteDirPath = $currentPath . '/' . $dirPart;
|
|
||||||
|
|
||||||
if (!$sftp->is_dir($remoteDirPath)) {
|
|
||||||
createDirectoryRecursive($sftp, $remoteDirPath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$remotePath = $remoteDirPath . '/' . $fileName;
|
$remotePath = requireRemotePath(rtrim($currentPath, '/') . '/' . ($relativePath !== '' ? $relativePath : $fileName), $currentPath);
|
||||||
} else {
|
if ($remotePath === $currentPath) {
|
||||||
$remotePath = $currentPath . '/' . $fileName;
|
http_response_code(400);
|
||||||
|
exit('Invalid upload path.');
|
||||||
}
|
}
|
||||||
} else {
|
$directoryPath = remoteDirname($remotePath);
|
||||||
$remotePath = $currentPath . '/' . $fileName;
|
if (!$sftp->is_dir($directoryPath) && !$sftp->mkdir($directoryPath, 0755, true)) {
|
||||||
|
$uploadMessages[] = 'Failed to create directory for: ' . $fileName;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($sftp->put($remotePath, $tmpName, SFTP::SOURCE_LOCAL_FILE)) {
|
if ($sftp->put($remotePath, $temporaryPath, SFTP::SOURCE_LOCAL_FILE)) {
|
||||||
$uploadStatus[] = "Uploaded: " . ($relativePath ? $relativePath : $fileName);
|
$uploadMessages[] = 'Uploaded: ' . ($relativePath !== '' ? $relativePath : $fileName);
|
||||||
$anySuccess = true;
|
$anySuccess = true;
|
||||||
} else {
|
} else {
|
||||||
$uploadStatus[] = "Failed to upload: " . ($relativePath ? $relativePath : $fileName);
|
$uploadMessages[] = 'Failed to upload: ' . ($relativePath !== '' ? $relativePath : $fileName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($anySuccess) {
|
if (!$anySuccess) {
|
||||||
echo implode("\n", $uploadStatus);
|
http_response_code($failureStatus);
|
||||||
exit;
|
|
||||||
} else {
|
|
||||||
echo "Failed to upload any files. Please check SFTP connection and permissions.";
|
|
||||||
http_response_code(500);
|
|
||||||
exit;
|
|
||||||
}
|
}
|
||||||
}
|
header('Content-Type: text/plain; charset=UTF-8');
|
||||||
|
echo implode("\n", $uploadMessages);
|
||||||
echo "No files received or invalid request.";
|
|
||||||
http_response_code(400);
|
|
||||||
exit;
|
|
||||||
|
|||||||
+23
-34
@@ -1,16 +1,7 @@
|
|||||||
<?php
|
<?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');
|
ini_set('memory_limit', '512M');
|
||||||
|
|
||||||
require_once __DIR__ . '/../../config/config.php';
|
require_once __DIR__ . '/../../config/config.php';
|
||||||
@@ -24,7 +15,7 @@ if (!isset($_GET['file'])) {
|
|||||||
if (isset($_POST['createShare'])) {
|
if (isset($_POST['createShare'])) {
|
||||||
$duration = intval($_POST['duration']);
|
$duration = intval($_POST['duration']);
|
||||||
$downloadAllowed = isset($_POST['downloadAllowed']) ? 1 : 0;
|
$downloadAllowed = isset($_POST['downloadAllowed']) ? 1 : 0;
|
||||||
$filePath = $_POST['filePath'];
|
$filePath = requireRemotePath($_POST['filePath'], $defaultPath);
|
||||||
|
|
||||||
$token = bin2hex(random_bytes(16));
|
$token = bin2hex(random_bytes(16));
|
||||||
|
|
||||||
@@ -32,11 +23,15 @@ if (isset($_POST['createShare'])) {
|
|||||||
|
|
||||||
$expiration = date('Y-m-d H:i:s', strtotime("+$duration hours"));
|
$expiration = date('Y-m-d H:i:s', strtotime("+$duration hours"));
|
||||||
|
|
||||||
$conn = getDatabaseConnection();
|
$database = getDatabaseConnection();
|
||||||
$stmt = $conn->prepare("INSERT INTO share (token, file_path, expiration, download_allowed) VALUES (?, ?, ?, ?)");
|
$statement = $database->prepare("INSERT INTO share (token, file_path, expiration, download_allowed) VALUES (?, ?, ?, ?)");
|
||||||
$stmt->bind_param("sssi", $token, $filePath, $expiration, $downloadAllowed);
|
$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]" .
|
$shareUrl = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://$_SERVER[HTTP_HOST]" .
|
||||||
remoteDirname($_SERVER['PHP_SELF']) . "/shared.php?token=$token";
|
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");
|
header("Location: view.php?file=" . urlencode($filePath) . "&shared=1");
|
||||||
exit;
|
exit;
|
||||||
} else {
|
} 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");
|
header("Location: view.php?file=" . urlencode($filePath) . "&shared=0");
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$stmt->close();
|
|
||||||
$conn->close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$shareCreated = false;
|
$shareCreated = false;
|
||||||
@@ -71,7 +64,7 @@ if (isset($_GET['shared'])) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$filePath = $_GET['file'];
|
$filePath = requireRemotePath($_GET['file'], $defaultPath);
|
||||||
$fileName = basename($filePath);
|
$fileName = basename($filePath);
|
||||||
$fileExtension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
|
$fileExtension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
|
||||||
|
|
||||||
@@ -79,13 +72,13 @@ if (!$sftp->stat($filePath)) {
|
|||||||
die("File not found: $filePath");
|
die("File not found: $filePath");
|
||||||
}
|
}
|
||||||
|
|
||||||
$imageTypes = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg'];
|
$imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg'];
|
||||||
$videoTypes = ['mp4', 'webm', 'ogg', 'mov', 'avi', 'mkv'];
|
$videoExtensions = ['mp4', 'webm', 'ogg', 'mov', 'avi', 'mkv'];
|
||||||
$audioTypes = ['mp3', 'wav', 'ogg', 'm4a', 'flac', 'aac'];
|
$audioExtensions = ['mp3', 'wav', 'ogg', 'm4a', 'flac', 'aac'];
|
||||||
|
|
||||||
$isImage = in_array($fileExtension, $imageTypes);
|
$isImage = in_array($fileExtension, $imageExtensions);
|
||||||
$isVideo = in_array($fileExtension, $videoTypes);
|
$isVideo = in_array($fileExtension, $videoExtensions);
|
||||||
$isAudio = in_array($fileExtension, $audioTypes);
|
$isAudio = in_array($fileExtension, $audioExtensions);
|
||||||
|
|
||||||
if (!$isImage && !$isVideo && !$isAudio) {
|
if (!$isImage && !$isVideo && !$isAudio) {
|
||||||
die("Unsupported file type");
|
die("Unsupported file type");
|
||||||
@@ -238,10 +231,10 @@ if (isset($_GET['stream'])) {
|
|||||||
// Throttle to prevent CPU overload
|
// Throttle to prevent CPU overload
|
||||||
usleep(100000); // 100ms
|
usleep(100000); // 100ms
|
||||||
}
|
}
|
||||||
} catch (Exception $e) {
|
} catch (Exception $exception) {
|
||||||
error_log("Streaming error: " . $e->getMessage());
|
error_log("Streaming error: " . $exception->getMessage());
|
||||||
if (isset($_GET['debug'])) {
|
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>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
@@ -278,7 +270,6 @@ if (isset($_GET['stream'])) {
|
|||||||
'class' => 'media-page',
|
'class' => 'media-page',
|
||||||
]); ?>
|
]); ?>
|
||||||
|
|
||||||
|
|
||||||
<section class="content-panel media-panel">
|
<section class="content-panel media-panel">
|
||||||
<article class="col-12">
|
<article class="col-12">
|
||||||
<div class="media-container position-relative">
|
<div class="media-container position-relative">
|
||||||
@@ -301,7 +292,7 @@ if (isset($_GET['stream'])) {
|
|||||||
</audio>
|
</audio>
|
||||||
<?php endif; ?>
|
<?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>
|
<a href="download.php?file=<?= urlencode($filePath) ?>" class="btn btn-success m-3">Download</a>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
@@ -412,8 +403,6 @@ function copyShareUrl() {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
const mediaElement = document.querySelector('video, audio');
|
const mediaElement = document.querySelector('video, audio');
|
||||||
|
|||||||
+40
-72
@@ -1,83 +1,51 @@
|
|||||||
<?php
|
<?php
|
||||||
session_start();
|
session_start();
|
||||||
|
|
||||||
if (isset($_POST['uname'], $_POST['pswd'])) {
|
|
||||||
require_once __DIR__ . '/../config/config.php';
|
require_once __DIR__ . '/../config/config.php';
|
||||||
// $username = "UNAME";
|
|
||||||
// $password = "PSWD";
|
|
||||||
// $db = "usbraidlogin";
|
|
||||||
|
|
||||||
$uname = htmlspecialchars($_POST['uname']);
|
if (!isset($_POST['uname'], $_POST['pswd'])
|
||||||
$pswd = htmlspecialchars($_POST['pswd']);
|
|| !is_string($_POST['uname']) || !is_string($_POST['pswd'])) {
|
||||||
|
$_SESSION['login_error'] = 'Please fill in all fields.';
|
||||||
$conn = getDatabaseConnection();
|
header('Location: ../index.php');
|
||||||
// charset already set in getDatabaseConnection()
|
exit;
|
||||||
|
|
||||||
if ($conn->connect_error) {
|
|
||||||
die("Connection failed: " . $conn->connect_error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$sql = "SELECT * FROM users WHERE uname=?";
|
$username = trim($_POST['uname']);
|
||||||
$stmt = $conn->prepare($sql);
|
$password = $_POST['pswd'];
|
||||||
if (!$stmt) {
|
$loginSuccessful = false;
|
||||||
$_SESSION['login_error'] = "Database error. Please try again.";
|
$database = getDatabaseConnection();
|
||||||
$conn->close();
|
|
||||||
header("Location: ../index.php");
|
try {
|
||||||
exit();
|
$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;
|
||||||
}
|
}
|
||||||
|
session_regenerate_id(true);
|
||||||
$stmt->bind_param("s", $uname);
|
$_SESSION['uname'] = $user['uname'];
|
||||||
$stmt->execute();
|
$_SESSION['admin'] = (bool) $user['admin'];
|
||||||
$result = $stmt->get_result();
|
$_SESSION['defPath'] = $user['defPath'];
|
||||||
|
foreach (['downPer', 'delPer', 'upPer'] as $permission) {
|
||||||
if ($result && $result->num_rows > 0) {
|
$_SESSION[$permission] = (bool) $user[$permission];
|
||||||
$login_successful = false;
|
}
|
||||||
while ($row = $result->fetch_assoc()) {
|
unset($_SESSION['login_error']);
|
||||||
$hash = $row["pswd"];
|
$loginSuccessful = true;
|
||||||
$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;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
$result->free();
|
||||||
|
$statement->close();
|
||||||
|
if (!$loginSuccessful) {
|
||||||
|
$_SESSION['login_error'] = 'Invalid username or password.';
|
||||||
|
}
|
||||||
|
} catch (mysqli_sql_exception $exception) {
|
||||||
|
error_log($exception->getMessage());
|
||||||
|
$_SESSION['login_error'] = 'Database error. Please try again.';
|
||||||
|
} finally {
|
||||||
|
$database->close();
|
||||||
}
|
}
|
||||||
$result->free_result();
|
|
||||||
$stmt->close();
|
|
||||||
$conn->close();
|
|
||||||
|
|
||||||
if ($login_successful) {
|
header('Location: ' . ($loginSuccessful ? 'ftp/index.php' : '../index.php'));
|
||||||
header("Location: ftp/index.php");
|
exit;
|
||||||
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();
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
|
|||||||
@@ -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), '/');
|
$parent = rtrim(substr($path, 0, $separator), '/');
|
||||||
return $parent === '' ? '/' : $parent;
|
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);
|
$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');
|
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
|
<?php
|
||||||
require_once __DIR__ . '/../Web/includes/workspace.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/includes/remotepaths.php';
|
||||||
require_once __DIR__ . '/../Web/content/ftp/lib/serverstats.php';
|
require_once __DIR__ . '/../Web/content/ftp/lib/serverstats.php';
|
||||||
set_error_handler(function ($severity, $message, $file, $line) {
|
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') {
|
function renderWorkspaceFixture($name, $admin = true, $defaultPath = '/mnt/raid', $currentPath = '/mnt/raid/Workspace') {
|
||||||
$_SESSION = ['uname' => 'michal', 'admin' => $admin, 'upPer' => true, 'downPer' => true, 'delPer' => true];
|
$_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';
|
$fileExtension = 'png'; $fileSize = 245760; $mimeType = 'image/png';
|
||||||
$isImage = true; $isVideo = false; $isAudio = false;
|
$isImage = true; $isVideo = false; $isAudio = false;
|
||||||
$editable = true; $showLineNumbers = true; $content = "{\n \"workspace\": \"USB RAID\",\n \"status\": \"connected\"\n}\n";
|
$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';
|
$share = ['expiration' => '2030-01-01']; $downloadAllowed = false; $token = 'test-only';
|
||||||
$items = ['Documents', 'Photos', 'README.md', 'Example.pdf'];
|
$items = ['Documents', 'Photos', 'README.md', 'Example.pdf'];
|
||||||
$imageExtensions = ['png']; $videoExtensions = []; $audioExtensions = []; $editableExtensions = ['md']; $archiveExtensions = [];
|
$imageExtensions = ['png']; $videoExtensions = []; $audioExtensions = []; $editableExtensions = ['md']; $archiveExtensions = [];
|
||||||
|
|||||||
Reference in New Issue
Block a user