WebApp production ver

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,50 @@
-- phpMyAdmin SQL Dump
-- version 5.1.1deb5ubuntu1
-- https://www.phpmyadmin.net/
--
-- Počítač: localhost:3306
-- Vytvořeno: Čtv 20. bře 2025, 12:55
-- Verze serveru: 8.0.41-0ubuntu0.22.04.1
-- Verze PHP: 8.1.2-1ubuntu2.20
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
START TRANSACTION;
SET time_zone = "+00:00";
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
--
-- Databáze: `usbraidlogin`
--
-- --------------------------------------------------------
--
-- Struktura tabulky `users`
--
CREATE TABLE `users` (
`uname` varchar(255) NOT NULL,
`pswd` varchar(60) NOT NULL,
`admin` tinyint(1) NOT NULL DEFAULT '0',
`defPath` varchar(255) NOT NULL DEFAULT '/',
`delPer` tinyint(1) NOT NULL DEFAULT '0',
`downPer` tinyint(1) NOT NULL DEFAULT '0',
`upPer` tinyint(1) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
--
-- Vypisuji data pro tabulku `users`
--
INSERT INTO `users` (`uname`, `pswd`, `admin`, `defPath`, `delPer`, `downPer`, `upPer`) VALUES
('admin', '$2y$10$QZEnBMv01f92Ikrta3Qo4ewbmoozVuDT.uPhtztNT7bsIwDQ.Fy8e', 1, '/', 1, 1, 1);
COMMIT;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

Binary file not shown.

View File

@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2025 Michal Sedlák
Copyright (c) 2024 Michal Sedlák
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -1 +1,2 @@
# USB-RAID-Array
# USB-RAID-pole
Cílem práce je nalézt využití zastaralých paměťových médií jako například slabopaměťové USB flash disky s malou paměťovou kapacitou. Technologii RAID (NAS) pole v praxi implementujte na vícero RAID technologiích a vzájemně komparujte řešení. Komparujte chování navrženého systému minimálně na technologiích RAID 0, RAID 1 a RAID 5. Vytvořte vzdálené připojení na toto RAID pole s možností nahrání a stažení souborů po přihlášení uživatele (web login). K tomuto účelu vytvořte webovou aplikaci. Zabezpečte vzdálené připojení. Použité technologie: RAID, NAS, FTP, USB, Flash, HTML, PHP, MySQL.

View File

@@ -0,0 +1,5 @@
{
"require": {
"phpseclib/phpseclib": "^3.0"
}
}

246
Web/betatest/composer.lock generated Normal file
View File

@@ -0,0 +1,246 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "6b7a718f1b7b53a21b92507cfa57827a",
"packages": [
{
"name": "paragonie/constant_time_encoding",
"version": "v3.0.0",
"source": {
"type": "git",
"url": "https://github.com/paragonie/constant_time_encoding.git",
"reference": "df1e7fde177501eee2037dd159cf04f5f301a512"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/df1e7fde177501eee2037dd159cf04f5f301a512",
"reference": "df1e7fde177501eee2037dd159cf04f5f301a512",
"shasum": ""
},
"require": {
"php": "^8"
},
"require-dev": {
"phpunit/phpunit": "^9",
"vimeo/psalm": "^4|^5"
},
"type": "library",
"autoload": {
"psr-4": {
"ParagonIE\\ConstantTime\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Paragon Initiative Enterprises",
"email": "security@paragonie.com",
"homepage": "https://paragonie.com",
"role": "Maintainer"
},
{
"name": "Steve 'Sc00bz' Thomas",
"email": "steve@tobtu.com",
"homepage": "https://www.tobtu.com",
"role": "Original Developer"
}
],
"description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)",
"keywords": [
"base16",
"base32",
"base32_decode",
"base32_encode",
"base64",
"base64_decode",
"base64_encode",
"bin2hex",
"encoding",
"hex",
"hex2bin",
"rfc4648"
],
"support": {
"email": "info@paragonie.com",
"issues": "https://github.com/paragonie/constant_time_encoding/issues",
"source": "https://github.com/paragonie/constant_time_encoding"
},
"time": "2024-05-08T12:36:18+00:00"
},
{
"name": "paragonie/random_compat",
"version": "v9.99.100",
"source": {
"type": "git",
"url": "https://github.com/paragonie/random_compat.git",
"reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a",
"reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a",
"shasum": ""
},
"require": {
"php": ">= 7"
},
"require-dev": {
"phpunit/phpunit": "4.*|5.*",
"vimeo/psalm": "^1"
},
"suggest": {
"ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
},
"type": "library",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Paragon Initiative Enterprises",
"email": "security@paragonie.com",
"homepage": "https://paragonie.com"
}
],
"description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
"keywords": [
"csprng",
"polyfill",
"pseudorandom",
"random"
],
"support": {
"email": "info@paragonie.com",
"issues": "https://github.com/paragonie/random_compat/issues",
"source": "https://github.com/paragonie/random_compat"
},
"time": "2020-10-15T08:29:30+00:00"
},
{
"name": "phpseclib/phpseclib",
"version": "3.0.43",
"source": {
"type": "git",
"url": "https://github.com/phpseclib/phpseclib.git",
"reference": "709ec107af3cb2f385b9617be72af8cf62441d02"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/709ec107af3cb2f385b9617be72af8cf62441d02",
"reference": "709ec107af3cb2f385b9617be72af8cf62441d02",
"shasum": ""
},
"require": {
"paragonie/constant_time_encoding": "^1|^2|^3",
"paragonie/random_compat": "^1.4|^2.0|^9.99.99",
"php": ">=5.6.1"
},
"require-dev": {
"phpunit/phpunit": "*"
},
"suggest": {
"ext-dom": "Install the DOM extension to load XML formatted public keys.",
"ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.",
"ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.",
"ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.",
"ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations."
},
"type": "library",
"autoload": {
"files": [
"phpseclib/bootstrap.php"
],
"psr-4": {
"phpseclib3\\": "phpseclib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jim Wigginton",
"email": "terrafrost@php.net",
"role": "Lead Developer"
},
{
"name": "Patrick Monnerat",
"email": "pm@datasphere.ch",
"role": "Developer"
},
{
"name": "Andreas Fischer",
"email": "bantu@phpbb.com",
"role": "Developer"
},
{
"name": "Hans-Jürgen Petrich",
"email": "petrich@tronic-media.com",
"role": "Developer"
},
{
"name": "Graham Campbell",
"email": "graham@alt-three.com",
"role": "Developer"
}
],
"description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.",
"homepage": "http://phpseclib.sourceforge.net",
"keywords": [
"BigInteger",
"aes",
"asn.1",
"asn1",
"blowfish",
"crypto",
"cryptography",
"encryption",
"rsa",
"security",
"sftp",
"signature",
"signing",
"ssh",
"twofish",
"x.509",
"x509"
],
"support": {
"issues": "https://github.com/phpseclib/phpseclib/issues",
"source": "https://github.com/phpseclib/phpseclib/tree/3.0.43"
},
"funding": [
{
"url": "https://github.com/terrafrost",
"type": "github"
},
{
"url": "https://www.patreon.com/phpseclib",
"type": "patreon"
},
{
"url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib",
"type": "tidelift"
}
],
"time": "2024-12-14T21:12:59+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": [],
"plugin-api-version": "2.6.0"
}

View File

@@ -0,0 +1,248 @@
<?php
session_start();
if (empty($_SESSION['admin']) || $_SESSION['admin'] !== true) {
session_destroy();
header("location: ../index.php");
exit();
}
ini_set('display_errors', 1);
error_reporting(E_ALL);
$servername = "localhost:3306";
$username = "UNAME";
$password = "PSWD";
$db = "usbraidlogin";
$conn = new mysqli($servername, $username, $password, $db);
$conn->set_charset("utf8");
if ($conn->connect_error) {
die("Database connection failed: " . $conn->connect_error);
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['uname']) && isset($_POST['pswd'])) {
$uname = trim($_POST['uname']);
$pswd = trim($_POST['pswd']);
$is_admin = isset($_POST['admin']) ? 1 : 0;
$defPath = isset($_POST['defPath']) ? trim($_POST['defPath']) : '';
$delPer = isset($_POST['delPer']) ? (int)$_POST['delPer'] : 0;
$dowPer = isset($_POST['downPer']) ? (int)$_POST['downPer'] : 0;
$upPer = isset($_POST['upPer']) ? (int)$_POST['upPer'] : 0;
if (empty($uname) || empty($pswd)) {
$_SESSION['message'] = 'Error: Username and password are required!';
$_SESSION['message_type'] = 'error';
} else {
$sql_check = "SELECT * FROM users WHERE uname = ?";
$stmt_check = $conn->prepare($sql_check);
if (!$stmt_check) {
$_SESSION['message'] = 'Error: Database preparation failed.';
$_SESSION['message_type'] = 'error';
} else {
$stmt_check->bind_param("s", $uname);
$stmt_check->execute();
$result_check = $stmt_check->get_result();
if ($result_check->num_rows > 0) {
$_SESSION['message'] = 'Error: Username already exists!';
$_SESSION['message_type'] = 'error';
} else {
$sql_insert = "INSERT INTO users (uname, pswd, admin, defPath, delPer, downPer, upPer) VALUES (?, ?, ?, ?, ?, ?, ?)";
$stmt_insert = $conn->prepare($sql_insert);
if (!$stmt_insert) {
$_SESSION['message'] = 'Error: Database preparation failed.';
$_SESSION['message_type'] = 'error';
} else {
$hash = password_hash($pswd, PASSWORD_BCRYPT);
if (!$hash) {
$_SESSION['message'] = 'Error: Password hashing failed.';
$_SESSION['message_type'] = 'error';
} else {
$stmt_insert->bind_param("ssisiii", $uname, $hash, $is_admin, $defPath, $delPer, $dowPer, $upPer);
if ($stmt_insert->execute()) {
$_SESSION['message'] = 'User added successfully!';
$_SESSION['message_type'] = 'success';
} else {
$_SESSION['message'] = 'Error: Failed to add user. Please try again later.';
$_SESSION['message_type'] = 'error';
}
}
}
}
$stmt_check->close();
if (isset($stmt_insert)) {
$stmt_insert->close();
}
}
}
header("Location: adminpanel.php");
exit();
} else {
$_SESSION['message'] = 'Error: Missing form data!';
$_SESSION['message_type'] = 'error';
header("Location: adminpanel.php");
exit();
}
}
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_type'] = 'success';
} else {
$_SESSION['message'] = 'Error: Failed to delete user.';
$_SESSION['message_type'] = 'error';
}
$stmt->close();
} else {
$_SESSION['message'] = 'Error: Database preparation failed.';
$_SESSION['message_type'] = 'error';
}
header("Location: adminpanel.php");
exit();
}
$result = $conn->query("SELECT uname, admin, defPath, delPer, downPer, upPer FROM users");
$message = $_SESSION['message'] ?? '';
$message_type = $_SESSION['message_type'] ?? '';
unset($_SESSION['message']);
unset($_SESSION['message_type']);
?>
<!DOCTYPE html>
<html>
<head>
<title>Admin Panel</title>
<link rel="icon" href="../img/favicon.ico" type="image/x-icon">
<link rel="stylesheet" href="../css/bootstrap.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<link rel="stylesheet" href="css/adminpanel.css">
<script src="../js/bootstrap.bundle.js"></script>
<script>
function confirmDelete(uname) {
if (confirm('Are you sure you want to delete this user?')) {
window.location.href = 'adminpanel.php?delete=' + encodeURIComponent(uname);
}
}
</script>
</head>
<body class="text-center">
<div class="d-flex justify-content-end p-3">
<button id="themeToggle" class="btn btn-sm theme-toggle">
<i class="bi"></i>
<span id="themeText"></span>
</button>
</div>
<div class="custom-container">
<header class="row border-bottom m-5">
<h1>USB RAID Array</h1>
<div class="mb-3 p-3">
<a href="logout.php" class="btn btn-danger">Logout</a>
<a href="changepassword.php" class="btn btn-warning">Change Password</a>
<a href="ftp/index.php" class="btn btn-primary">SFTP</a>
</div>
</header>
<section class="row">
<article class="col border border-2 border-primary rounded p-2">
<h2 class="mb-4">User Management</h2>
<?php if ($message): ?>
<div class="alert alert-<?php echo $message_type === 'success' ? 'success' : 'danger'; ?>">
<?php echo htmlspecialchars($message); ?>
</div>
<?php endif; ?>
<form method="POST" action="" class="mb-4 add-user-form">
<div class="mb-3">
<label class="form-label">Username</label>
<input type="text" name="uname" class="form-control" placeholder="Username" required>
</div>
<div class="mb-3">
<label class="form-label">Password</label>
<input type="password" name="pswd" class="form-control" placeholder="Password" required>
</div>
<div class="mb-3">
<label class="form-label">Default Path</label>
<input type="text" name="defPath" class="form-control" placeholder="Default Path (e.g., /mnt/raid)" required>
</div>
<div class="d-flex flex-column align-items-center mb-3">
<div class="form-check mb-2">
<input type="checkbox" name="admin" id="adminCheck" value="1" class="form-check-input">
<label class="form-check-label" for="adminCheck">Admin</label>
</div>
<div class="form-check mb-2">
<input type="checkbox" name="delPer" id="delPerCheck" value="1" class="form-check-input">
<label class="form-check-label" for="delPerCheck">Delete Permission</label>
</div>
<div class="form-check mb-2">
<input type="checkbox" name="downPer" id="downPerCheck" value="1" class="form-check-input">
<label class="form-check-label" for="downPerCheck">Download Permission</label>
</div>
<div class="form-check mb-2">
<input type="checkbox" name="upPer" id="upPerCheck" value="1" class="form-check-input">
<label class="form-check-label" for="upPerCheck">Upload Permission</label>
</div>
</div>
<button type="submit" class="btn btn-primary">Add User</button>
</form>
<h3 class="mb-3">Users List</h3>
<table class="table table-bordered">
<thead class="table-dark">
<tr>
<th>Username</th>
<th>Admin</th>
<th>Default Path</th>
<th>Delete Permission</th>
<th>Download Permission</th>
<th>Upload Permission</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php while ($row = $result->fetch_assoc()) { ?>
<tr>
<td><?php echo htmlspecialchars($row['uname']); ?></td>
<td><?php echo $row['admin'] ? 'Yes' : 'No'; ?></td>
<td><?php echo htmlspecialchars($row['defPath']); ?></td>
<td><?php echo $row['delPer'] ? 'Yes' : 'No'; ?></td>
<td><?php echo $row['downPer'] ? 'Yes' : 'No'; ?></td>
<td><?php echo $row['upPer'] ? 'Yes' : 'No'; ?></td>
<td>
<button class="btn btn-danger btn-sm" onclick="confirmDelete('<?php echo htmlspecialchars($row['uname']); ?>')">Delete</button>
</td>
</tr>
<?php } ?>
</tbody>
</table>
</article>
</section>
<footer class="d-flex flex-column justify-content-center align-items-center p-3 border-top gap-3 m-3">
<span class="text-muted">Developed by Michal Sedlák</span>
<div class="d-flex gap-3">
<a href="https://github.com/michalcz10/USB-RAID-pole" class="text-decoration-none" target="_blank" rel="noopener noreferrer">
<img src="../img/GitHub_Logo.png" alt="GitHub Logo" class="img-fluid hover-effect light-logo" style="height: 32px;">
<img src="../img/GitHub_Logo_White.png" alt="GitHub Logo" class="img-fluid hover-effect dark-logo" style="height: 32px;">
</a>
<a href="https://app.freelo.io/public/shared-link-view/?a=81efbcb4df761b3f29cdc80855b41e6d&b=4519c717f0729cc8e953af661e9dc981" class="text-decoration-none" target="_blank" rel="noopener noreferrer">
<img src="../img/freelo-logo-rgb.png" alt="Freelo Logo" class="img-fluid hover-effect light-logo" style="height: 24px;">
<img src="../img/freelo-logo-rgb-on-dark.png" alt="Freelo Logo" class="img-fluid hover-effect dark-logo" style="height: 24px;">
</a>
</div>
</footer>
<script src="../js/theme.js"></script>
</div>
</body>
</html>
<?php
$conn->close();
?>

View File

@@ -0,0 +1,157 @@
<?php
session_start();
if (!isset($_SESSION['uname'])) {
header("location: ../index.php");
exit();
}
$servername = "localhost:3306";
$username = "UNAME";
$password = "PSWD";
$db = "usbraidlogin";
$message = "";
$messageType = "";
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['current_password'], $_POST['new_password'], $_POST['confirm_password'])) {
$current_password = htmlspecialchars($_POST['current_password']);
$new_password = htmlspecialchars($_POST['new_password']);
$confirm_password = htmlspecialchars($_POST['confirm_password']);
$uname = $_SESSION['uname'];
if ($new_password !== $confirm_password) {
$message = "New passwords do not match!";
$messageType = "danger";
} else {
$conn = new mysqli($servername, $username, $password, $db);
$conn->set_charset("utf8");
if ($conn->connect_error) {
$message = "Database connection failed!";
$messageType = "danger";
} else {
$sql = "SELECT pswd FROM users WHERE uname=?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("s", $uname);
$stmt->execute();
$result = $stmt->get_result();
if ($result && $result->num_rows > 0) {
$row = $result->fetch_assoc();
$stored_hash = $row["pswd"];
if (password_verify($current_password, $stored_hash)) {
$new_hash = password_hash($new_password, PASSWORD_BCRYPT);
$update_sql = "UPDATE users SET pswd=? WHERE uname=?";
$update_stmt = $conn->prepare($update_sql);
$update_stmt->bind_param("ss", $new_hash, $uname);
if ($update_stmt->execute()) {
$message = "Password changed successfully!";
$messageType = "success";
} else {
$message = "Error updating password: " . $conn->error;
$messageType = "danger";
}
$update_stmt->close();
} else {
$message = "Current password is incorrect!";
$messageType = "danger";
}
} else {
$message = "User not found!";
$messageType = "danger";
}
$stmt->close();
$conn->close();
}
}
} else {
$message = "All fields are required!";
$messageType = "danger";
}
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Change Password</title>
<link rel="icon" href="../img/favicon.ico" type="image/x-icon">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="../css/bootstrap.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<link rel="stylesheet" href="css/changepassword.css">
<script src="../js/bootstrap.bundle.js"></script>
</head>
<body class="text-center">
<div class="d-flex justify-content-end p-3">
<button id="themeToggle" class="btn btn-sm theme-toggle">
<i class="bi"></i>
<span id="themeText"></span>
</button>
</div>
<div class="container-fluid">
<header class="row border-bottom my-3 my-md-5">
<h1>USB RAID Array</h1>
<div class="mb-3 p-3">
<div class="btn-group-responsive">
<a href="logout.php" class="btn btn-danger">Logout</a>
<a href="ftp/index.php" class="btn btn-primary">Back to Files</a>
<?php if (isset($_SESSION["admin"]) && $_SESSION["admin"] == true) { ?>
<a href="adminpanel.php" class="btn btn-primary">Admin Panel</a>
<?php } ?>
</div>
</div>
</header>
<section class="row content-section">
<article class="col-12 border border-2 border-primary rounded p-2 p-md-4">
<h2 class="mb-4">Change Password</h2>
<?php if ($message): ?>
<div class="alert alert-<?php echo $messageType; ?> mb-4">
<?php echo $message; ?>
</div>
<?php endif; ?>
<form method="POST" action="" class="mb-4 change-password-form">
<div class="mb-3">
<label class="form-label">Current Password</label>
<input type="password" name="current_password" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-label">New Password</label>
<input type="password" name="new_password" class="form-control" required>
</div>
<div class="mb-3">
<label class="form-label">Confirm New Password</label>
<input type="password" name="confirm_password" class="form-control" required>
</div>
<button type="submit" class="btn btn-primary">Change Password</button>
</form>
</article>
</section>
<footer class="d-flex flex-column justify-content-center align-items-center p-3 border-top gap-3 m-3">
<span class="text-muted">Developed by Michal Sedlák</span>
<div class="d-flex gap-3 flex-wrap justify-content-center">
<a href="https://github.com/michalcz10/USB-RAID-pole" class="text-decoration-none" target="_blank" rel="noopener noreferrer">
<img src="../img/GitHub_Logo.png" alt="GitHub Logo" class="img-fluid hover-effect light-logo" style="height: 32px;">
<img src="../img/GitHub_Logo_White.png" alt="GitHub Logo" class="img-fluid hover-effect dark-logo" style="height: 32px;">
</a>
<a href="https://app.freelo.io/public/shared-link-view/?a=81efbcb4df761b3f29cdc80855b41e6d&b=4519c717f0729cc8e953af661e9dc981" class="text-decoration-none" target="_blank" rel="noopener noreferrer">
<img src="../img/freelo-logo-rgb.png" alt="Freelo Logo" class="img-fluid hover-effect light-logo" style="height: 24px;">
<img src="../img/freelo-logo-rgb-on-dark.png" alt="Freelo Logo" class="img-fluid hover-effect dark-logo" style="height: 24px;">
</a>
</div>
</footer>
<script src="../js/theme.js"></script>
</div>
</body>
</html>

View File

@@ -0,0 +1,52 @@
html, body {
height: 100%;
margin: 0;
}
.custom-container {
display: flex;
flex-direction: column;
min-height: 100vh;
max-width: 60%;
margin: 0 auto;
}
section.row {
flex: 1;
}
@media (max-width: 1120px) {
.custom-container {
max-width: 90%;
}
}
.add-user-form {
max-width: 400px;
margin: 0 auto;
}
.form-check {
display: flex;
align-items: center;
width: 100%;
justify-content: flex-start;
}
.form-check-input {
margin-right: 10px;
}
.form-check-label {
margin-left: 0.5rem;
}
body {
min-width: 800px;
}
.hover-effect {
transition: opacity 0.3s ease;
}
.hover-effect:hover {
opacity: 0.8;
}
.theme-light .dark-logo {
display: none;
}
.theme-dark .light-logo {
display: none;
}

View File

@@ -0,0 +1,56 @@
.container-fluid {
width: 100%;
padding-right: 15px;
padding-left: 15px;
margin-right: auto;
margin-left: auto;
}
.content-section {
max-width: 800px;
margin: 0 auto;
}
.change-password-form {
max-width: 100%;
margin: 0 auto;
}
@media (min-width: 768px) {
.change-password-form {
max-width: 400px;
}
}
.hover-effect {
transition: opacity 0.3s ease;
}
.hover-effect:hover {
opacity: 0.8;
}
.theme-light .dark-logo {
display: none;
}
.theme-dark .light-logo {
display: none;
}
.btn-group-responsive {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
justify-content: center;
}
h1 {
font-size: 1.8rem;
}
@media (min-width: 576px) {
h1 {
font-size: 2.5rem;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,253 @@
body {
min-height: 100vh;
margin: 0;
padding: 0;
}
#pdfContainer {
width: 100%;
min-height: calc(100vh - 80px);
display: flex;
flex-direction: column;
align-items: center;
padding: 20px 0;
}
#loadingMessage {
position: absolute;
top: 0;
left: 0;
z-index: 1000;
background-color: var(--bs-body-bg);
}
[data-bs-theme="dark"] #loadingMessage {
background-color: rgba(33, 37, 41, 0.9);
}
[data-bs-theme="light"] #loadingMessage {
background-color: rgba(248, 249, 250, 0.9);
}
.page-container {
background-color: white;
margin: 10px;
box-shadow: 0 0 10px rgba(0,0,0,0.3);
border-radius: 4px;
overflow: hidden;
}
.controls {
display: flex;
justify-content: center;
align-items: center;
margin: 0;
}
canvas {
display: block;
}
footer {
margin-top: auto;
padding: 20px;
border-top: 1px solid #ddd;
width: 100%;
}
.hover-effect {
transition: opacity 0.3s ease;
}
.hover-effect:hover {
opacity: 0.8;
}
.theme-light .dark-logo {
display: none;
}
.theme-dark .light-logo {
display: none;
}
.file-info table {
table-layout: fixed;
word-wrap: break-word;
}
.actionButton {
display: flex;
justify-content: center;
align-items: center;
margin: 0;
}
.fullscreen-mode {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100vh;
background-color: var(--bs-body-bg);
z-index: 9999;
padding: 0;
display: flex;
flex-direction: column;
overflow: hidden;
}
.fullscreen-mode header,
.fullscreen-mode footer,
.fullscreen-mode .theme-toggle,
.fullscreen-mode .actionButton {
display: none !important;
}
.fullscreen-mode #pdfContainer {
flex: 1;
margin: 0;
padding: 0;
height: 100vh;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.fullscreen-mode .controls {
position: fixed;
bottom: 10px;
left: 50%;
transform: translateX(-50%);
margin: 0;
padding: 6px 12px;
background-color: rgba(var(--bs-body-bg-rgb), 0.3);
backdrop-filter: blur(5px);
transition: all 0.3s ease;
opacity: 0.3;
z-index: 1000;
border-radius: .375rem;
width: auto;
max-width: 95%;
}
.fullscreen-mode .controls:hover {
opacity: 1;
background-color: rgba(var(--bs-body-bg-rgb), 0.9);
box-shadow: 0 0 15px rgba(0,0,0,0.2);
}
.fullscreen-mode[data-bs-theme="light"] .btn-light {
background-color: rgba(233, 236, 239, 0.7);
border-color: rgba(222, 226, 230, 0.7);
}
.fullscreen-mode[data-bs-theme="dark"] .btn-light {
background-color: rgba(52, 58, 64, 0.7);
border-color: rgba(73, 80, 87, 0.7);
}
.fullscreen-mode .btn-light:hover {
transform: scale(1.05);
transition: transform 0.2s ease;
}
.fullscreen-mode .btn-light.disabled {
background-color: rgba(var(--bs-body-bg-rgb), 0.5);
}
.fullscreen-mode .page-container {
display: flex;
justify-content: center;
align-items: center;
margin: 0;
height: 100vh;
width: 100%;
}
.fullscreen-mode canvas {
max-width: 100%;
max-height: 100vh;
width: 100% !important;
height: auto !important;
margin: 0;
object-fit: contain;
}
.fullscreen-mode .btn-group {
display: flex;
align-items: center;
gap: 4px;
}
.fullscreen-mode .btn-group .btn {
min-width: 44px;
display: flex;
align-items: center;
justify-content: center;
white-space: nowrap;
}
.fullscreen-mode .btn-group .btn.disabled {
min-width: 120px;
padding: 6px 12px;
}
/* Mobile-specific adjustments */
@media (max-width: 768px) {
.fullscreen-mode .controls {
bottom: 5px;
padding: 4px 8px;
width: auto;
max-width: calc(100% - 20px);
}
.fullscreen-mode .btn-group .btn {
min-width: 36px;
padding: 4px 6px;
}
.fullscreen-mode .btn-group .btn.disabled {
min-width: 110px;
padding: 4px 8px;
}
.fullscreen-mode #pageNum,
.fullscreen-mode #pageCount {
font-size: 0.875rem;
margin: 0 2px;
}
.fullscreen-mode .btn-group {
gap: 3px;
}
/* Add spacing between page numbers and "of" text */
.fullscreen-mode .btn-light.disabled span {
margin: 0 2px;
}
}
/* Theme-specific button styles */
[data-bs-theme="light"] .btn-light {
background-color: #e9ecef;
border-color: #dee2e6;
color: #212529;
}
[data-bs-theme="light"] .btn-light:hover {
background-color: #dde2e6;
border-color: #ced4da;
color: #000;
}
[data-bs-theme="dark"] .btn-light {
background-color: #343a40;
border-color: #495057;
color: #f8f9fa;
}
[data-bs-theme="dark"] .btn-light:hover {
background-color: #495057;
border-color: #6c757d;
color: #fff;
}
/* Button group specific styles */
.btn-group .btn-light {
margin: 0 1px;
}
.btn-group .btn-light.disabled {
opacity: 0.8;
}

View File

@@ -0,0 +1,124 @@
/* Layout */
body {
min-height: 100vh;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
}
footer {
margin-top: auto;
padding: 20px;
border-top: 1px solid var(--bs-border-color);
width: 100%;
}
/* Monitor Cards */
.monitor-card {
transition: transform 0.2s ease-in-out;
height: 100%;
}
.monitor-card:hover {
transform: translateY(-5px);
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
}
.monitor-card .card-body {
display: flex;
flex-direction: column;
justify-content: center;
padding: 1.5rem;
min-height: 320px;
}
.monitor-header {
background-color: var(--bs-body-bg);
border-bottom: 1px solid var(--bs-border-color);
}
.progress {
background-color: var(--bs-tertiary-bg);
}
/* CPU Gauge */
.cpu-gauge {
position: relative;
width: 100%;
height: 200px;
display: flex;
align-items: center;
justify-content: center;
}
.cpu-gauge canvas {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.cpu-gauge .position-relative {
position: absolute !important;
top: 50%;
left: 50%;
transform: translate(-50%, -55%);
z-index: 2;
width: auto;
text-align: center;
line-height: 1.2;
}
.cpu-gauge .h3 {
font-size: 1.75rem;
line-height: 1;
margin: 0;
}
.cpu-gauge .small {
opacity: 0.75;
margin-top: 3px;
}
/* Theme & Logo */
.hover-effect {
transition: opacity 0.3s ease;
}
.hover-effect:hover {
opacity: 0.8;
}
.theme-light .dark-logo {
display: none;
}
.theme-dark .light-logo {
display: none;
}
/* Grid Spacing */
.row.g-4 {
--bs-gutter-y: 2rem;
}
/* Responsive */
@media (max-width: 768px) {
.container {
padding: 0.5rem;
}
.h4 {
font-size: 1.25rem;
}
.monitor-card .card-body {
padding: 1rem;
min-height: 280px;
}
.row.g-4 > [class*="col-"] {
margin-bottom: 1rem;
}
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,244 @@
<?php
session_start();
if(!isset($_SESSION['uname'])){
header("location: ../../index.php");
session_destroy();
exit;
}
// Check if user has permission to upload/modify
if(!isset($_SESSION["upPer"]) || $_SESSION["upPer"] != true) {
die("You don't have permission to extract archives.");
}
// Include error reporting for debugging
ini_set('display_errors', 1);
error_reporting(E_ALL);
require 'config.php';
use phpseclib3\Net\SFTP;
$sftp = initializeSFTP($host, $username, $password);
// Process extraction request
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['archive'])) {
$archivePath = $_POST['archive'];
$dirPath = dirname($archivePath);
$archiveName = basename($archivePath);
$extension = pathinfo($archiveName, PATHINFO_EXTENSION);
// Create a temporary local directory for extraction
$tempDir = sys_get_temp_dir() . '/sftp_extract_' . time();
if (!mkdir($tempDir, 0777, true)) {
die("Failed to create temporary directory");
}
// Download the archive to the temp directory
$localArchivePath = $tempDir . '/' . $archiveName;
if (!$sftp->get($archivePath, $localArchivePath)) {
rmdir($tempDir);
die("Failed to download the archive");
}
// Detect archive type and extract
$extractionSuccess = false;
try {
// Determine the extraction method based on file extension
switch (strtolower($extension)) {
case 'zip':
$extractionSuccess = extractZip($localArchivePath, $tempDir);
break;
case 'rar':
$extractionSuccess = extractRar($localArchivePath, $tempDir);
break;
case 'tar':
case 'gz':
case 'bz2':
case 'xz':
case '7z':
$extractionSuccess = extractArchive($localArchivePath, $tempDir);
break;
default:
die("Unsupported archive format");
}
if ($extractionSuccess) {
// Upload extracted files back to the server
uploadExtractedFiles($sftp, $tempDir, $dirPath);
// Clean up temporary directory
deleteDirectory($tempDir);
// Redirect back to the file listing
header("Location: index.php?path=" . urlencode($dirPath));
exit;
} else {
die("Failed to extract the archive");
}
} catch (Exception $e) {
deleteDirectory($tempDir);
die("Error during extraction: " . $e->getMessage());
}
} else {
header("Location: index.php");
exit;
}
// Extract ZIP archives
function extractZip($archivePath, $destination) {
$zip = new ZipArchive();
if ($zip->open($archivePath) === TRUE) {
$zip->extractTo($destination);
$zip->close();
return true;
}
return false;
}
// Extract RAR archives (requires rar extension or unrar command)
function extractRar($archivePath, $destination) {
// Try PHP Rar extension first
if (extension_loaded('rar')) {
$rar = RarArchive::open($archivePath);
if ($rar) {
$entries = $rar->getEntries();
foreach ($entries as $entry) {
$entry->extract($destination);
}
$rar->close();
return true;
}
}
// Fallback to command line unrar if available
if (shell_exec("which unrar") || file_exists('C:\\Program Files\\WinRAR\\UnRAR.exe')) {
$command = '';
if (PHP_OS_FAMILY === 'Windows') {
$command = '"C:\\Program Files\\WinRAR\\UnRAR.exe" x -o+ ' . escapeshellarg($archivePath) . ' ' . escapeshellarg($destination);
} else {
$command = 'unrar x -o+ ' . escapeshellarg($archivePath) . ' ' . escapeshellarg($destination);
}
exec($command, $output, $returnCode);
return $returnCode === 0;
}
throw new Exception("RAR extraction is not available. Please install PHP RAR extension or UnRAR command line tool.");
}
// Extract other archives using system commands
function extractArchive($archivePath, $destination) {
$extension = pathinfo($archivePath, PATHINFO_EXTENSION);
$command = '';
// Change to the destination directory
$currentDir = getcwd();
chdir($destination);
if (PHP_OS_FAMILY === 'Windows') {
// For Windows, you'll need to have 7-Zip installed
$sevenZipPath = 'C:\\Program Files\\7-Zip\\7z.exe';
if (file_exists($sevenZipPath)) {
$command = '"' . $sevenZipPath . '" x ' . escapeshellarg($archivePath);
} else {
throw new Exception("7-Zip is not installed or not found at the expected location.");
}
} else {
// For Linux/Unix systems
switch (strtolower($extension)) {
case 'tar':
$command = 'tar -xf ' . escapeshellarg($archivePath);
break;
case 'gz':
if (strpos($archivePath, '.tar.gz') !== false) {
$command = 'tar -xzf ' . escapeshellarg($archivePath);
} else {
$command = 'gzip -d ' . escapeshellarg($archivePath);
}
break;
case 'bz2':
if (strpos($archivePath, '.tar.bz2') !== false) {
$command = 'tar -xjf ' . escapeshellarg($archivePath);
} else {
$command = 'bzip2 -d ' . escapeshellarg($archivePath);
}
break;
case 'xz':
if (strpos($archivePath, '.tar.xz') !== false) {
$command = 'tar -xJf ' . escapeshellarg($archivePath);
} else {
$command = 'xz -d ' . escapeshellarg($archivePath);
}
break;
case '7z':
$command = '7z x ' . escapeshellarg($archivePath);
break;
default:
throw new Exception("Unsupported archive format");
}
}
exec($command, $output, $returnCode);
// Change back to the original directory
chdir($currentDir);
return $returnCode === 0;
}
// Upload extracted files back to the SFTP server
function uploadExtractedFiles($sftp, $localDir, $remoteDir) {
$items = scandir($localDir);
foreach ($items as $item) {
if ($item === '.' || $item === '..') continue;
$localPath = $localDir . '/' . $item;
$remotePath = $remoteDir . '/' . $item;
if (is_dir($localPath)) {
// Create the directory on the remote server
if (!$sftp->is_dir($remotePath)) {
$sftp->mkdir($remotePath);
}
// Upload the contents of the directory
uploadExtractedFiles($sftp, $localPath, $remotePath);
} else {
// Upload the file - use the proper phpseclib3 method
$sftp->put($remotePath, $localPath, SFTP::SOURCE_LOCAL_FILE);
}
}
}
// Recursively delete a directory
function deleteDirectory($dir) {
if (!is_dir($dir)) {
return;
}
$items = scandir($dir);
foreach ($items as $item) {
if ($item === '.' || $item === '..') continue;
$path = $dir . '/' . $item;
if (is_dir($path)) {
deleteDirectory($path);
} else {
unlink($path);
}
}
rmdir($dir);
}
?>

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,331 @@
<?php
session_start();
if(!isset($_SESSION['uname'])){
header("location: ../../index.php");
session_destroy();
exit;
}
require 'config.php';
$sftp = initializeSFTP($host, $username, $password);
if (!isset($_GET['file'])) {
die("No file specified");
}
$filePath = $_GET['file'];
$fileName = basename($filePath);
$fileSize = $sftp->stat($filePath)['size'];
if (isset($_GET['stream'])) {
session_write_close();
while (ob_get_level()) {
ob_end_clean();
}
if (ini_get('zlib.output_compression')) {
ini_set('zlib.output_compression', 'Off');
}
$start = 0;
$end = $fileSize - 1;
$length = $fileSize;
if (isset($_SERVER['HTTP_RANGE'])) {
$rangeHeader = $_SERVER['HTTP_RANGE'];
$matches = [];
if (preg_match('/bytes=(\d+)-(\d*)/', $rangeHeader, $matches)) {
$start = intval($matches[1]);
if (!empty($matches[2])) {
$end = intval($matches[2]);
}
$length = $end - $start + 1;
header('HTTP/1.1 206 Partial Content');
header('Content-Range: bytes ' . $start . '-' . $end . '/' . $fileSize);
}
}
header('Content-Type: application/pdf');
header('Content-Disposition: inline; filename="' . basename($filePath) . '"');
header("Accept-Ranges: bytes");
header("Content-Length: $length");
header("Cache-Control: no-cache, no-store, must-revalidate");
header("Pragma: no-cache");
header("Expires: 0");
set_time_limit(0);
$minChunkSize = 64 * 1024; // 64KB minimum
$maxChunkSize = 2 * 1024 * 1024; // 2MB maximum
$chunkSize = 256 * 1024; // Start with 256KB
$currentPosition = $start;
$bytesRemaining = $length;
$lastChunkTime = microtime(true);
try {
while ($bytesRemaining > 0) {
if (connection_aborted()) {
break;
}
$readSize = min($chunkSize, $bytesRemaining);
$chunkData = $sftp->get($filePath, false, $currentPosition, $readSize);
if ($chunkData !== false) {
$bytesSent = strlen($chunkData);
echo $chunkData;
$bytesRemaining -= $bytesSent;
$currentPosition += $bytesSent;
if (ob_get_level()) {
ob_flush();
}
flush();
$currentTime = microtime(true);
$timeDiff = $currentTime - $lastChunkTime;
$lastChunkTime = $currentTime;
if ($timeDiff > 0) {
$speed = $bytesSent / $timeDiff;
$chunkSize = min(
max($minChunkSize, $chunkSize * (($speed > 512 * 1024) ? 1.5 : 0.8)),
$maxChunkSize
);
}
} else {
break;
}
}
} catch (Exception $e) {
error_log("Streaming error: " . $e->getMessage());
}
exit;
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PDF Viewer</title>
<link rel="icon" href="../../img/favicon.ico" type="image/x-icon">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="../../css/bootstrap.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<link rel="stylesheet" href="css/pdf.css">
<script src="../../js/bootstrap.bundle.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.js"></script>
</head>
<body>
<div class="d-flex justify-content-end p-3">
<button id="themeToggle" class="btn btn-sm theme-toggle">
<i class="bi"></i>
<span id="themeText"></span>
</button>
</div>
<div class="custom-container">
<header class="text-center border-bottom m-5">
<h1 class="mb-4">PDF Viewer</h1>
<div class="mb-3">
<a href="index.php?path=<?= urlencode(dirname($filePath)) ?>" class="btn btn-primary">Back to Files</a>
</div>
</header>
<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 class="bg-dark bg-opacity-75 text-white p-3 rounded">
<span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
Loading PDF...
</div>
</div>
</div>
<div class="controls">
<div class="btn-group" role="group" aria-label="PDF Navigation">
<button id="prev" class="btn btn-light border">
<i class="bi bi-chevron-left"></i> Previous
</button>
<button class="btn btn-light border disabled">
Page <span id="pageNum"></span> of <span id="pageCount"></span>
</button>
<button id="next" class="btn btn-light border">
Next <i class="bi bi-chevron-right"></i>
</button>
<button id="fullscreen" class="btn btn-light border">
<i class="bi bi-fullscreen"></i>
</button>
</div>
</div>
<div class="actionButton">
<?php if (isset($_SESSION["downPer"]) && $_SESSION["downPer"] == true) : ?>
<a href="download.php?file=<?= urlencode($filePath) ?>" class="btn btn-success m-3">Download</a>
<?php endif; ?>
</div>
<footer class="d-flex flex-column justify-content-center align-items-center p-3 border-top gap-3 m-3">
<span class="text-muted">Developed by Michal Sedlák</span>
<div class="d-flex gap-3">
<a href="https://github.com/michalcz10/USB-RAID-pole" class="text-decoration-none" target="_blank" rel="noopener noreferrer">
<img src="../../img/GitHub_Logo.png" alt="GitHub Logo" class="img-fluid hover-effect light-logo" style="height: 32px;">
<img src="../../img/GitHub_Logo_White.png" alt="GitHub Logo" class="img-fluid hover-effect dark-logo" style="height: 32px;">
</a>
<a href="https://app.freelo.io/public/shared-link-view/?a=81efbcb4df761b3f29cdc80855b41e6d&b=4519c717f0729cc8e953af661e9dc981" class="text-decoration-none" target="_blank" rel="noopener noreferrer">
<img src="../../img/freelo-logo-rgb.png" alt="Freelo Logo" class="img-fluid hover-effect light-logo" style="height: 24px;">
<img src="../../img/freelo-logo-rgb-on-dark.png" alt="Freelo Logo" class="img-fluid hover-effect dark-logo" style="height: 24px;">
</a>
</div>
</footer>
</div>
<script>
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js';
const url = 'pdf.php?file=<?= urlencode($filePath) ?>&stream=1';
const pdfFileName = '<?= htmlspecialchars($fileName) ?>';
let currentPage = 1;
let pdfDoc = null;
const PdfPageStorage = {
getStorageKey() {
return `pdf_page_${pdfFileName}`;
},
savePage(pageNum) {
localStorage.setItem(this.getStorageKey(), pageNum.toString());
},
loadPage() {
return parseInt(localStorage.getItem(this.getStorageKey())) || 1;
}
};
async function loadPDF() {
try {
pdfDoc = await pdfjsLib.getDocument(url).promise;
document.getElementById('pageCount').textContent = pdfDoc.numPages;
currentPage = Math.min(PdfPageStorage.loadPage(), pdfDoc.numPages);
renderPage(currentPage);
document.getElementById('loadingMessage').classList.add('d-none');
} catch (error) {
console.error('Error loading PDF:', error);
const loadingMessage = document.getElementById('loadingMessage');
loadingMessage.querySelector('div').classList.add('bg-danger');
loadingMessage.querySelector('div').innerHTML = '<i class="bi bi-exclamation-triangle"></i> Error loading PDF';
}
}
async function renderPage(pageNumber) {
const page = await pdfDoc.getPage(pageNumber);
const pageContainer = document.createElement('div');
pageContainer.className = 'page-container';
const windowWidth = window.innerWidth;
const windowHeight = window.innerHeight;
const viewport = page.getViewport({ scale: 1.0 });
const pixelRatio = window.devicePixelRatio || 1;
const widthScale = (windowWidth / viewport.width);
const heightScale = (windowHeight / viewport.height);
let scale;
if (isFullscreen) {
scale = Math.min(widthScale, heightScale) * 0.95;
} else if (windowWidth < 768) {
scale = widthScale * 0.95;
} else {
scale = Math.min(widthScale, heightScale, 2);
}
const scaledViewport = page.getViewport({ scale: scale * pixelRatio });
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.height = scaledViewport.height;
canvas.width = scaledViewport.width;
canvas.style.width = `${scaledViewport.width / pixelRatio}px`;
canvas.style.height = `${scaledViewport.height / pixelRatio}px`;
pageContainer.appendChild(canvas);
document.getElementById('pdfContainer').innerHTML = '';
document.getElementById('pdfContainer').appendChild(pageContainer);
document.getElementById('pageNum').textContent = pageNumber;
await page.render({
canvasContext: context,
viewport: scaledViewport // Use scaledViewport instead of viewport
}).promise;
PdfPageStorage.savePage(pageNumber);
}
document.getElementById('prev').addEventListener('click', () => {
if (currentPage > 1) {
currentPage--;
renderPage(currentPage);
}
});
document.getElementById('next').addEventListener('click', () => {
if (currentPage < pdfDoc.numPages) {
currentPage++;
renderPage(currentPage);
}
});
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && isFullscreen) {
document.getElementById('fullscreen').click();
} else if (e.key === 'ArrowLeft' && currentPage > 1) {
currentPage--;
renderPage(currentPage);
} else if (e.key === 'ArrowRight' && currentPage < pdfDoc.numPages) {
currentPage++;
renderPage(currentPage);
}
});
window.addEventListener('resize', () => {
if (currentPage) {
renderPage(currentPage);
}
});
let isFullscreen = false;
document.getElementById('fullscreen').addEventListener('click', () => {
const container = document.querySelector('.custom-container');
const fullscreenBtn = document.getElementById('fullscreen');
const fullscreenIcon = fullscreenBtn.querySelector('i');
if (!isFullscreen) {
container.classList.add('fullscreen-mode');
document.body.style.overflow = 'hidden';
fullscreenIcon.classList.remove('bi-fullscreen');
fullscreenIcon.classList.add('bi-fullscreen-exit');
} else {
container.classList.remove('fullscreen-mode');
document.body.style.overflow = '';
fullscreenIcon.classList.remove('bi-fullscreen-exit');
fullscreenIcon.classList.add('bi-fullscreen');
}
isFullscreen = !isFullscreen;
renderPage(currentPage);
});
loadPDF();
</script>
<script src="../../js/theme.js"></script>
</body>
</html>

View File

@@ -0,0 +1,48 @@
<?php
session_start();
if(!isset($_SESSION['uname'])){
header("location: ../../index.php");
session_destroy();
exit;
}
if(!isset($_SESSION["upPer"]) || $_SESSION["upPer"] != true) {
die("You don't have permission to rename files or directories.");
}
ini_set('display_errors', 1);
error_reporting(E_ALL);
require 'config.php';
$sftp = initializeSFTP($host, $username, $password);
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$oldPath = isset($_POST['path']) ? $_POST['path'] : '';
$newName = isset($_POST['newName']) ? trim($_POST['newName']) : '';
$isDirectory = isset($_POST['isDirectory']) && $_POST['isDirectory'] == '1';
if (empty($oldPath) || empty($newName)) {
die("Missing required information for renaming.");
}
$dirPath = dirname($oldPath);
$oldName = basename($oldPath);
$newPath = $dirPath . '/' . $newName;
if ($sftp->file_exists($newPath) || $sftp->is_dir($newPath)) {
die("Error: A file or directory with this name already exists.");
}
if ($sftp->rename($oldPath, $newPath)) {
header("Location: index.php?path=" . urlencode($dirPath));
exit;
} else {
die("Failed to rename the item. Please try again.");
}
} else {
header("Location: index.php");
exit;
}
?>

View File

@@ -0,0 +1,560 @@
<?php
// Include this at the top to see potential errors
// Comment out in production
ini_set('display_errors', 1);
error_reporting(E_ALL);
session_start();
if (empty($_SESSION['admin']) || $_SESSION['admin'] !== true) {
session_destroy();
header("location: ../../index.php");
exit();
}
require '../../vendor/autoload.php';
use phpseclib3\Net\SSH2;
$ssh_host = 'localhost';
$ssh_username = 'uname';
$ssh_password = 'pswd';
$ssh = new SSH2($ssh_host);
if (!$ssh->login($ssh_username, $ssh_password)) {
die('SSH login failed');
}
function getDiskData($ssh, $device) {
return $ssh->exec("df -h $device");
}
function getSystemData($ssh) {
// Get memory info using awk to clean up the output
$meminfo = $ssh->exec("free -m | awk 'NR==2{printf \"%s,%s,%s,\", $2,$3,$7} NR==3{printf \"%s,%s,%s\", $2,$3,$4}'");
// Use vmstat to get CPU idle percentage, more reliable and commonly available
$cpuIdle = $ssh->exec("vmstat 1 2 | tail -1 | awk '{print $15}'");
return [
'storage' => [
'system' => parseDiskData($ssh->exec('df -h /dev/ubuntu-vg/ubuntu-lv')),
'data' => parseDiskData($ssh->exec('df -h /dev/sdb1'))
],
'raid' => parseRaidStatus($ssh->exec('cat /proc/mdstat')),
'resources' => parseSystemStats($cpuIdle, $meminfo),
'lastUpdate' => date('Y-m-d H:i:s')
];
}
$systemData = getSystemData($ssh);
$monitors = [
[
'id' => 'SystemDisk',
'title' => 'System Disk (sda)',
'icon' => 'hdd',
'color' => 'primary',
'type' => 'storage',
'source' => 'system'
],
[
'id' => 'DataDisk',
'title' => 'Data Disk (sdb)',
'icon' => 'hdd',
'color' => 'primary',
'type' => 'storage',
'source' => 'data'
],
[
'id' => 'RAID',
'title' => 'RAID Status',
'icon' => 'shield-check',
'color' => 'warning',
'type' => 'raid'
],
[
'id' => 'CPU',
'title' => 'CPU Usage',
'icon' => 'cpu',
'color' => 'info',
'type' => 'resources',
'source' => 'cpu'
],
[
'id' => 'Memory',
'title' => 'Memory Usage',
'icon' => 'memory',
'color' => 'success',
'type' => 'resources',
'source' => 'memory'
]
];
function parseDiskData($output) {
$lines = explode("\n", trim($output));
if (count($lines) < 2) return null;
$values = preg_split('/\s+/', trim($lines[1]));
return [
'size' => $values[1] ?? 'N/A',
'used' => $values[2] ?? 'N/A',
'available' => $values[3] ?? 'N/A',
'usage' => $values[4] ?? '0%'
];
}
function parseRaidStatus($output) {
if (empty($output)) {
return [
'active' => false,
'status' => 'unknown',
'type' => 'N/A'
];
}
$status = [
'active' => false,
'status' => 'unknown',
'type' => 'N/A'
];
$lines = explode("\n", trim($output));
foreach ($lines as $line) {
if (preg_match('/active\s+(\w+)/', $line, $matches)) {
$status['active'] = true;
$status['type'] = $matches[1] ?? 'N/A';
}
if (strpos($line, '[UU]') !== false) {
$status['status'] = 'healthy';
} elseif (strpos($line, '_') !== false) {
$status['status'] = 'degraded';
}
}
return $status;
}
function parseSystemStats($cpuOutput, $memOutput) {
// Parse CPU stats - convert idle percentage to usage percentage
$idlePercent = floatval(trim(str_replace(',', '.', $cpuOutput)));
$cpuUsage = 100 - $idlePercent; // Convert idle to usage percentage
if (is_nan($cpuUsage) || $cpuUsage < 0 || $cpuUsage > 100) {
$cpuUsage = 0; // Default to 0% if parsing fails
}
$cpu = ['usage' => number_format($cpuUsage, 1)];
// Parse memory stats
$parts = explode(',', $memOutput);
if (count($parts) >= 6) {
$memTotal = intval($parts[0]);
$memUsed = intval($parts[1]);
$memAvail = intval($parts[2]);
$swapTotal = intval($parts[3]);
$swapUsed = intval($parts[4]);
$swapFree = intval($parts[5]);
$memory = [
'total' => number_format($memTotal / 1024, 2) . 'G',
'used' => number_format($memUsed / 1024, 2) . 'G',
'available' => number_format($memAvail / 1024, 2) . 'G',
'usage' => ($memTotal > 0 ? round(($memUsed / $memTotal) * 100, 1) : 0) . '%',
'swap_total' => number_format($swapTotal / 1024, 2) . 'G',
'swap_used' => number_format($swapUsed / 1024, 2) . 'G',
'swap_free' => number_format($swapFree / 1024, 2) . 'G',
'swap_usage' => ($swapTotal > 0 ? round(($swapUsed / $swapTotal) * 100, 1) : 0) . '%'
];
} else {
$memory = [
'total' => '0G',
'used' => '0G',
'available' => '0G',
'usage' => '0%',
'swap_total' => '0G',
'swap_used' => '0G',
'swap_free' => '0G',
'swap_usage' => '0%'
];
}
return ['cpu' => $cpu, 'memory' => $memory];
}
function convertToBytes($size) {
if (preg_match('/^([\d.]+)([KMGT]?)i?B?$/', trim($size), $matches)) {
$value = floatval($matches[1]);
$unit = strtoupper($matches[2]);
switch ($unit) {
case 'P': $value *= 1024;
case 'T': $value *= 1024;
case 'G': $value *= 1024;
case 'M': $value *= 1024;
case 'K': $value *= 1024;
}
return $value;
}
return 0;
}
function formatBytes($bytes, $forceUnit = '') {
$units = ['B', 'K', 'M', 'G', 'T', 'P'];
$bytes = max($bytes, 0);
if ($forceUnit) {
$unitIndex = array_search($forceUnit, $units);
if ($unitIndex !== false) {
$bytes /= pow(1024, $unitIndex);
return round($bytes, 2) . $forceUnit;
}
}
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1);
$bytes /= pow(1024, $pow);
return round($bytes, 2) . $units[$pow];
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<title>Server Status</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="icon" href="../../img/favicon.ico" type="image/x-icon">
<link rel="stylesheet" href="../../css/bootstrap.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<link rel="stylesheet" href="css/serverstat.css">
<script src="../../js/bootstrap.bundle.js"></script>
</head>
<body class="container-fluid text-center">
<div class="d-flex justify-content-end p-3">
<button id="themeToggle" class="btn btn-sm theme-toggle">
<i class="bi"></i>
<span id="themeText"></span>
</button>
</div>
<header class="row border-bottom m-5">
<h1>Server Status</h1>
<div class="mb-3 p-3">
<a href="../logout.php" class="btn btn-danger">Logout</a>
<a href="../changepassword.php" class="btn btn-warning">Change Password</a>
<a href="../adminpanel.php" class="btn btn-primary">Admin Panel</a>
<a href="index.php" class="btn btn-primary">SFTP</a>
</div>
</header>
<div class="container py-4">
<div class="row justify-content-center">
<div class="col-lg-10">
<div class="card shadow-sm bg-body-tertiary">
<div class="card-header monitor-header">
<div class="d-flex justify-content-between align-items-center">
<h1 class="h4 mb-0">
<i class="bi bi-server text-primary me-2"></i>
Server Disk Status
</h1>
<span class="badge bg-light text-dark">
<i class="bi bi-clock me-1"></i>
<?= htmlspecialchars($systemData['lastUpdate']) ?>
</span>
</div>
</div>
<div class="card-body">
<div class="row g-4">
<?php foreach ($monitors as $monitor): ?>
<div class="col-md-6">
<div class="monitor-card card h-100 border-<?= $monitor['color'] ?>">
<div class="card-header bg-<?= $monitor['color'] ?> bg-opacity-10">
<h3 class="h5 mb-0">
<i class="bi bi-<?= $monitor['icon'] ?> text-<?= $monitor['color'] ?> me-2"></i>
<?= $monitor['title'] ?>
</h3>
</div>
<div class="card-body">
<?php if ($monitor['type'] === 'storage'):
$diskData = $systemData['storage'][$monitor['source']];
$usagePercent = intval(rtrim($diskData['usage'], '%'));
?>
<div class="mb-3">
<div class="d-flex justify-content-between mb-1">
<span>Storage Usage</span>
<span><?= $diskData['usage'] ?></span>
</div>
<div class="progress" style="height: 10px">
<div class="progress-bar bg-<?= $usagePercent > 90 ? 'danger' : ($usagePercent > 75 ? 'warning' : 'success') ?>"
role="progressbar"
style="width: <?= $usagePercent ?>%"
aria-valuenow="<?= $usagePercent ?>"
aria-valuemin="0"
aria-valuemax="100">
</div>
</div>
</div>
<div class="row text-center g-2">
<div class="col-4">
<div class="p-2 border rounded">
<div class="small text-muted">Total</div>
<div class="fw-bold"><?= $diskData['size'] ?></div>
</div>
</div>
<div class="col-4">
<div class="p-2 border rounded">
<div class="small text-muted">Used</div>
<div class="fw-bold"><?= $diskData['used'] ?></div>
</div>
</div>
<div class="col-4">
<div class="p-2 border rounded">
<div class="small text-muted">Free</div>
<div class="fw-bold"><?= $diskData['available'] ?></div>
</div>
</div>
</div>
<?php elseif ($monitor['type'] === 'raid'): ?>
<div class="text-center">
<div class="display-4 mb-2">
<i class="bi bi-<?= $systemData['raid']['active'] ? 'check-circle-fill text-success' : 'x-circle-fill text-danger' ?>"></i>
</div>
<h4 class="h6"><?= $systemData['raid']['type'] ?> Array</h4>
<span class="badge bg-<?= $systemData['raid']['status'] === 'healthy' ? 'success' : 'warning' ?>">
<?= ucfirst($systemData['raid']['status']) ?>
</span>
</div>
<?php elseif ($monitor['type'] === 'resources' && $monitor['source'] === 'cpu'): ?>
<div class="cpu-gauge">
<canvas id="cpuGauge" width="150" height="150" data-value="<?= $systemData['resources']['cpu']['usage'] ?>"></canvas>
</div>
<?php elseif ($monitor['type'] === 'resources' && $monitor['source'] === 'memory'):
$memData = $systemData['resources']['memory'];
$usagePercent = floatval(rtrim($memData['usage'], '%'));
$swapPercent = floatval(rtrim($memData['swap_usage'], '%'));
?>
<div class="mb-3">
<div class="d-flex justify-content-between mb-1">
<span>Memory Usage</span>
<span><?= $memData['usage'] ?></span>
</div>
<div class="progress" style="height: 10px">
<div class="progress-bar bg-<?= $usagePercent > 90 ? 'danger' : ($usagePercent > 75 ? 'warning' : 'success') ?>"
role="progressbar"
style="width: <?= $usagePercent ?>%"
aria-valuenow="<?= $usagePercent ?>"
aria-valuemin="0"
aria-valuemax="100">
</div>
</div>
</div>
<div class="row text-center g-2 mb-3">
<div class="col-4">
<div class="p-2 border rounded">
<div class="small text-muted">Total</div>
<div class="fw-bold"><?= $memData['total'] ?></div>
</div>
</div>
<div class="col-4">
<div class="p-2 border rounded">
<div class="small text-muted">Used</div>
<div class="fw-bold"><?= $memData['used'] ?></div>
</div>
</div>
<div class="col-4">
<div class="p-2 border rounded">
<div class="small text-muted">Available</div>
<div class="fw-bold"><?= $memData['available'] ?></div>
</div>
</div>
</div>
<div class="mb-3">
<div class="d-flex justify-content-between mb-1">
<span>Swap Usage</span>
<span><?= $memData['swap_usage'] ?></span>
</div>
<div class="progress" style="height: 10px">
<div class="progress-bar bg-<?= $swapPercent > 90 ? 'danger' : ($swapPercent > 75 ? 'warning' : 'success') ?>"
role="progressbar"
style="width: <?= $swapPercent ?>%"
aria-valuenow="<?= $swapPercent ?>"
aria-valuemin="0"
aria-valuemax="100">
</div>
</div>
</div>
<div class="row text-center g-2">
<div class="col-4">
<div class="p-2 border rounded">
<div class="small text-muted">Total</div>
<div class="fw-bold"><?= $memData['swap_total'] ?></div>
</div>
</div>
<div class="col-4">
<div class="p-2 border rounded">
<div class="small text-muted">Used</div>
<div class="fw-bold"><?= $memData['swap_used'] ?></div>
</div>
</div>
<div class="col-4">
<div class="p-2 border rounded">
<div class="small text-muted">Free</div>
<div class="fw-bold"><?= $memData['swap_free'] ?></div>
</div>
</div>
</div>
<?php endif; ?>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
</div>
</div>
</div>
</div>
<footer class="d-flex flex-column justify-content-center align-items-center p-3 border-top gap-3">
<span class="text-muted">Developed by Michal Sedlák</span>
<div class="d-flex gap-3">
<a href="https://github.com/michalcz10/USB-RAID-pole" class="text-decoration-none" target="_blank" rel="noopener noreferrer">
<img src="../../img/GitHub_Logo.png" alt="GitHub Logo" class="img-fluid hover-effect light-logo" style="height: 32px;">
<img src="../../img/GitHub_Logo_White.png" alt="GitHub Logo" class="img-fluid hover-effect dark-logo" style="height: 32px;">
</a>
<a href="https://app.freelo.io/public/shared-link-view/?a=81efbcb4df761b3f29cdc80855b41e6d&b=4519c717f0729cc8e953af661e9dc981" class="text-decoration-none" target="_blank" rel="noopener noreferrer">
<img src="../../img/freelo-logo-rgb.png" alt="Freelo Logo" class="img-fluid hover-effect light-logo" style="height: 24px;">
<img src="../../img/freelo-logo-rgb-on-dark.png" alt="Freelo Logo" class="img-fluid hover-effect dark-logo" style="height: 24px;">
</a>
</div>
</footer>
<script src="../../js/theme.js"></script>
<script>
let refreshInterval;
let isRefreshing = false;
function updateData(html) {
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
// Update monitor cards
document.querySelectorAll('.monitor-card').forEach((card, index) => {
const newCard = doc.querySelectorAll('.monitor-card')[index];
if (newCard && card.querySelector('.card-body')) {
card.querySelector('.card-body').innerHTML = newCard.querySelector('.card-body').innerHTML;
// Redraw CPU gauge if this is the CPU card
if (card.querySelector('.cpu-gauge')) {
const cpuValue = parseFloat(newCard.querySelector('canvas').getAttribute('data-value') || '0');
drawCpuGauge(cpuValue);
}
}
});
// Update last update time
const newLastUpdate = doc.querySelector('.badge.bg-light.text-dark')?.innerHTML;
const currentLastUpdate = document.querySelector('.badge.bg-light.text-dark');
if (newLastUpdate && currentLastUpdate && newLastUpdate !== currentLastUpdate.innerHTML) {
currentLastUpdate.innerHTML = newLastUpdate;
}
// Update CPU gauge if needed
const cpuValue = document.querySelector('.cpu-gauge .h4')?.textContent;
if (cpuValue) {
drawCpuGauge(parseFloat(cpuValue));
}
}
function startRefresh() {
if (!refreshInterval) {
refreshInterval = setInterval(() => {
if (!isRefreshing) {
isRefreshing = true;
fetch(window.location.href)
.then(response => response.text())
.then(html => {
updateData(html);
isRefreshing = false;
})
.catch(() => {
isRefreshing = false;
});
}
}, 5000);
}
}
function stopRefresh() {
if (refreshInterval) {
clearInterval(refreshInterval);
refreshInterval = null;
}
}
// Start refresh when page is visible
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
stopRefresh();
} else {
startRefresh();
}
});
// Initial setup
startRefresh();
drawCpuGauge(parseFloat(document.querySelector('.cpu-gauge .h4')?.textContent || '0'));
function drawCpuGauge(value) {
const canvas = document.getElementById('cpuGauge');
if (!canvas) return;
// Ensure value is a valid number between 0 and 100
value = parseFloat(value) || 0;
value = Math.max(0, Math.min(100, value));
const ctx = canvas.getContext('2d');
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const radius = Math.min(centerX, centerY) - 10;
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw background circle
ctx.beginPath();
ctx.arc(centerX, centerY, radius, 0, Math.PI * 2);
ctx.strokeStyle = getComputedStyle(document.body).getPropertyValue('--bs-border-color');
ctx.lineWidth = 10;
ctx.stroke();
// Draw value arc
const startAngle = -Math.PI / 2;
const endAngle = startAngle + (Math.PI * 2 * value / 100);
ctx.beginPath();
ctx.arc(centerX, centerY, radius, startAngle, endAngle);
ctx.strokeStyle = value > 90 ? '#dc3545' : value > 75 ? '#ffc107' : '#198754';
ctx.lineWidth = 10;
ctx.stroke();
// Draw percentage text inside the circle
ctx.font = 'bold 20px Arial';
ctx.fillStyle = getComputedStyle(document.body).getPropertyValue('--bs-body-color');
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(`${value.toFixed(1)}%`, centerX, centerY - 10);
// Draw 'CPU USAGE' text below the percentage
ctx.font = '12px Arial';
ctx.fillText('CPU USAGE', centerX, centerY + 15);
}
// Initial CPU gauge draw
window.addEventListener('load', function() {
const cpuValue = parseFloat(document.querySelector('.cpu-gauge canvas')?.getAttribute('data-value') || '0');
drawCpuGauge(cpuValue);
});
</script>
</body>
</html>

View File

@@ -0,0 +1,340 @@
<?php
// Include this at the top to see potential errors
// Comment out in production
ini_set('display_errors', 1);
error_reporting(E_ALL);
require 'config.php';
function getDatabaseConnection() {
$servername = "localhost:3306";
$username = "UNAME";
$password = "PSWD";
$db = "usbraidlogin";
$conn = new mysqli($servername, $username, $password, $db);
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
return $conn;
}
if (!isset($_GET['token'])) {
die("Invalid request. No token provided.");
}
$token = $_GET['token'];
$conn = getDatabaseConnection();
// Get share information
$stmt = $conn->prepare("SELECT * FROM share WHERE token = ? AND expiration > NOW()");
$stmt->bind_param("s", $token);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows === 0) {
die("Invalid or expired share link.");
}
$share = $result->fetch_assoc();
$filePath = $share['file_path'];
$downloadAllowed = $share['download_allowed'] == 1;
$stmt->close();
// Initialize SFTP
$sftp = initializeSFTP($host, $username, $password);
if (!$sftp->stat($filePath)) {
die("File not found: $filePath");
}
$fileName = basename($filePath);
$fileExtension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
$fileSize = $sftp->stat($filePath)['size'];
$imageTypes = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg'];
$videoTypes = ['mp4', 'webm', 'ogg', 'mov', 'avi', 'mkv'];
$audioTypes = ['mp3', 'wav', 'ogg', 'm4a', 'flac', 'aac'];
$isImage = in_array($fileExtension, $imageTypes);
$isVideo = in_array($fileExtension, $videoTypes);
$isAudio = in_array($fileExtension, $audioTypes);
if (!$isImage && !$isVideo && !$isAudio) {
die("Unsupported file type");
}
$mimeMap = [
'mp4' => 'video/mp4',
'webm' => 'video/mp4',
'ogg' => 'video/mp4',
'mov' => 'video/mp4',
'avi' => 'video/mp4',
'mkv' => 'video/mp4',
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'png' => 'image/png',
'gif' => 'image/gif',
'bmp' => 'image/bmp',
'webp' => 'image/webp',
'svg' => 'image/svg+xml',
'mp3' => 'audio/mpeg',
'wav' => 'audio/wav',
'm4a' => 'audio/mp4',
'flac' => 'audio/flac',
'aac' => 'audio/aac',
];
$mimeType = isset($mimeMap[$fileExtension]) ? $mimeMap[$fileExtension] : 'application/octet-stream';
// If streaming requested, stream file
if (isset($_GET['stream'])) {
// Similar streaming code as in view.php
session_write_close();
while (ob_get_level()) {
ob_end_clean();
}
if (ini_get('zlib.output_compression')) {
ini_set('zlib.output_compression', 'Off');
}
$start = 0;
$end = $fileSize - 1;
$length = $fileSize;
if (isset($_SERVER['HTTP_RANGE'])) {
$rangeHeader = $_SERVER['HTTP_RANGE'];
$matches = [];
if (preg_match('/bytes=(\d+)-(\d*)/', $rangeHeader, $matches)) {
$start = intval($matches[1]);
if (!empty($matches[2])) {
$end = intval($matches[2]);
}
$length = $end - $start + 1;
header('HTTP/1.1 206 Partial Content');
header('Content-Range: bytes ' . $start . '-' . $end . '/' . $fileSize);
}
}
header("Content-Type: $mimeType");
header("Accept-Ranges: bytes");
header("Content-Length: $length");
header("Cache-Control: no-cache, no-store, must-revalidate");
header("Pragma: no-cache");
header("Expires: 0");
set_time_limit(0);
$minChunkSize = 64 * 1024;
$maxChunkSize = 2 * 1024 * 1024;
$chunkSize = 256 * 1024;
$currentPosition = $start;
$bytesRemaining = $length;
$lastChunkTime = microtime(true);
try {
while ($bytesRemaining > 0) {
if (connection_aborted() || connection_status() !== CONNECTION_NORMAL) {
break;
}
$readSize = min($chunkSize, $bytesRemaining);
$chunkData = $sftp->get($filePath, false, $currentPosition, $readSize);
if ($chunkData !== false) {
$bytesSent = strlen($chunkData);
echo $chunkData;
$bytesRemaining -= $bytesSent;
$currentPosition += $bytesSent;
if (ob_get_level()) {
ob_flush();
}
flush();
$currentTime = microtime(true);
$timeDiff = $currentTime - $lastChunkTime;
$lastChunkTime = $currentTime;
if ($timeDiff > 0) {
$speed = $bytesSent / $timeDiff;
$chunkSize = min(
max($minChunkSize, $chunkSize * (($speed > 512 * 1024) ? 1.5 : 0.8)),
$maxChunkSize
);
}
} else {
break;
}
usleep(100000);
}
} catch (Exception $e) {
error_log("Shared streaming error: " . $e->getMessage());
}
exit;
}
// Create download handler
if (isset($_GET['download'])) {
if (!$downloadAllowed) {
die("Download not permitted for this share.");
}
// Download file code similar to download.php
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"$fileName\"");
header("Content-Length: $fileSize");
$minChunkSize = 64 * 1024;
$maxChunkSize = 2 * 1024 * 1024;
$chunkSize = 256 * 1024;
$currentPosition = 0;
$bytesRemaining = $fileSize;
set_time_limit(0);
while ($bytesRemaining > 0) {
$readSize = min($chunkSize, $bytesRemaining);
$chunkData = $sftp->get($filePath, false, $currentPosition, $readSize);
if ($chunkData !== false) {
$bytesRead = strlen($chunkData);
echo $chunkData;
$bytesRemaining -= $bytesRead;
$currentPosition += $bytesRead;
flush();
} else {
break;
}
}
exit;
}
function formatBytes($bytes, $precision = 2) {
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
$bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1);
$bytes /= (1 << (10 * $pow));
return round($bytes, $precision) . ' ' . $units[$pow];
}
?>
<!DOCTYPE html>
<html lang="en" data-bs-theme="<?= isset($_COOKIE['theme']) ? $_COOKIE['theme'] : 'light' ?>">
<head>
<title>Shared Media - <?= htmlspecialchars($fileName) ?></title>
<link rel="icon" href="../../img/favicon.ico" type="image/x-icon">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="../../css/bootstrap.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<link rel="stylesheet" href="css/view.css">
<script src="../../js/bootstrap.bundle.js"></script>
</head>
<body class="text-center">
<div class="d-flex justify-content-end p-3">
<button id="themeToggle" class="btn btn-sm theme-toggle">
<i class="bi"></i>
<span id="themeText"></span>
</button>
</div>
<div class="custom-container">
<header class="row border-bottom m-5">
<h1>Shared Media</h1>
<div class="mb-3">
<span class="badge bg-info">Shared Link</span>
<p class="text-muted small">This link will expire on <?= date('F j, Y, g:i a', strtotime($share['expiration'])) ?></p>
</div>
</header>
<section class="row">
<article class="col-12">
<div class="media-container position-relative">
<?php if ($isImage): ?>
<img src="shared.php?token=<?= urlencode($token) ?>&stream=1" alt="<?= htmlspecialchars($fileName) ?>" class="img-fluid">
<?php elseif ($isVideo): ?>
<video id="videoPlayer" controls autoplay playsinline>
<source src="shared.php?token=<?= urlencode($token) ?>&stream=1" type="<?= $mimeType ?>">
Your browser does not support this video format.
</video>
<?php if($fileExtension !== 'mp4'): ?>
<div class="alert alert-warning mt-2">
Note: For best results, use MP4 format (H.264 codec). Other formats may not play correctly in all browsers.
</div>
<?php endif; ?>
<?php elseif ($isAudio): ?>
<audio controls autoplay style="width: 80%;">
<source src="shared.php?token=<?= urlencode($token) ?>&stream=1" type="<?= $mimeType ?>">
Your browser does not support the audio element.
</audio>
<?php endif; ?>
<?php if ($downloadAllowed) : ?>
<a href="shared.php?token=<?= urlencode($token) ?>&download=1" class="btn btn-success m-3">Download</a>
<?php endif; ?>
</div>
<div class="file-info mt-4">
<h4>File Information</h4>
<table class="table table-bordered w-auto mx-auto text-wrap">
<tr>
<th>File Name</th>
<td class="text-break"><?= htmlspecialchars($fileName) ?></td>
</tr>
<tr>
<th>File Type</th>
<td><?= htmlspecialchars(strtoupper($fileExtension)) ?></td>
</tr>
<tr>
<th>File Size</th>
<td><?= formatBytes($fileSize) ?></td>
</tr>
</table>
</div>
</article>
</section>
<footer class="d-flex flex-column justify-content-center align-items-center p-3 border-top gap-3 m-3">
<span class="text-muted">Developed by Michal Sedlák</span>
<div class="d-flex gap-3">
<a href="https://github.com/michalcz10/USB-RAID-pole" class="text-decoration-none" target="_blank" rel="noopener noreferrer">
<img src="../../img/GitHub_Logo.png" alt="GitHub Logo" class="img-fluid hover-effect light-logo" style="height: 32px;">
<img src="../../img/GitHub_Logo_White.png" alt="GitHub Logo" class="img-fluid hover-effect dark-logo" style="height: 32px;">
</a>
<a href="https://app.freelo.io/public/shared-link-view/?a=81efbcb4df761b3f29cdc80855b41e6d&b=4519c717f0729cc8e953af661e9dc981" class="text-decoration-none" target="_blank" rel="noopener noreferrer">
<img src="../../img/freelo-logo-rgb.png" alt="Freelo Logo" class="img-fluid hover-effect light-logo" style="height: 24px;">
<img src="../../img/freelo-logo-rgb-on-dark.png" alt="Freelo Logo" class="img-fluid hover-effect dark-logo" style="height: 24px;">
</a>
</div>
</footer>
</div>
<script src="../../js/theme.js"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
const mediaElement = document.querySelector('video, audio');
if (mediaElement) {
const savedVolume = localStorage.getItem('mediaVolume');
if (savedVolume !== null) {
mediaElement.volume = parseFloat(savedVolume);
}
mediaElement.addEventListener('volumechange', () => {
localStorage.setItem('mediaVolume', mediaElement.volume);
});
}
});
</script>
</body>
</html>

View File

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

View File

@@ -0,0 +1,473 @@
<?php
session_start();
if(!isset($_SESSION['uname'])){
header("location: ../../index.php");
session_destroy();
exit;
}
// Include this at the top to see potential errors
// Comment out in production
ini_set('display_errors', 1);
error_reporting(E_ALL);
ini_set('memory_limit', '512M');
require 'config.php';
$sftp = initializeSFTP($host, $username, $password);
if (!isset($_GET['file'])) {
die("No file specified");
}
if (isset($_POST['createShare'])) {
$duration = intval($_POST['duration']);
$downloadAllowed = isset($_POST['downloadAllowed']) ? 1 : 0;
$filePath = $_POST['filePath'];
$token = bin2hex(random_bytes(16));
date_default_timezone_set('Europe/Prague');
$expiration = date('Y-m-d H:i:s', strtotime("+$duration hours"));
$conn = getDatabaseConnection();
$stmt = $conn->prepare("INSERT INTO share (token, file_path, expiration, download_allowed) VALUES (?, ?, ?, ?)");
$stmt->bind_param("sssi", $token, $filePath, $expiration, $downloadAllowed);
if ($stmt->execute()) {
$shareUrl = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://$_SERVER[HTTP_HOST]" .
dirname($_SERVER['PHP_SELF']) . "/shared.php?token=$token";
$_SESSION['shareCreated'] = true;
$_SESSION['shareUrl'] = $shareUrl;
$_SESSION['shareExpiration'] = $expiration;
header("Location: view.php?file=" . urlencode($filePath) . "&shared=1");
exit;
} else {
$_SESSION['shareError'] = "Failed to create share: " . $conn->error;
header("Location: view.php?file=" . urlencode($filePath) . "&shared=0");
exit;
}
$stmt->close();
$conn->close();
}
$shareCreated = false;
$shareUrl = '';
$shareExpiration = '';
$shareError = '';
if (isset($_GET['shared'])) {
if ($_GET['shared'] == '1' && isset($_SESSION['shareCreated']) && $_SESSION['shareCreated']) {
$shareCreated = true;
$shareUrl = $_SESSION['shareUrl'];
$shareExpiration = $_SESSION['shareExpiration'];
} elseif ($_GET['shared'] == '0' && isset($_SESSION['shareError'])) {
$shareError = $_SESSION['shareError'];
}
}
function getDatabaseConnection() {
$servername = "localhost:3306";
$username = "UNAME";
$password = "PSWD";
$db = "usbraidlogin";
$conn = new mysqli($servername, $username, $password, $db);
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
return $conn;
}
$filePath = $_GET['file'];
$fileName = basename($filePath);
$fileExtension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
if (!$sftp->stat($filePath)) {
die("File not found: $filePath");
}
$imageTypes = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg'];
$videoTypes = ['mp4', 'webm', 'ogg', 'mov', 'avi', 'mkv'];
$audioTypes = ['mp3', 'wav', 'ogg', 'm4a', 'flac', 'aac'];
$isImage = in_array($fileExtension, $imageTypes);
$isVideo = in_array($fileExtension, $videoTypes);
$isAudio = in_array($fileExtension, $audioTypes);
if (!$isImage && !$isVideo && !$isAudio) {
die("Unsupported file type");
}
$fileSize = $sftp->stat($filePath)['size'];
$mimeMap = [
'mp4' => 'video/mp4',
'webm' => 'video/mp4',
'ogg' => 'video/mp4',
'mov' => 'video/mp4',
'avi' => 'video/mp4',
'mkv' => 'video/mp4',
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'png' => 'image/png',
'gif' => 'image/gif',
'bmp' => 'image/bmp',
'webp' => 'image/webp',
'svg' => 'image/svg+xml',
'mp3' => 'audio/mpeg',
'wav' => 'audio/wav',
'm4a' => 'audio/mp4',
'flac' => 'audio/flac',
'aac' => 'audio/aac',
];
$mimeType = isset($mimeMap[$fileExtension]) ? $mimeMap[$fileExtension] : 'application/octet-stream';
if (isset($_GET['stream'])) {
// Close session to allow other scripts to run
session_write_close();
// Prevent output buffering
while (ob_get_level()) {
ob_end_clean();
}
// Disable output compression
if (ini_get('zlib.output_compression')) {
ini_set('zlib.output_compression', 'Off');
}
// Initialize range variables
$start = 0;
$end = $fileSize - 1;
$length = $fileSize;
// Handle range requests
if (isset($_SERVER['HTTP_RANGE'])) {
$rangeHeader = $_SERVER['HTTP_RANGE'];
$matches = [];
if (preg_match('/bytes=(\d+)-(\d*)/', $rangeHeader, $matches)) {
$start = intval($matches[1]);
if (!empty($matches[2])) {
$end = intval($matches[2]);
}
$length = $end - $start + 1;
header('HTTP/1.1 206 Partial Content');
header('Content-Range: bytes ' . $start . '-' . $end . '/' . $fileSize);
}
}
// Set headers for streaming
header("Content-Type: $mimeType");
header("Accept-Ranges: bytes");
header("Content-Length: $length");
header("Cache-Control: no-cache, no-store, must-revalidate");
header("Pragma: no-cache");
header("Expires: 0");
// Debug headers
if (isset($_GET['debug'])) {
header("X-Stream-Info: Chunked SFTP Streaming");
header("X-File-Path: " . basename($filePath));
header("X-File-Size: $fileSize");
}
// Set timeout to 0 to prevent script termination
set_time_limit(0);
// Chunk settings
$minChunkSize = 64 * 1024; // 64KB minimum
$maxChunkSize = 2 * 1024 * 1024; // 2MB maximum
$chunkSize = 256 * 1024; // Start with 256KB
$currentPosition = $start;
$bytesRemaining = $length;
$lastChunkTime = microtime(true);
try {
while ($bytesRemaining > 0) {
// Check client connection and server status
if (connection_aborted() || connection_status() !== CONNECTION_NORMAL) {
if (isset($_GET['debug'])) {
error_log("Client disconnected at position $currentPosition");
}
break;
}
// Calculate adaptive chunk size
$readSize = min($chunkSize, $bytesRemaining);
// Get chunk from SFTP
$chunkData = $sftp->get($filePath, false, $currentPosition, $readSize);
if ($chunkData !== false) {
$bytesSent = strlen($chunkData);
// Output chunk
echo $chunkData;
$bytesRemaining -= $bytesSent;
$currentPosition += $bytesSent;
// Flush buffers
if (ob_get_level()) {
ob_flush();
}
flush();
// Adaptive chunk sizing based on transfer speed
$currentTime = microtime(true);
$timeDiff = $currentTime - $lastChunkTime;
$lastChunkTime = $currentTime;
if ($timeDiff > 0) {
$speed = $bytesSent / $timeDiff; // bytes/second
$chunkSize = min(
max($minChunkSize, $chunkSize * (($speed > 512 * 1024) ? 1.5 : 0.8)),
$maxChunkSize
);
}
if (isset($_GET['debug'])) {
header("X-Chunk-Size: $bytesSent");
header("X-Position: $currentPosition");
header("X-Remaining: $bytesRemaining");
}
} else {
error_log("SFTP read error at position $currentPosition");
break;
}
// Throttle to prevent CPU overload
usleep(100000); // 100ms
}
} catch (Exception $e) {
error_log("Streaming error: " . $e->getMessage());
if (isset($_GET['debug'])) {
header("X-Stream-Error: " . $e->getMessage());
}
}
if (isset($_GET['debug'])) {
error_log("Streaming completed. Sent $currentPosition bytes of $fileSize");
}
exit;
}
?>
<!DOCTYPE html>
<html lang="en" data-bs-theme="<?= isset($_COOKIE['theme']) ? $_COOKIE['theme'] : 'light' ?>">
<head>
<title>Media Viewer - <?= htmlspecialchars($fileName) ?></title>
<link rel="icon" href="../../img/favicon.ico" type="image/x-icon">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="../../css/bootstrap.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<link rel="stylesheet" href="css/view.css">
<script src="../../js/bootstrap.bundle.js"></script>
</head>
<body class="text-center">
<div class="d-flex justify-content-end p-3">
<button id="themeToggle" class="btn btn-sm theme-toggle">
<i class="bi"></i>
<span id="themeText"></span>
</button>
</div>
<div class="custom-container">
<header class="row border-bottom m-5">
<h1>Media Viewer</h1>
<div class="mb-3 p-3">
<a href="index.php?path=<?= urlencode(dirname($filePath)) ?>" class="btn btn-primary">Back to Files</a>
</div>
</header>
<section class="row">
<article class="col-12">
<div class="media-container position-relative">
<?php if ($isImage): ?>
<img src="view.php?file=<?= urlencode($filePath) ?>&stream=1" alt="<?= htmlspecialchars($fileName) ?>" class="img-fluid">
<?php elseif ($isVideo): ?>
<video id="videoPlayer" controls autoplay playsinline>
<source src="view.php?file=<?= urlencode($filePath) ?>&stream=1" type="<?= $mimeType ?>">
Your browser does not support this video format. Try downloading the file instead.
</video>
<?php if($fileExtension !== 'mp4'): ?>
<div class="alert alert-warning mt-2">
Note: For best results, use MP4 format (H.264 codec). Other formats may not play correctly in all browsers.
</div>
<?php endif; ?>
<?php elseif ($isAudio): ?>
<audio controls autoplay style="width: 80%;">
<source src="view.php?file=<?= urlencode($filePath) ?>&stream=1" type="<?= $mimeType ?>">
Your browser does not support the audio element.
</audio>
<?php endif; ?>
<?php if (isset($_SESSION["downPer"]) && $_SESSION["downPer"] == true) : ?>
<a href="download.php?file=<?= urlencode($filePath) ?>" class="btn btn-success m-3">Download</a>
<?php endif; ?>
<!-- Add Share Button -->
<button type="button" class="btn btn-info m-3" data-bs-toggle="modal" data-bs-target="#shareModal">
<i class="bi bi-share"></i> Share
</button>
</div>
<!-- Share success message -->
<?php if ($shareCreated): ?>
<div class="alert alert-success mt-3">
<p>Share link created successfully!</p>
<div class="input-group mb-3">
<input type="text" class="form-control" id="shareUrl" value="<?= htmlspecialchars($shareUrl) ?>" readonly>
<button class="btn btn-outline-secondary" type="button" onclick="copyShareUrl()">
<i class="bi bi-clipboard"></i> Copy
</button>
</div>
<p class="small text-muted">This link will expire on <?= date('F j, Y, g:i a', strtotime($shareExpiration)) ?></p>
</div>
<?php endif; ?>
<?php if ($shareError): ?>
<div class="alert alert-danger mt-3">
<?= htmlspecialchars($shareError) ?>
</div>
<?php endif; ?>
<div class="file-info mt-4">
<h4>File Information</h4>
<table class="table table-bordered w-auto mx-auto text-wrap">
<tr>
<th>File Name</th>
<td class="text-break"><?= htmlspecialchars($fileName) ?></td>
</tr>
<tr>
<th>File Type</th>
<td><?= htmlspecialchars(strtoupper($fileExtension)) ?></td>
</tr>
<tr>
<th>MIME Type</th>
<td class="text-break"><?= htmlspecialchars($mimeType) ?></td>
</tr>
<tr>
<th>File Size</th>
<td><?= formatBytes($fileSize) ?></td>
</tr>
</table>
</div>
</article>
</section>
<footer class="d-flex flex-column justify-content-center align-items-center p-3 border-top gap-3 m-3">
<span class="text-muted">Developed by Michal Sedlák</span>
<div class="d-flex gap-3">
<a href="https://github.com/michalcz10/USB-RAID-pole" class="text-decoration-none" target="_blank" rel="noopener noreferrer">
<img src="../../img/GitHub_Logo.png" alt="GitHub Logo" class="img-fluid hover-effect light-logo" style="height: 32px;">
<img src="../../img/GitHub_Logo_White.png" alt="GitHub Logo" class="img-fluid hover-effect dark-logo" style="height: 32px;">
</a>
<a href="https://app.freelo.io/public/shared-link-view/?a=81efbcb4df761b3f29cdc80855b41e6d&b=4519c717f0729cc8e953af661e9dc981" class="text-decoration-none" target="_blank" rel="noopener noreferrer">
<img src="../../img/freelo-logo-rgb.png" alt="Freelo Logo" class="img-fluid hover-effect light-logo" style="height: 24px;">
<img src="../../img/freelo-logo-rgb-on-dark.png" alt="Freelo Logo" class="img-fluid hover-effect dark-logo" style="height: 24px;">
</a>
</div>
</footer>
</div>
<!-- Share Modal -->
<div class="modal fade" id="shareModal" tabindex="-1" aria-labelledby="shareModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="shareModalLabel">Share "<?= htmlspecialchars($fileName) ?>"</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form method="post" action="">
<input type="hidden" name="filePath" value="<?= htmlspecialchars($filePath) ?>">
<div class="mb-3">
<label for="duration" class="form-label">Link expires after</label>
<select class="form-select" id="duration" name="duration">
<option value="1">1 hour</option>
<option value="6">6 hours</option>
<option value="24" selected>1 day</option>
<option value="168">1 week</option>
<option value="720">30 days</option>
</select>
</div>
<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input" id="downloadAllowed" name="downloadAllowed">
<label class="form-check-label" for="downloadAllowed">Allow downloading</label>
</div>
<div class="text-end">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" name="createShare" class="btn btn-primary">Create Share Link</button>
</div>
</form>
</div>
</div>
</div>
</div>
<script>
function copyShareUrl() {
const shareUrl = document.getElementById('shareUrl');
shareUrl.select();
document.execCommand('copy');
// Show feedback
const button = shareUrl.nextElementSibling;
const originalText = button.innerHTML;
button.innerHTML = '<i class="bi bi-check"></i> Copied!';
setTimeout(() => {
button.innerHTML = originalText;
}, 2000);
}
</script>
<script src="../../js/theme.js"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
const mediaElement = document.querySelector('video, audio');
if (mediaElement) {
// Restore volume from localStorage
const savedVolume = localStorage.getItem('mediaVolume');
if (savedVolume !== null) {
mediaElement.volume = parseFloat(savedVolume);
}
// Save volume to localStorage when it changes
mediaElement.addEventListener('volumechange', () => {
localStorage.setItem('mediaVolume', mediaElement.volume);
});
}
});
</script>
</body>
</html>
<?php
function formatBytes($bytes, $precision = 2) {
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
$bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1);
$bytes /= (1 << (10 * $pow));
return round($bytes, $precision) . ' ' . $units[$pow];
}
?>

View File

@@ -0,0 +1,83 @@
<?php
session_start();
if (isset($_POST['uname'], $_POST['pswd'])) {
$servername = "localhost:3306";
$username = "UNAME";
$password = "PSWD";
$db = "usbraidlogin";
$uname = htmlspecialchars($_POST['uname']);
$pswd = htmlspecialchars($_POST['pswd']);
$conn = new mysqli($servername, $username, $password, $db);
$conn->set_charset("utf8");
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$sql = "SELECT * FROM users WHERE uname=?";
$stmt = $conn->prepare($sql);
if (!$stmt) {
$_SESSION['login_error'] = "Database error. Please try again.";
$conn->close();
header("Location: ../index.php");
exit();
}
$stmt->bind_param("s", $uname);
$stmt->execute();
$result = $stmt->get_result();
if ($result && $result->num_rows > 0) {
$login_successful = false;
while ($row = $result->fetch_assoc()) {
$hash = $row["pswd"];
$admin = (bool)$row["admin"];
$defPath = $row["defPath"];
$delPer = $row["delPer"];
$downPer = $row["downPer"];
$upPer = $row["upPer"];
if (password_verify($pswd, $hash)) {
// Login successful - store info in session
$_SESSION['uname'] = $uname;
$_SESSION['admin'] = $admin;
$_SESSION['defPath'] = $defPath;
$_SESSION['downPer'] = $downPer;
$_SESSION['delPer'] = $delPer;
$_SESSION['upPer'] = $upPer;
$login_successful = true;
break;
}
}
$result->free_result();
$stmt->close();
$conn->close();
if ($login_successful) {
header("Location: ftp/index.php");
exit();
} else {
// If password verification fails
$_SESSION['login_error'] = "Invalid username or password.";
header("Location: ../index.php");
exit();
}
} else {
// If no user is found
$result->free_result();
$stmt->close();
$conn->close();
$_SESSION['login_error'] = "Invalid username or password.";
header("Location: ../index.php");
exit();
}
} else {
// If form fields are not set
$_SESSION['login_error'] = "Please fill in all fields.";
header("Location: ../index.php");
exit();
}
?>

View File

@@ -0,0 +1,23 @@
<?php
session_start();
$_SESSION = array();
if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params();
setcookie(
session_name(),
'',
time() - 42000,
$params["path"],
$params["domain"],
$params["secure"],
$params["httponly"]
);
}
session_destroy();
header("Location: ../index.php");
exit();
?>

12057
Web/betatest/css/bootstrap.css vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,47 @@
html, body {
height: 100%;
margin: 0;
}
body {
display: flex;
flex-direction: column;
}
main {
flex: 1;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
padding-top: 7%;
}
.fixed {
-ms-flex: 0 0 300px;
flex: 0 0 300px;
min-width: 300px;
}
.hover-effect {
transition: opacity 0.3s ease;
}
.hover-effect:hover {
opacity: 0.8;
}
.theme-toggle {
position: fixed;
top: 20px;
right: 20px;
z-index: 1000;
}
.theme-light .dark-logo {
display: none;
}
.theme-dark .light-logo {
display: none;
}

View File

@@ -0,0 +1,61 @@
/* Preloader styles */
.preloader {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
transition: opacity 0.5s;
}
.theme-light .preloader {
background-color: #ffffff;
}
.theme-dark .preloader {
background-color: #212529;
}
.spinner {
width: 50px;
height: 50px;
border: 5px solid rgba(0, 0, 0, 0.1);
border-radius: 50%;
border-top-color: #0d6efd;
animation: spin 1s ease-in-out infinite;
}
.theme-dark .spinner {
border: 5px solid rgba(255, 255, 255, 0.1);
border-top-color: #0d6efd;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.fade-in {
opacity: 0;
animation: fadeIn 0.5s forwards;
}
@keyframes fadeIn {
to {
opacity: 1;
}
}
.content-wrapper {
opacity: 0;
transition: opacity 0.3s;
}
.content-wrapper.visible {
opacity: 1;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

79
Web/betatest/index.php Normal file
View File

@@ -0,0 +1,79 @@
<?php
session_start();
if (empty($_SESSION)) {
if (session_status() === PHP_SESSION_ACTIVE) {
session_destroy();
}
} elseif (isset($_SESSION["uname"])) {
header("location: content/ftp/index.php");
exit();
} elseif (isset($_SESSION['login_error'])) {
echo "<script>alert('" . $_SESSION['login_error'] . "');</script>";
unset($_SESSION['login_error']);
}
?>
<!DOCTYPE html>
<html lang="cz">
<head>
<title>Login</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="icon" href="img/favicon.ico" type="image/x-icon">
<link rel="stylesheet" href="css/bootstrap.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<link rel="stylesheet" href="css/index.css">
<script src="js/bootstrap.bundle.js"></script>
</head>
<body class="container-fluid text-center">
<div class="d-flex justify-content-end p-3">
<button id="themeToggle" class="btn btn-sm theme-toggle">
<i class="bi"></i>
<span id="themeText"></span>
</button>
</div>
<header class="row border-bottom m-5">
<h1>USB RAID Array</h1>
</header>
<main>
<section class="row m-3">
<article class="col border border-2 border-primary rounded p-2 fixed">
<form action="content/login.php" method="post">
<div class="mb-3">
<label for="uname">Username:</label>
<br>
<input type="text" name="uname" class="form-control" required>
</div>
<div class="mb-3">
<label for="pswd">Password:</label>
<br>
<input type="password" name="pswd" class="form-control" required>
</div>
<div class="mb-3">
<input type="submit" class="btn btn-primary" value="Login">
</div>
</form>
</article>
</section>
</main>
<footer class="d-flex flex-column justify-content-center align-items-center p-3 border-top gap-3">
<span class="text-muted">Developed by Michal Sedlák</span>
<div class="d-flex gap-3">
<a href="https://github.com/michalcz10/USB-RAID-pole" class="text-decoration-none" target="_blank" rel="noopener noreferrer">
<img src="img/GitHub_Logo.png" alt="GitHub Logo" class="img-fluid hover-effect light-logo" style="height: 32px;">
<img src="img/GitHub_Logo_White.png" alt="GitHub Logo" class="img-fluid hover-effect dark-logo" style="height: 32px;">
</a>
<a href="https://app.freelo.io/public/shared-link-view/?a=81efbcb4df761b3f29cdc80855b41e6d&b=4519c717f0729cc8e953af661e9dc981" class="text-decoration-none" target="_blank" rel="noopener noreferrer">
<img src="img/freelo-logo-rgb.png" alt="Freelo Logo" class="img-fluid hover-effect light-logo" style="height: 24px;">
<img src="img/freelo-logo-rgb-on-dark.png" alt="Freelo Logo" class="img-fluid hover-effect dark-logo" style="height: 24px;">
</a>
</div>
</footer>
<script src="js/theme.js"></script>
</body>
</html>

6314
Web/betatest/js/bootstrap.bundle.js vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4447
Web/betatest/js/bootstrap.esm.js vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

7
Web/betatest/js/bootstrap.esm.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4494
Web/betatest/js/bootstrap.js vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

7
Web/betatest/js/bootstrap.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

40
Web/betatest/js/theme.js Normal file
View File

@@ -0,0 +1,40 @@
document.addEventListener('DOMContentLoaded', function() {
const themeToggle = document.getElementById('themeToggle');
const html = document.documentElement;
const themeText = document.getElementById('themeText');
const themeIcon = themeToggle.querySelector('.bi');
function setTheme(theme) {
html.setAttribute('data-bs-theme', theme);
document.body.classList.remove('theme-light', 'theme-dark');
document.body.classList.add('theme-' + theme);
localStorage.setItem('theme', theme);
if (theme === 'dark') {
themeText.textContent = 'Light Mode';
themeIcon.className = 'bi bi-sun';
themeToggle.classList.remove('btn-dark');
themeToggle.classList.add('btn-light');
} else {
themeText.textContent = 'Dark Mode';
themeIcon.className = 'bi bi-moon';
themeToggle.classList.remove('btn-light');
themeToggle.classList.add('btn-dark');
}
}
const savedTheme = localStorage.getItem('theme');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
if (savedTheme) {
setTheme(savedTheme);
} else {
setTheme(prefersDark ? 'dark' : 'light');
}
themeToggle.addEventListener('click', function() {
const currentTheme = html.getAttribute('data-bs-theme');
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
setTheme(newTheme);
});
});

25
Web/betatest/vendor/autoload.php vendored Normal file
View File

@@ -0,0 +1,25 @@
<?php
// autoload.php @generated by Composer
if (PHP_VERSION_ID < 50600) {
if (!headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
}
$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
if (!ini_get('display_errors')) {
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
fwrite(STDERR, $err);
} elseif (!headers_sent()) {
echo $err;
}
}
trigger_error(
$err,
E_USER_ERROR
);
}
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInita702940bbf24a822224611db5c22942b::getLoader();

View File

@@ -0,0 +1,579 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see https://www.php-fig.org/psr/psr-0/
* @see https://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
/** @var \Closure(string):void */
private static $includeFile;
/** @var string|null */
private $vendorDir;
// PSR-4
/**
* @var array<string, array<string, int>>
*/
private $prefixLengthsPsr4 = array();
/**
* @var array<string, list<string>>
*/
private $prefixDirsPsr4 = array();
/**
* @var list<string>
*/
private $fallbackDirsPsr4 = array();
// PSR-0
/**
* List of PSR-0 prefixes
*
* Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
*
* @var array<string, array<string, list<string>>>
*/
private $prefixesPsr0 = array();
/**
* @var list<string>
*/
private $fallbackDirsPsr0 = array();
/** @var bool */
private $useIncludePath = false;
/**
* @var array<string, string>
*/
private $classMap = array();
/** @var bool */
private $classMapAuthoritative = false;
/**
* @var array<string, bool>
*/
private $missingClasses = array();
/** @var string|null */
private $apcuPrefix;
/**
* @var array<string, self>
*/
private static $registeredLoaders = array();
/**
* @param string|null $vendorDir
*/
public function __construct($vendorDir = null)
{
$this->vendorDir = $vendorDir;
self::initializeIncludeClosure();
}
/**
* @return array<string, list<string>>
*/
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
}
return array();
}
/**
* @return array<string, list<string>>
*/
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
/**
* @return list<string>
*/
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
/**
* @return list<string>
*/
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
/**
* @return array<string, string> Array of classname => path
*/
public function getClassMap()
{
return $this->classMap;
}
/**
* @param array<string, string> $classMap Class to filename map
*
* @return void
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param list<string>|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*
* @return void
*/
public function add($prefix, $paths, $prepend = false)
{
$paths = (array) $paths;
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
$paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
$paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
$paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param list<string>|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
$paths = (array) $paths;
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
$paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
$paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
$paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param list<string>|string $paths The PSR-0 base directories
*
* @return void
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param list<string>|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*
* @return void
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*
* @return void
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
*
* @return void
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
}
/**
* The APCu prefix in use, or null if APCu caching is not enabled.
*
* @return string|null
*/
public function getApcuPrefix()
{
return $this->apcuPrefix;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*
* @return void
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
if (null === $this->vendorDir) {
return;
}
if ($prepend) {
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
} else {
unset(self::$registeredLoaders[$this->vendorDir]);
self::$registeredLoaders[$this->vendorDir] = $this;
}
}
/**
* Unregisters this instance as an autoloader.
*
* @return void
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
if (null !== $this->vendorDir) {
unset(self::$registeredLoaders[$this->vendorDir]);
}
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return true|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
$includeFile = self::$includeFile;
$includeFile($file);
return true;
}
return null;
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
return false;
}
if (null !== $this->apcuPrefix) {
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
if ($hit) {
return $file;
}
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if (false === $file && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if (null !== $this->apcuPrefix) {
apcu_add($this->apcuPrefix.$class, $file);
}
if (false === $file) {
// Remember that this class does not exist.
$this->missingClasses[$class] = true;
}
return $file;
}
/**
* Returns the currently registered loaders keyed by their corresponding vendor directories.
*
* @return array<string, self>
*/
public static function getRegisteredLoaders()
{
return self::$registeredLoaders;
}
/**
* @param string $class
* @param string $ext
* @return string|false
*/
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
$subPath = $class;
while (false !== $lastPos = strrpos($subPath, '\\')) {
$subPath = substr($subPath, 0, $lastPos);
$search = $subPath . '\\';
if (isset($this->prefixDirsPsr4[$search])) {
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
foreach ($this->prefixDirsPsr4[$search] as $dir) {
if (file_exists($file = $dir . $pathEnd)) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
return false;
}
/**
* @return void
*/
private static function initializeIncludeClosure()
{
if (self::$includeFile !== null) {
return;
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*
* @param string $file
* @return void
*/
self::$includeFile = \Closure::bind(static function($file) {
include $file;
}, null, null);
}
}

View File

@@ -0,0 +1,359 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer;
use Composer\Autoload\ClassLoader;
use Composer\Semver\VersionParser;
/**
* This class is copied in every Composer installed project and available to all
*
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
*
* To require its presence, you can require `composer-runtime-api ^2.0`
*
* @final
*/
class InstalledVersions
{
/**
* @var mixed[]|null
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
*/
private static $installed;
/**
* @var bool|null
*/
private static $canGetVendors;
/**
* @var array[]
* @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
private static $installedByVendor = array();
/**
* Returns a list of all package names which are present, either by being installed, replaced or provided
*
* @return string[]
* @psalm-return list<string>
*/
public static function getInstalledPackages()
{
$packages = array();
foreach (self::getInstalled() as $installed) {
$packages[] = array_keys($installed['versions']);
}
if (1 === \count($packages)) {
return $packages[0];
}
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
}
/**
* Returns a list of all package names with a specific type e.g. 'library'
*
* @param string $type
* @return string[]
* @psalm-return list<string>
*/
public static function getInstalledPackagesByType($type)
{
$packagesByType = array();
foreach (self::getInstalled() as $installed) {
foreach ($installed['versions'] as $name => $package) {
if (isset($package['type']) && $package['type'] === $type) {
$packagesByType[] = $name;
}
}
}
return $packagesByType;
}
/**
* Checks whether the given package is installed
*
* This also returns true if the package name is provided or replaced by another package
*
* @param string $packageName
* @param bool $includeDevRequirements
* @return bool
*/
public static function isInstalled($packageName, $includeDevRequirements = true)
{
foreach (self::getInstalled() as $installed) {
if (isset($installed['versions'][$packageName])) {
return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
}
}
return false;
}
/**
* Checks whether the given package satisfies a version constraint
*
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
*
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
*
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
* @param string $packageName
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
* @return bool
*/
public static function satisfies(VersionParser $parser, $packageName, $constraint)
{
$constraint = $parser->parseConstraints((string) $constraint);
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
return $provided->matches($constraint);
}
/**
* Returns a version constraint representing all the range(s) which are installed for a given package
*
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
* whether a given version of a package is installed, and not just whether it exists
*
* @param string $packageName
* @return string Version constraint usable with composer/semver
*/
public static function getVersionRanges($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
$ranges = array();
if (isset($installed['versions'][$packageName]['pretty_version'])) {
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
}
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
}
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
}
if (array_key_exists('provided', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
}
return implode(' || ', $ranges);
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
*/
public static function getVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['version'])) {
return null;
}
return $installed['versions'][$packageName]['version'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
*/
public static function getPrettyVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
return null;
}
return $installed['versions'][$packageName]['pretty_version'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
*/
public static function getReference($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['reference'])) {
return null;
}
return $installed['versions'][$packageName]['reference'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
*/
public static function getInstallPath($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @return array
* @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
*/
public static function getRootPackage()
{
$installed = self::getInstalled();
return $installed[0]['root'];
}
/**
* Returns the raw installed.php data for custom implementations
*
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
* @return array[]
* @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
*/
public static function getRawData()
{
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
if (null === self::$installed) {
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
self::$installed = include __DIR__ . '/installed.php';
} else {
self::$installed = array();
}
}
return self::$installed;
}
/**
* Returns the raw data of all installed.php which are currently loaded for custom implementations
*
* @return array[]
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
public static function getAllRawData()
{
return self::getInstalled();
}
/**
* Lets you reload the static array from another file
*
* This is only useful for complex integrations in which a project needs to use
* this class but then also needs to execute another project's autoloader in process,
* and wants to ensure both projects have access to their version of installed.php.
*
* A typical case would be PHPUnit, where it would need to make sure it reads all
* the data it needs from this class, then call reload() with
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
* the project in which it runs can then also use this class safely, without
* interference between PHPUnit's dependencies and the project's dependencies.
*
* @param array[] $data A vendor/composer/installed.php data set
* @return void
*
* @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
*/
public static function reload($data)
{
self::$installed = $data;
self::$installedByVendor = array();
}
/**
* @return array[]
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
private static function getInstalled()
{
if (null === self::$canGetVendors) {
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
}
$installed = array();
if (self::$canGetVendors) {
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
if (isset(self::$installedByVendor[$vendorDir])) {
$installed[] = self::$installedByVendor[$vendorDir];
} elseif (is_file($vendorDir.'/composer/installed.php')) {
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
$required = require $vendorDir.'/composer/installed.php';
$installed[] = self::$installedByVendor[$vendorDir] = $required;
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
self::$installed = $installed[count($installed) - 1];
}
}
}
}
if (null === self::$installed) {
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
$required = require __DIR__ . '/installed.php';
self::$installed = $required;
} else {
self::$installed = array();
}
}
if (self::$installed !== array()) {
$installed[] = self::$installed;
}
return $installed;
}
}

19
Web/betatest/vendor/composer/LICENSE vendored Normal file
View File

@@ -0,0 +1,19 @@
Copyright (c) Nils Adermann, Jordi Boggiano
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,10 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
);

View File

@@ -0,0 +1,10 @@
<?php
// autoload_files.php @generated by Composer
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
'decc78cc4436b1292c6c0d151b19445c' => $vendorDir . '/phpseclib/phpseclib/phpseclib/bootstrap.php',
);

View File

@@ -0,0 +1,9 @@
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
);

View File

@@ -0,0 +1,11 @@
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
'phpseclib3\\' => array($vendorDir . '/phpseclib/phpseclib/phpseclib'),
'ParagonIE\\ConstantTime\\' => array($vendorDir . '/paragonie/constant_time_encoding/src'),
);

View File

@@ -0,0 +1,50 @@
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInita702940bbf24a822224611db5c22942b
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
/**
* @return \Composer\Autoload\ClassLoader
*/
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
require __DIR__ . '/platform_check.php';
spl_autoload_register(array('ComposerAutoloaderInita702940bbf24a822224611db5c22942b', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
spl_autoload_unregister(array('ComposerAutoloaderInita702940bbf24a822224611db5c22942b', 'loadClassLoader'));
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInita702940bbf24a822224611db5c22942b::getInitializer($loader));
$loader->register(true);
$filesToLoad = \Composer\Autoload\ComposerStaticInita702940bbf24a822224611db5c22942b::$files;
$requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
require $file;
}
}, null, null);
foreach ($filesToLoad as $fileIdentifier => $file) {
$requireFile($fileIdentifier, $file);
}
return $loader;
}
}

View File

@@ -0,0 +1,48 @@
<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInita702940bbf24a822224611db5c22942b
{
public static $files = array (
'decc78cc4436b1292c6c0d151b19445c' => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib/bootstrap.php',
);
public static $prefixLengthsPsr4 = array (
'p' =>
array (
'phpseclib3\\' => 11,
),
'P' =>
array (
'ParagonIE\\ConstantTime\\' => 23,
),
);
public static $prefixDirsPsr4 = array (
'phpseclib3\\' =>
array (
0 => __DIR__ . '/..' . '/phpseclib/phpseclib/phpseclib',
),
'ParagonIE\\ConstantTime\\' =>
array (
0 => __DIR__ . '/..' . '/paragonie/constant_time_encoding/src',
),
);
public static $classMap = array (
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInita702940bbf24a822224611db5c22942b::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInita702940bbf24a822224611db5c22942b::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInita702940bbf24a822224611db5c22942b::$classMap;
}, null, ClassLoader::class);
}
}

View File

@@ -0,0 +1,242 @@
{
"packages": [
{
"name": "paragonie/constant_time_encoding",
"version": "v3.0.0",
"version_normalized": "3.0.0.0",
"source": {
"type": "git",
"url": "https://github.com/paragonie/constant_time_encoding.git",
"reference": "df1e7fde177501eee2037dd159cf04f5f301a512"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/df1e7fde177501eee2037dd159cf04f5f301a512",
"reference": "df1e7fde177501eee2037dd159cf04f5f301a512",
"shasum": ""
},
"require": {
"php": "^8"
},
"require-dev": {
"phpunit/phpunit": "^9",
"vimeo/psalm": "^4|^5"
},
"time": "2024-05-08T12:36:18+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"ParagonIE\\ConstantTime\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Paragon Initiative Enterprises",
"email": "security@paragonie.com",
"homepage": "https://paragonie.com",
"role": "Maintainer"
},
{
"name": "Steve 'Sc00bz' Thomas",
"email": "steve@tobtu.com",
"homepage": "https://www.tobtu.com",
"role": "Original Developer"
}
],
"description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)",
"keywords": [
"base16",
"base32",
"base32_decode",
"base32_encode",
"base64",
"base64_decode",
"base64_encode",
"bin2hex",
"encoding",
"hex",
"hex2bin",
"rfc4648"
],
"support": {
"email": "info@paragonie.com",
"issues": "https://github.com/paragonie/constant_time_encoding/issues",
"source": "https://github.com/paragonie/constant_time_encoding"
},
"install-path": "../paragonie/constant_time_encoding"
},
{
"name": "paragonie/random_compat",
"version": "v9.99.100",
"version_normalized": "9.99.100.0",
"source": {
"type": "git",
"url": "https://github.com/paragonie/random_compat.git",
"reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a",
"reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a",
"shasum": ""
},
"require": {
"php": ">= 7"
},
"require-dev": {
"phpunit/phpunit": "4.*|5.*",
"vimeo/psalm": "^1"
},
"suggest": {
"ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
},
"time": "2020-10-15T08:29:30+00:00",
"type": "library",
"installation-source": "dist",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Paragon Initiative Enterprises",
"email": "security@paragonie.com",
"homepage": "https://paragonie.com"
}
],
"description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
"keywords": [
"csprng",
"polyfill",
"pseudorandom",
"random"
],
"support": {
"email": "info@paragonie.com",
"issues": "https://github.com/paragonie/random_compat/issues",
"source": "https://github.com/paragonie/random_compat"
},
"install-path": "../paragonie/random_compat"
},
{
"name": "phpseclib/phpseclib",
"version": "3.0.43",
"version_normalized": "3.0.43.0",
"source": {
"type": "git",
"url": "https://github.com/phpseclib/phpseclib.git",
"reference": "709ec107af3cb2f385b9617be72af8cf62441d02"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/709ec107af3cb2f385b9617be72af8cf62441d02",
"reference": "709ec107af3cb2f385b9617be72af8cf62441d02",
"shasum": ""
},
"require": {
"paragonie/constant_time_encoding": "^1|^2|^3",
"paragonie/random_compat": "^1.4|^2.0|^9.99.99",
"php": ">=5.6.1"
},
"require-dev": {
"phpunit/phpunit": "*"
},
"suggest": {
"ext-dom": "Install the DOM extension to load XML formatted public keys.",
"ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.",
"ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.",
"ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.",
"ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations."
},
"time": "2024-12-14T21:12:59+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"files": [
"phpseclib/bootstrap.php"
],
"psr-4": {
"phpseclib3\\": "phpseclib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jim Wigginton",
"email": "terrafrost@php.net",
"role": "Lead Developer"
},
{
"name": "Patrick Monnerat",
"email": "pm@datasphere.ch",
"role": "Developer"
},
{
"name": "Andreas Fischer",
"email": "bantu@phpbb.com",
"role": "Developer"
},
{
"name": "Hans-Jürgen Petrich",
"email": "petrich@tronic-media.com",
"role": "Developer"
},
{
"name": "Graham Campbell",
"email": "graham@alt-three.com",
"role": "Developer"
}
],
"description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.",
"homepage": "http://phpseclib.sourceforge.net",
"keywords": [
"BigInteger",
"aes",
"asn.1",
"asn1",
"blowfish",
"crypto",
"cryptography",
"encryption",
"rsa",
"security",
"sftp",
"signature",
"signing",
"ssh",
"twofish",
"x.509",
"x509"
],
"support": {
"issues": "https://github.com/phpseclib/phpseclib/issues",
"source": "https://github.com/phpseclib/phpseclib/tree/3.0.43"
},
"funding": [
{
"url": "https://github.com/terrafrost",
"type": "github"
},
{
"url": "https://www.patreon.com/phpseclib",
"type": "patreon"
},
{
"url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib",
"type": "tidelift"
}
],
"install-path": "../phpseclib/phpseclib"
}
],
"dev": true,
"dev-package-names": []
}

View File

@@ -0,0 +1,50 @@
<?php return array(
'root' => array(
'name' => '__root__',
'pretty_version' => '1.0.0+no-version-set',
'version' => '1.0.0.0',
'reference' => null,
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'dev' => true,
),
'versions' => array(
'__root__' => array(
'pretty_version' => '1.0.0+no-version-set',
'version' => '1.0.0.0',
'reference' => null,
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'dev_requirement' => false,
),
'paragonie/constant_time_encoding' => array(
'pretty_version' => 'v3.0.0',
'version' => '3.0.0.0',
'reference' => 'df1e7fde177501eee2037dd159cf04f5f301a512',
'type' => 'library',
'install_path' => __DIR__ . '/../paragonie/constant_time_encoding',
'aliases' => array(),
'dev_requirement' => false,
),
'paragonie/random_compat' => array(
'pretty_version' => 'v9.99.100',
'version' => '9.99.100.0',
'reference' => '996434e5492cb4c3edcb9168db6fbb1359ef965a',
'type' => 'library',
'install_path' => __DIR__ . '/../paragonie/random_compat',
'aliases' => array(),
'dev_requirement' => false,
),
'phpseclib/phpseclib' => array(
'pretty_version' => '3.0.43',
'version' => '3.0.43.0',
'reference' => '709ec107af3cb2f385b9617be72af8cf62441d02',
'type' => 'library',
'install_path' => __DIR__ . '/../phpseclib/phpseclib',
'aliases' => array(),
'dev_requirement' => false,
),
),
);

View File

@@ -0,0 +1,26 @@
<?php
// platform_check.php @generated by Composer
$issues = array();
if (!(PHP_VERSION_ID >= 80000)) {
$issues[] = 'Your Composer dependencies require a PHP version ">= 8.0.0". You are running ' . PHP_VERSION . '.';
}
if ($issues) {
if (!headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
}
if (!ini_get('display_errors')) {
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
} elseif (!headers_sent()) {
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
}
}
trigger_error(
'Composer detected issues in your platform: ' . implode(' ', $issues),
E_USER_ERROR
);
}

View File

@@ -0,0 +1,48 @@
The MIT License (MIT)
Copyright (c) 2016 - 2022 Paragon Initiative Enterprises
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
------------------------------------------------------------------------------
This library was based on the work of Steve "Sc00bz" Thomas.
------------------------------------------------------------------------------
The MIT License (MIT)
Copyright (c) 2014 Steve Thomas
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,88 @@
# Constant-Time Encoding
[![Build Status](https://github.com/paragonie/constant_time_encoding/actions/workflows/ci.yml/badge.svg)](https://github.com/paragonie/constant_time_encoding/actions)
[![Static Analysis](https://github.com/paragonie/constant_time_encoding/actions/workflows/psalm.yml/badge.svg)](https://github.com/paragonie/constant_time_encoding/actions)
[![Latest Stable Version](https://poser.pugx.org/paragonie/constant_time_encoding/v/stable)](https://packagist.org/packages/paragonie/constant_time_encoding)
[![Latest Unstable Version](https://poser.pugx.org/paragonie/constant_time_encoding/v/unstable)](https://packagist.org/packages/paragonie/constant_time_encoding)
[![License](https://poser.pugx.org/paragonie/constant_time_encoding/license)](https://packagist.org/packages/paragonie/constant_time_encoding)
[![Downloads](https://img.shields.io/packagist/dt/paragonie/constant_time_encoding.svg)](https://packagist.org/packages/paragonie/constant_time_encoding)
Based on the [constant-time base64 implementation made by Steve "Sc00bz" Thomas](https://github.com/Sc00bz/ConstTimeEncoding),
this library aims to offer character encoding functions that do not leak
information about what you are encoding/decoding via processor cache
misses. Further reading on [cache-timing attacks](http://blog.ircmaxell.com/2014/11/its-all-about-time.html).
Our fork offers the following enhancements:
* `mbstring.func_overload` resistance
* Unit tests
* Composer- and Packagist-ready
* Base16 encoding
* Base32 encoding
* Uses `pack()` and `unpack()` instead of `chr()` and `ord()`
## PHP Version Requirements
Version 3 of this library should work on **PHP 8** or newer.
Version 2 of this library should work on **PHP 7** or newer. See [the v2.x branch](https://github.com/paragonie/constant_time_encoding/tree/v2.x).
For PHP 5 support, see [the v1.x branch](https://github.com/paragonie/constant_time_encoding/tree/v1.x).
If you are adding this as a dependency to a project intended to work on PHP 5 through 8.4, please set the required version to `^1|^2|^3`.
## How to Install
```sh
composer require paragonie/constant_time_encoding
```
## How to Use
```php
use ParagonIE\ConstantTime\Encoding;
// possibly (if applicable):
// require 'vendor/autoload.php';
$data = random_bytes(32);
echo Encoding::base64Encode($data), "\n";
echo Encoding::base32EncodeUpper($data), "\n";
echo Encoding::base32Encode($data), "\n";
echo Encoding::hexEncode($data), "\n";
echo Encoding::hexEncodeUpper($data), "\n";
```
Example output:
```
1VilPkeVqirlPifk5scbzcTTbMT2clp+Zkyv9VFFasE=
2VMKKPSHSWVCVZJ6E7SONRY3ZXCNG3GE6ZZFU7TGJSX7KUKFNLAQ====
2vmkkpshswvcvzj6e7sonry3zxcng3ge6zzfu7tgjsx7kukfnlaq====
d558a53e4795aa2ae53e27e4e6c71bcdc4d36cc4f6725a7e664caff551456ac1
D558A53E4795AA2AE53E27E4E6C71BDCC4D36CC4F6725A7E664CAFF551456AC1
```
If you only need a particular variant, you can just reference the
required class like so:
```php
use ParagonIE\ConstantTime\Base64;
use ParagonIE\ConstantTime\Base32;
$data = random_bytes(32);
echo Base64::encode($data), "\n";
echo Base32::encode($data), "\n";
```
Example output:
```
1VilPkeVqirlPifk5scbzcTTbMT2clp+Zkyv9VFFasE=
2vmkkpshswvcvzj6e7sonry3zxcng3ge6zzfu7tgjsx7kukfnlaq====
```
## Support Contracts
If your company uses this library in their products or services, you may be
interested in [purchasing a support contract from Paragon Initiative Enterprises](https://paragonie.com/enterprise).

View File

@@ -0,0 +1,56 @@
{
"name": "paragonie/constant_time_encoding",
"description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)",
"keywords": [
"base64",
"encoding",
"rfc4648",
"base32",
"base16",
"hex",
"bin2hex",
"hex2bin",
"base64_encode",
"base64_decode",
"base32_encode",
"base32_decode"
],
"license": "MIT",
"type": "library",
"authors": [
{
"name": "Paragon Initiative Enterprises",
"email": "security@paragonie.com",
"homepage": "https://paragonie.com",
"role": "Maintainer"
},
{
"name": "Steve 'Sc00bz' Thomas",
"email": "steve@tobtu.com",
"homepage": "https://www.tobtu.com",
"role": "Original Developer"
}
],
"support": {
"issues": "https://github.com/paragonie/constant_time_encoding/issues",
"email": "info@paragonie.com",
"source": "https://github.com/paragonie/constant_time_encoding"
},
"require": {
"php": "^8"
},
"require-dev": {
"phpunit/phpunit": "^9",
"vimeo/psalm": "^4|^5"
},
"autoload": {
"psr-4": {
"ParagonIE\\ConstantTime\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"ParagonIE\\ConstantTime\\Tests\\": "tests/"
}
}
}

View File

@@ -0,0 +1,541 @@
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;
use InvalidArgumentException;
use RangeException;
use TypeError;
/**
* Copyright (c) 2016 - 2022 Paragon Initiative Enterprises.
* Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* Class Base32
* [A-Z][2-7]
*
* @package ParagonIE\ConstantTime
*/
abstract class Base32 implements EncoderInterface
{
/**
* Decode a Base32-encoded string into raw binary
*
* @param string $encodedString
* @param bool $strictPadding
* @return string
*/
public static function decode(
#[\SensitiveParameter]
string $encodedString,
bool $strictPadding = false
): string {
return static::doDecode($encodedString, false, $strictPadding);
}
/**
* Decode an uppercase Base32-encoded string into raw binary
*
* @param string $src
* @param bool $strictPadding
* @return string
*/
public static function decodeUpper(
#[\SensitiveParameter]
string $src,
bool $strictPadding = false
): string {
return static::doDecode($src, true, $strictPadding);
}
/**
* Encode into Base32 (RFC 4648)
*
* @param string $binString
* @return string
* @throws TypeError
*/
public static function encode(
#[\SensitiveParameter]
string $binString
): string {
return static::doEncode($binString, false, true);
}
/**
* Encode into Base32 (RFC 4648)
*
* @param string $src
* @return string
* @throws TypeError
*/
public static function encodeUnpadded(
#[\SensitiveParameter]
string $src
): string {
return static::doEncode($src, false, false);
}
/**
* Encode into uppercase Base32 (RFC 4648)
*
* @param string $src
* @return string
* @throws TypeError
*/
public static function encodeUpper(
#[\SensitiveParameter]
string $src
): string {
return static::doEncode($src, true, true);
}
/**
* Encode into uppercase Base32 (RFC 4648)
*
* @param string $src
* @return string
* @throws TypeError
*/
public static function encodeUpperUnpadded(
#[\SensitiveParameter]
string $src
): string {
return static::doEncode($src, true, false);
}
/**
* Uses bitwise operators instead of table-lookups to turn 5-bit integers
* into 8-bit integers.
*
* @param int $src
* @return int
*/
protected static function decode5Bits(int $src): int
{
$ret = -1;
// if ($src > 96 && $src < 123) $ret += $src - 97 + 1; // -64
$ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 96);
// if ($src > 0x31 && $src < 0x38) $ret += $src - 24 + 1; // -23
$ret += (((0x31 - $src) & ($src - 0x38)) >> 8) & ($src - 23);
return $ret;
}
/**
* Uses bitwise operators instead of table-lookups to turn 5-bit integers
* into 8-bit integers.
*
* Uppercase variant.
*
* @param int $src
* @return int
*/
protected static function decode5BitsUpper(int $src): int
{
$ret = -1;
// if ($src > 64 && $src < 91) $ret += $src - 65 + 1; // -64
$ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 64);
// if ($src > 0x31 && $src < 0x38) $ret += $src - 24 + 1; // -23
$ret += (((0x31 - $src) & ($src - 0x38)) >> 8) & ($src - 23);
return $ret;
}
/**
* Uses bitwise operators instead of table-lookups to turn 8-bit integers
* into 5-bit integers.
*
* @param int $src
* @return string
*/
protected static function encode5Bits(int $src): string
{
$diff = 0x61;
// if ($src > 25) $ret -= 72;
$diff -= ((25 - $src) >> 8) & 73;
return \pack('C', $src + $diff);
}
/**
* Uses bitwise operators instead of table-lookups to turn 8-bit integers
* into 5-bit integers.
*
* Uppercase variant.
*
* @param int $src
* @return string
*/
protected static function encode5BitsUpper(int $src): string
{
$diff = 0x41;
// if ($src > 25) $ret -= 40;
$diff -= ((25 - $src) >> 8) & 41;
return \pack('C', $src + $diff);
}
/**
* @param string $encodedString
* @param bool $upper
* @return string
*/
public static function decodeNoPadding(
#[\SensitiveParameter]
string $encodedString,
bool $upper = false
): string {
$srcLen = Binary::safeStrlen($encodedString);
if ($srcLen === 0) {
return '';
}
if (($srcLen & 7) === 0) {
for ($j = 0; $j < 7 && $j < $srcLen; ++$j) {
if ($encodedString[$srcLen - $j - 1] === '=') {
throw new InvalidArgumentException(
"decodeNoPadding() doesn't tolerate padding"
);
}
}
}
return static::doDecode(
$encodedString,
$upper,
true
);
}
/**
* Base32 decoding
*
* @param string $src
* @param bool $upper
* @param bool $strictPadding
* @return string
*
* @throws TypeError
*/
protected static function doDecode(
#[\SensitiveParameter]
string $src,
bool $upper = false,
bool $strictPadding = false
): string {
// We do this to reduce code duplication:
$method = $upper
? 'decode5BitsUpper'
: 'decode5Bits';
// Remove padding
$srcLen = Binary::safeStrlen($src);
if ($srcLen === 0) {
return '';
}
if ($strictPadding) {
if (($srcLen & 7) === 0) {
for ($j = 0; $j < 7; ++$j) {
if ($src[$srcLen - 1] === '=') {
$srcLen--;
} else {
break;
}
}
}
if (($srcLen & 7) === 1) {
throw new RangeException(
'Incorrect padding'
);
}
} else {
$src = \rtrim($src, '=');
$srcLen = Binary::safeStrlen($src);
}
$err = 0;
$dest = '';
// Main loop (no padding):
for ($i = 0; $i + 8 <= $srcLen; $i += 8) {
/** @var array<int, int> $chunk */
$chunk = \unpack('C*', Binary::safeSubstr($src, $i, 8));
/** @var int $c0 */
$c0 = static::$method($chunk[1]);
/** @var int $c1 */
$c1 = static::$method($chunk[2]);
/** @var int $c2 */
$c2 = static::$method($chunk[3]);
/** @var int $c3 */
$c3 = static::$method($chunk[4]);
/** @var int $c4 */
$c4 = static::$method($chunk[5]);
/** @var int $c5 */
$c5 = static::$method($chunk[6]);
/** @var int $c6 */
$c6 = static::$method($chunk[7]);
/** @var int $c7 */
$c7 = static::$method($chunk[8]);
$dest .= \pack(
'CCCCC',
(($c0 << 3) | ($c1 >> 2) ) & 0xff,
(($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff,
(($c3 << 4) | ($c4 >> 1) ) & 0xff,
(($c4 << 7) | ($c5 << 2) | ($c6 >> 3)) & 0xff,
(($c6 << 5) | ($c7 ) ) & 0xff
);
$err |= ($c0 | $c1 | $c2 | $c3 | $c4 | $c5 | $c6 | $c7) >> 8;
}
// The last chunk, which may have padding:
if ($i < $srcLen) {
/** @var array<int, int> $chunk */
$chunk = \unpack('C*', Binary::safeSubstr($src, $i, $srcLen - $i));
/** @var int $c0 */
$c0 = static::$method($chunk[1]);
if ($i + 6 < $srcLen) {
/** @var int $c1 */
$c1 = static::$method($chunk[2]);
/** @var int $c2 */
$c2 = static::$method($chunk[3]);
/** @var int $c3 */
$c3 = static::$method($chunk[4]);
/** @var int $c4 */
$c4 = static::$method($chunk[5]);
/** @var int $c5 */
$c5 = static::$method($chunk[6]);
/** @var int $c6 */
$c6 = static::$method($chunk[7]);
$dest .= \pack(
'CCCC',
(($c0 << 3) | ($c1 >> 2) ) & 0xff,
(($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff,
(($c3 << 4) | ($c4 >> 1) ) & 0xff,
(($c4 << 7) | ($c5 << 2) | ($c6 >> 3)) & 0xff
);
$err |= ($c0 | $c1 | $c2 | $c3 | $c4 | $c5 | $c6) >> 8;
if ($strictPadding) {
$err |= ($c6 << 5) & 0xff;
}
} elseif ($i + 5 < $srcLen) {
/** @var int $c1 */
$c1 = static::$method($chunk[2]);
/** @var int $c2 */
$c2 = static::$method($chunk[3]);
/** @var int $c3 */
$c3 = static::$method($chunk[4]);
/** @var int $c4 */
$c4 = static::$method($chunk[5]);
/** @var int $c5 */
$c5 = static::$method($chunk[6]);
$dest .= \pack(
'CCCC',
(($c0 << 3) | ($c1 >> 2) ) & 0xff,
(($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff,
(($c3 << 4) | ($c4 >> 1) ) & 0xff,
(($c4 << 7) | ($c5 << 2) ) & 0xff
);
$err |= ($c0 | $c1 | $c2 | $c3 | $c4 | $c5) >> 8;
} elseif ($i + 4 < $srcLen) {
/** @var int $c1 */
$c1 = static::$method($chunk[2]);
/** @var int $c2 */
$c2 = static::$method($chunk[3]);
/** @var int $c3 */
$c3 = static::$method($chunk[4]);
/** @var int $c4 */
$c4 = static::$method($chunk[5]);
$dest .= \pack(
'CCC',
(($c0 << 3) | ($c1 >> 2) ) & 0xff,
(($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff,
(($c3 << 4) | ($c4 >> 1) ) & 0xff
);
$err |= ($c0 | $c1 | $c2 | $c3 | $c4) >> 8;
if ($strictPadding) {
$err |= ($c4 << 7) & 0xff;
}
} elseif ($i + 3 < $srcLen) {
/** @var int $c1 */
$c1 = static::$method($chunk[2]);
/** @var int $c2 */
$c2 = static::$method($chunk[3]);
/** @var int $c3 */
$c3 = static::$method($chunk[4]);
$dest .= \pack(
'CC',
(($c0 << 3) | ($c1 >> 2) ) & 0xff,
(($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff
);
$err |= ($c0 | $c1 | $c2 | $c3) >> 8;
if ($strictPadding) {
$err |= ($c3 << 4) & 0xff;
}
} elseif ($i + 2 < $srcLen) {
/** @var int $c1 */
$c1 = static::$method($chunk[2]);
/** @var int $c2 */
$c2 = static::$method($chunk[3]);
$dest .= \pack(
'CC',
(($c0 << 3) | ($c1 >> 2) ) & 0xff,
(($c1 << 6) | ($c2 << 1) ) & 0xff
);
$err |= ($c0 | $c1 | $c2) >> 8;
if ($strictPadding) {
$err |= ($c2 << 6) & 0xff;
}
} elseif ($i + 1 < $srcLen) {
/** @var int $c1 */
$c1 = static::$method($chunk[2]);
$dest .= \pack(
'C',
(($c0 << 3) | ($c1 >> 2) ) & 0xff
);
$err |= ($c0 | $c1) >> 8;
if ($strictPadding) {
$err |= ($c1 << 6) & 0xff;
}
} else {
$dest .= \pack(
'C',
(($c0 << 3) ) & 0xff
);
$err |= ($c0) >> 8;
}
}
$check = ($err === 0);
if (!$check) {
throw new RangeException(
'Base32::doDecode() only expects characters in the correct base32 alphabet'
);
}
return $dest;
}
/**
* Base32 Encoding
*
* @param string $src
* @param bool $upper
* @param bool $pad
* @return string
* @throws TypeError
*/
protected static function doEncode(
#[\SensitiveParameter]
string $src,
bool $upper = false,
$pad = true
): string {
// We do this to reduce code duplication:
$method = $upper
? 'encode5BitsUpper'
: 'encode5Bits';
$dest = '';
$srcLen = Binary::safeStrlen($src);
// Main loop (no padding):
for ($i = 0; $i + 5 <= $srcLen; $i += 5) {
/** @var array<int, int> $chunk */
$chunk = \unpack('C*', Binary::safeSubstr($src, $i, 5));
$b0 = $chunk[1];
$b1 = $chunk[2];
$b2 = $chunk[3];
$b3 = $chunk[4];
$b4 = $chunk[5];
$dest .=
static::$method( ($b0 >> 3) & 31) .
static::$method((($b0 << 2) | ($b1 >> 6)) & 31) .
static::$method((($b1 >> 1) ) & 31) .
static::$method((($b1 << 4) | ($b2 >> 4)) & 31) .
static::$method((($b2 << 1) | ($b3 >> 7)) & 31) .
static::$method((($b3 >> 2) ) & 31) .
static::$method((($b3 << 3) | ($b4 >> 5)) & 31) .
static::$method( $b4 & 31);
}
// The last chunk, which may have padding:
if ($i < $srcLen) {
/** @var array<int, int> $chunk */
$chunk = \unpack('C*', Binary::safeSubstr($src, $i, $srcLen - $i));
$b0 = $chunk[1];
if ($i + 3 < $srcLen) {
$b1 = $chunk[2];
$b2 = $chunk[3];
$b3 = $chunk[4];
$dest .=
static::$method( ($b0 >> 3) & 31) .
static::$method((($b0 << 2) | ($b1 >> 6)) & 31) .
static::$method((($b1 >> 1) ) & 31) .
static::$method((($b1 << 4) | ($b2 >> 4)) & 31) .
static::$method((($b2 << 1) | ($b3 >> 7)) & 31) .
static::$method((($b3 >> 2) ) & 31) .
static::$method((($b3 << 3) ) & 31);
if ($pad) {
$dest .= '=';
}
} elseif ($i + 2 < $srcLen) {
$b1 = $chunk[2];
$b2 = $chunk[3];
$dest .=
static::$method( ($b0 >> 3) & 31) .
static::$method((($b0 << 2) | ($b1 >> 6)) & 31) .
static::$method((($b1 >> 1) ) & 31) .
static::$method((($b1 << 4) | ($b2 >> 4)) & 31) .
static::$method((($b2 << 1) ) & 31);
if ($pad) {
$dest .= '===';
}
} elseif ($i + 1 < $srcLen) {
$b1 = $chunk[2];
$dest .=
static::$method( ($b0 >> 3) & 31) .
static::$method((($b0 << 2) | ($b1 >> 6)) & 31) .
static::$method((($b1 >> 1) ) & 31) .
static::$method((($b1 << 4) ) & 31);
if ($pad) {
$dest .= '====';
}
} else {
$dest .=
static::$method( ($b0 >> 3) & 31) .
static::$method( ($b0 << 2) & 31);
if ($pad) {
$dest .= '======';
}
}
}
return $dest;
}
}

View File

@@ -0,0 +1,111 @@
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;
/**
* Copyright (c) 2016 - 2022 Paragon Initiative Enterprises.
* Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* Class Base32Hex
* [0-9][A-V]
*
* @package ParagonIE\ConstantTime
*/
abstract class Base32Hex extends Base32
{
/**
* Uses bitwise operators instead of table-lookups to turn 5-bit integers
* into 8-bit integers.
*
* @param int $src
* @return int
*/
protected static function decode5Bits(int $src): int
{
$ret = -1;
// if ($src > 0x30 && $src < 0x3a) ret += $src - 0x2e + 1; // -47
$ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src - 47);
// if ($src > 0x60 && $src < 0x77) ret += $src - 0x61 + 10 + 1; // -86
$ret += (((0x60 - $src) & ($src - 0x77)) >> 8) & ($src - 86);
return $ret;
}
/**
* Uses bitwise operators instead of table-lookups to turn 5-bit integers
* into 8-bit integers.
*
* @param int $src
* @return int
*/
protected static function decode5BitsUpper(int $src): int
{
$ret = -1;
// if ($src > 0x30 && $src < 0x3a) ret += $src - 0x2e + 1; // -47
$ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src - 47);
// if ($src > 0x40 && $src < 0x57) ret += $src - 0x41 + 10 + 1; // -54
$ret += (((0x40 - $src) & ($src - 0x57)) >> 8) & ($src - 54);
return $ret;
}
/**
* Uses bitwise operators instead of table-lookups to turn 8-bit integers
* into 5-bit integers.
*
* @param int $src
* @return string
*/
protected static function encode5Bits(int $src): string
{
$src += 0x30;
// if ($src > 0x39) $src += 0x61 - 0x3a; // 39
$src += ((0x39 - $src) >> 8) & 39;
return \pack('C', $src);
}
/**
* Uses bitwise operators instead of table-lookups to turn 8-bit integers
* into 5-bit integers.
*
* Uppercase variant.
*
* @param int $src
* @return string
*/
protected static function encode5BitsUpper(int $src): string
{
$src += 0x30;
// if ($src > 0x39) $src += 0x41 - 0x3a; // 7
$src += ((0x39 - $src) >> 8) & 7;
return \pack('C', $src);
}
}

View File

@@ -0,0 +1,319 @@
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;
use InvalidArgumentException;
use RangeException;
use TypeError;
/**
* Copyright (c) 2016 - 2022 Paragon Initiative Enterprises.
* Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* Class Base64
* [A-Z][a-z][0-9]+/
*
* @package ParagonIE\ConstantTime
*/
abstract class Base64 implements EncoderInterface
{
/**
* Encode into Base64
*
* Base64 character set "[A-Z][a-z][0-9]+/"
*
* @param string $binString
* @return string
*
* @throws TypeError
*/
public static function encode(
#[\SensitiveParameter]
string $binString
): string {
return static::doEncode($binString, true);
}
/**
* Encode into Base64, no = padding
*
* Base64 character set "[A-Z][a-z][0-9]+/"
*
* @param string $src
* @return string
*
* @throws TypeError
*/
public static function encodeUnpadded(
#[\SensitiveParameter]
string $src
): string {
return static::doEncode($src, false);
}
/**
* @param string $src
* @param bool $pad Include = padding?
* @return string
*
* @throws TypeError
*/
protected static function doEncode(
#[\SensitiveParameter]
string $src,
bool $pad = true
): string {
$dest = '';
$srcLen = Binary::safeStrlen($src);
// Main loop (no padding):
for ($i = 0; $i + 3 <= $srcLen; $i += 3) {
/** @var array<int, int> $chunk */
$chunk = \unpack('C*', Binary::safeSubstr($src, $i, 3));
$b0 = $chunk[1];
$b1 = $chunk[2];
$b2 = $chunk[3];
$dest .=
static::encode6Bits( $b0 >> 2 ) .
static::encode6Bits((($b0 << 4) | ($b1 >> 4)) & 63) .
static::encode6Bits((($b1 << 2) | ($b2 >> 6)) & 63) .
static::encode6Bits( $b2 & 63);
}
// The last chunk, which may have padding:
if ($i < $srcLen) {
/** @var array<int, int> $chunk */
$chunk = \unpack('C*', Binary::safeSubstr($src, $i, $srcLen - $i));
$b0 = $chunk[1];
if ($i + 1 < $srcLen) {
$b1 = $chunk[2];
$dest .=
static::encode6Bits($b0 >> 2) .
static::encode6Bits((($b0 << 4) | ($b1 >> 4)) & 63) .
static::encode6Bits(($b1 << 2) & 63);
if ($pad) {
$dest .= '=';
}
} else {
$dest .=
static::encode6Bits( $b0 >> 2) .
static::encode6Bits(($b0 << 4) & 63);
if ($pad) {
$dest .= '==';
}
}
}
return $dest;
}
/**
* decode from base64 into binary
*
* Base64 character set "./[A-Z][a-z][0-9]"
*
* @param string $encodedString
* @param bool $strictPadding
* @return string
*
* @throws RangeException
* @throws TypeError
*/
public static function decode(
#[\SensitiveParameter]
string $encodedString,
bool $strictPadding = false
): string {
// Remove padding
$srcLen = Binary::safeStrlen($encodedString);
if ($srcLen === 0) {
return '';
}
if ($strictPadding) {
if (($srcLen & 3) === 0) {
if ($encodedString[$srcLen - 1] === '=') {
$srcLen--;
if ($encodedString[$srcLen - 1] === '=') {
$srcLen--;
}
}
}
if (($srcLen & 3) === 1) {
throw new RangeException(
'Incorrect padding'
);
}
if ($encodedString[$srcLen - 1] === '=') {
throw new RangeException(
'Incorrect padding'
);
}
} else {
$encodedString = \rtrim($encodedString, '=');
$srcLen = Binary::safeStrlen($encodedString);
}
$err = 0;
$dest = '';
// Main loop (no padding):
for ($i = 0; $i + 4 <= $srcLen; $i += 4) {
/** @var array<int, int> $chunk */
$chunk = \unpack('C*', Binary::safeSubstr($encodedString, $i, 4));
$c0 = static::decode6Bits($chunk[1]);
$c1 = static::decode6Bits($chunk[2]);
$c2 = static::decode6Bits($chunk[3]);
$c3 = static::decode6Bits($chunk[4]);
$dest .= \pack(
'CCC',
((($c0 << 2) | ($c1 >> 4)) & 0xff),
((($c1 << 4) | ($c2 >> 2)) & 0xff),
((($c2 << 6) | $c3 ) & 0xff)
);
$err |= ($c0 | $c1 | $c2 | $c3) >> 8;
}
// The last chunk, which may have padding:
if ($i < $srcLen) {
/** @var array<int, int> $chunk */
$chunk = \unpack('C*', Binary::safeSubstr($encodedString, $i, $srcLen - $i));
$c0 = static::decode6Bits($chunk[1]);
if ($i + 2 < $srcLen) {
$c1 = static::decode6Bits($chunk[2]);
$c2 = static::decode6Bits($chunk[3]);
$dest .= \pack(
'CC',
((($c0 << 2) | ($c1 >> 4)) & 0xff),
((($c1 << 4) | ($c2 >> 2)) & 0xff)
);
$err |= ($c0 | $c1 | $c2) >> 8;
if ($strictPadding) {
$err |= ($c2 << 6) & 0xff;
}
} elseif ($i + 1 < $srcLen) {
$c1 = static::decode6Bits($chunk[2]);
$dest .= \pack(
'C',
((($c0 << 2) | ($c1 >> 4)) & 0xff)
);
$err |= ($c0 | $c1) >> 8;
if ($strictPadding) {
$err |= ($c1 << 4) & 0xff;
}
} elseif ($strictPadding) {
$err |= 1;
}
}
$check = ($err === 0);
if (!$check) {
throw new RangeException(
'Base64::decode() only expects characters in the correct base64 alphabet'
);
}
return $dest;
}
/**
* @param string $encodedString
* @return string
*/
public static function decodeNoPadding(
#[\SensitiveParameter]
string $encodedString
): string {
$srcLen = Binary::safeStrlen($encodedString);
if ($srcLen === 0) {
return '';
}
if (($srcLen & 3) === 0) {
// If $strLen is not zero, and it is divisible by 4, then it's at least 4.
if ($encodedString[$srcLen - 1] === '=' || $encodedString[$srcLen - 2] === '=') {
throw new InvalidArgumentException(
"decodeNoPadding() doesn't tolerate padding"
);
}
}
return static::decode(
$encodedString,
true
);
}
/**
* Uses bitwise operators instead of table-lookups to turn 6-bit integers
* into 8-bit integers.
*
* Base64 character set:
* [A-Z] [a-z] [0-9] + /
* 0x41-0x5a, 0x61-0x7a, 0x30-0x39, 0x2b, 0x2f
*
* @param int $src
* @return int
*/
protected static function decode6Bits(int $src): int
{
$ret = -1;
// if ($src > 0x40 && $src < 0x5b) $ret += $src - 0x41 + 1; // -64
$ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 64);
// if ($src > 0x60 && $src < 0x7b) $ret += $src - 0x61 + 26 + 1; // -70
$ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 70);
// if ($src > 0x2f && $src < 0x3a) $ret += $src - 0x30 + 52 + 1; // 5
$ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src + 5);
// if ($src == 0x2b) $ret += 62 + 1;
$ret += (((0x2a - $src) & ($src - 0x2c)) >> 8) & 63;
// if ($src == 0x2f) ret += 63 + 1;
$ret += (((0x2e - $src) & ($src - 0x30)) >> 8) & 64;
return $ret;
}
/**
* Uses bitwise operators instead of table-lookups to turn 8-bit integers
* into 6-bit integers.
*
* @param int $src
* @return string
*/
protected static function encode6Bits(int $src): string
{
$diff = 0x41;
// if ($src > 25) $diff += 0x61 - 0x41 - 26; // 6
$diff += ((25 - $src) >> 8) & 6;
// if ($src > 51) $diff += 0x30 - 0x61 - 26; // -75
$diff -= ((51 - $src) >> 8) & 75;
// if ($src > 61) $diff += 0x2b - 0x30 - 10; // -15
$diff -= ((61 - $src) >> 8) & 15;
// if ($src > 62) $diff += 0x2f - 0x2b - 1; // 3
$diff += ((62 - $src) >> 8) & 3;
return \pack('C', $src + $diff);
}
}

View File

@@ -0,0 +1,88 @@
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;
/**
* Copyright (c) 2016 - 2022 Paragon Initiative Enterprises.
* Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* Class Base64DotSlash
* ./[A-Z][a-z][0-9]
*
* @package ParagonIE\ConstantTime
*/
abstract class Base64DotSlash extends Base64
{
/**
* Uses bitwise operators instead of table-lookups to turn 6-bit integers
* into 8-bit integers.
*
* Base64 character set:
* ./ [A-Z] [a-z] [0-9]
* 0x2e-0x2f, 0x41-0x5a, 0x61-0x7a, 0x30-0x39
*
* @param int $src
* @return int
*/
protected static function decode6Bits(int $src): int
{
$ret = -1;
// if ($src > 0x2d && $src < 0x30) ret += $src - 0x2e + 1; // -45
$ret += (((0x2d - $src) & ($src - 0x30)) >> 8) & ($src - 45);
// if ($src > 0x40 && $src < 0x5b) ret += $src - 0x41 + 2 + 1; // -62
$ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 62);
// if ($src > 0x60 && $src < 0x7b) ret += $src - 0x61 + 28 + 1; // -68
$ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 68);
// if ($src > 0x2f && $src < 0x3a) ret += $src - 0x30 + 54 + 1; // 7
$ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src + 7);
return $ret;
}
/**
* Uses bitwise operators instead of table-lookups to turn 8-bit integers
* into 6-bit integers.
*
* @param int $src
* @return string
*/
protected static function encode6Bits(int $src): string
{
$src += 0x2e;
// if ($src > 0x2f) $src += 0x41 - 0x30; // 17
$src += ((0x2f - $src) >> 8) & 17;
// if ($src > 0x5a) $src += 0x61 - 0x5b; // 6
$src += ((0x5a - $src) >> 8) & 6;
// if ($src > 0x7a) $src += 0x30 - 0x7b; // -75
$src -= ((0x7a - $src) >> 8) & 75;
return \pack('C', $src);
}
}

View File

@@ -0,0 +1,82 @@
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;
/**
* Copyright (c) 2016 - 2022 Paragon Initiative Enterprises.
* Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* Class Base64DotSlashOrdered
* ./[0-9][A-Z][a-z]
*
* @package ParagonIE\ConstantTime
*/
abstract class Base64DotSlashOrdered extends Base64
{
/**
* Uses bitwise operators instead of table-lookups to turn 6-bit integers
* into 8-bit integers.
*
* Base64 character set:
* [.-9] [A-Z] [a-z]
* 0x2e-0x39, 0x41-0x5a, 0x61-0x7a
*
* @param int $src
* @return int
*/
protected static function decode6Bits(int $src): int
{
$ret = -1;
// if ($src > 0x2d && $src < 0x3a) ret += $src - 0x2e + 1; // -45
$ret += (((0x2d - $src) & ($src - 0x3a)) >> 8) & ($src - 45);
// if ($src > 0x40 && $src < 0x5b) ret += $src - 0x41 + 12 + 1; // -52
$ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 52);
// if ($src > 0x60 && $src < 0x7b) ret += $src - 0x61 + 38 + 1; // -58
$ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 58);
return $ret;
}
/**
* Uses bitwise operators instead of table-lookups to turn 8-bit integers
* into 6-bit integers.
*
* @param int $src
* @return string
*/
protected static function encode6Bits(int $src): string
{
$src += 0x2e;
// if ($src > 0x39) $src += 0x41 - 0x3a; // 7
$src += ((0x39 - $src) >> 8) & 7;
// if ($src > 0x5a) $src += 0x61 - 0x5b; // 6
$src += ((0x5a - $src) >> 8) & 6;
return \pack('C', $src);
}
}

View File

@@ -0,0 +1,95 @@
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;
/**
* Copyright (c) 2016 - 2022 Paragon Initiative Enterprises.
* Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* Class Base64UrlSafe
* [A-Z][a-z][0-9]\-_
*
* @package ParagonIE\ConstantTime
*/
abstract class Base64UrlSafe extends Base64
{
/**
* Uses bitwise operators instead of table-lookups to turn 6-bit integers
* into 8-bit integers.
*
* Base64 character set:
* [A-Z] [a-z] [0-9] - _
* 0x41-0x5a, 0x61-0x7a, 0x30-0x39, 0x2d, 0x5f
*
* @param int $src
* @return int
*/
protected static function decode6Bits(int $src): int
{
$ret = -1;
// if ($src > 0x40 && $src < 0x5b) $ret += $src - 0x41 + 1; // -64
$ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 64);
// if ($src > 0x60 && $src < 0x7b) $ret += $src - 0x61 + 26 + 1; // -70
$ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 70);
// if ($src > 0x2f && $src < 0x3a) $ret += $src - 0x30 + 52 + 1; // 5
$ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src + 5);
// if ($src == 0x2c) $ret += 62 + 1;
$ret += (((0x2c - $src) & ($src - 0x2e)) >> 8) & 63;
// if ($src == 0x5f) ret += 63 + 1;
$ret += (((0x5e - $src) & ($src - 0x60)) >> 8) & 64;
return $ret;
}
/**
* Uses bitwise operators instead of table-lookups to turn 8-bit integers
* into 6-bit integers.
*
* @param int $src
* @return string
*/
protected static function encode6Bits(int $src): string
{
$diff = 0x41;
// if ($src > 25) $diff += 0x61 - 0x41 - 26; // 6
$diff += ((25 - $src) >> 8) & 6;
// if ($src > 51) $diff += 0x30 - 0x61 - 26; // -75
$diff -= ((51 - $src) >> 8) & 75;
// if ($src > 61) $diff += 0x2d - 0x30 - 10; // -13
$diff -= ((61 - $src) >> 8) & 13;
// if ($src > 62) $diff += 0x5f - 0x2b - 1; // 3
$diff += ((62 - $src) >> 8) & 49;
return \pack('C', $src + $diff);
}
}

View File

@@ -0,0 +1,93 @@
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;
use TypeError;
/**
* Copyright (c) 2016 - 2022 Paragon Initiative Enterprises.
* Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* Class Binary
*
* Binary string operators that don't choke on
* mbstring.func_overload
*
* @package ParagonIE\ConstantTime
*/
abstract class Binary
{
/**
* Safe string length
*
* @ref mbstring.func_overload
*
* @param string $str
* @return int
*/
public static function safeStrlen(
#[\SensitiveParameter]
string $str
): int {
if (\function_exists('mb_strlen')) {
// mb_strlen in PHP 7.x can return false.
/** @psalm-suppress RedundantCast */
return (int) \mb_strlen($str, '8bit');
} else {
return \strlen($str);
}
}
/**
* Safe substring
*
* @ref mbstring.func_overload
*
* @staticvar boolean $exists
* @param string $str
* @param int $start
* @param ?int $length
* @return string
*
* @throws TypeError
*/
public static function safeSubstr(
#[\SensitiveParameter]
string $str,
int $start = 0,
?int $length = null
): string {
if ($length === 0) {
return '';
}
if (\function_exists('mb_substr')) {
return \mb_substr($str, $start, $length, '8bit');
}
// Unlike mb_substr(), substr() doesn't accept NULL for length
if ($length !== null) {
return \substr($str, $start, $length);
} else {
return \substr($str, $start);
}
}
}

View File

@@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;
/**
* Copyright (c) 2016 - 2022 Paragon Initiative Enterprises.
* Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* Interface EncoderInterface
* @package ParagonIE\ConstantTime
*/
interface EncoderInterface
{
/**
* Convert a binary string into a hexadecimal string without cache-timing
* leaks
*
* @param string $binString (raw binary)
* @return string
*/
public static function encode(string $binString): string;
/**
* Convert a binary string into a hexadecimal string without cache-timing
* leaks
*
* @param string $encodedString
* @param bool $strictPadding Error on invalid padding
* @return string (raw binary)
*/
public static function decode(string $encodedString, bool $strictPadding = false): string;
}

View File

@@ -0,0 +1,298 @@
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;
use TypeError;
/**
* Copyright (c) 2016 - 2022 Paragon Initiative Enterprises.
* Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* Class Encoding
* @package ParagonIE\ConstantTime
*/
abstract class Encoding
{
/**
* RFC 4648 Base32 encoding
*
* @param string $str
* @return string
* @throws TypeError
*/
public static function base32Encode(
#[\SensitiveParameter]
string $str
): string {
return Base32::encode($str);
}
/**
* RFC 4648 Base32 encoding
*
* @param string $str
* @return string
* @throws TypeError
*/
public static function base32EncodeUpper(
#[\SensitiveParameter]
string $str
): string {
return Base32::encodeUpper($str);
}
/**
* RFC 4648 Base32 decoding
*
* @param string $str
* @return string
* @throws TypeError
*/
public static function base32Decode(
#[\SensitiveParameter]
string $str
): string {
return Base32::decode($str);
}
/**
* RFC 4648 Base32 decoding
*
* @param string $str
* @return string
* @throws TypeError
*/
public static function base32DecodeUpper(
#[\SensitiveParameter]
string $str
): string {
return Base32::decodeUpper($str);
}
/**
* RFC 4648 Base32 encoding
*
* @param string $str
* @return string
* @throws TypeError
*/
public static function base32HexEncode(
#[\SensitiveParameter]
string $str
): string {
return Base32Hex::encode($str);
}
/**
* RFC 4648 Base32Hex encoding
*
* @param string $str
* @return string
* @throws TypeError
*/
public static function base32HexEncodeUpper(
#[\SensitiveParameter]
string $str
): string {
return Base32Hex::encodeUpper($str);
}
/**
* RFC 4648 Base32Hex decoding
*
* @param string $str
* @return string
* @throws TypeError
*/
public static function base32HexDecode(
#[\SensitiveParameter]
string $str
): string {
return Base32Hex::decode($str);
}
/**
* RFC 4648 Base32Hex decoding
*
* @param string $str
* @return string
* @throws TypeError
*/
public static function base32HexDecodeUpper(
#[\SensitiveParameter]
string $str
): string {
return Base32Hex::decodeUpper($str);
}
/**
* RFC 4648 Base64 encoding
*
* @param string $str
* @return string
* @throws TypeError
*/
public static function base64Encode(
#[\SensitiveParameter]
string $str
): string {
return Base64::encode($str);
}
/**
* RFC 4648 Base64 decoding
*
* @param string $str
* @return string
* @throws TypeError
*/
public static function base64Decode(
#[\SensitiveParameter]
string $str
): string {
return Base64::decode($str);
}
/**
* Encode into Base64
*
* Base64 character set "./[A-Z][a-z][0-9]"
* @param string $str
* @return string
* @throws TypeError
*/
public static function base64EncodeDotSlash(
#[\SensitiveParameter]
string $str
): string {
return Base64DotSlash::encode($str);
}
/**
* Decode from base64 to raw binary
*
* Base64 character set "./[A-Z][a-z][0-9]"
*
* @param string $str
* @return string
* @throws \RangeException
* @throws TypeError
*/
public static function base64DecodeDotSlash(
#[\SensitiveParameter]
string $str
): string {
return Base64DotSlash::decode($str);
}
/**
* Encode into Base64
*
* Base64 character set "[.-9][A-Z][a-z]" or "./[0-9][A-Z][a-z]"
* @param string $str
* @return string
* @throws TypeError
*/
public static function base64EncodeDotSlashOrdered(
#[\SensitiveParameter]
string $str
): string {
return Base64DotSlashOrdered::encode($str);
}
/**
* Decode from base64 to raw binary
*
* Base64 character set "[.-9][A-Z][a-z]" or "./[0-9][A-Z][a-z]"
*
* @param string $str
* @return string
* @throws \RangeException
* @throws TypeError
*/
public static function base64DecodeDotSlashOrdered(
#[\SensitiveParameter]
string $str
): string {
return Base64DotSlashOrdered::decode($str);
}
/**
* Convert a binary string into a hexadecimal string without cache-timing
* leaks
*
* @param string $bin_string (raw binary)
* @return string
* @throws TypeError
*/
public static function hexEncode(
#[\SensitiveParameter]
string $bin_string
): string {
return Hex::encode($bin_string);
}
/**
* Convert a hexadecimal string into a binary string without cache-timing
* leaks
*
* @param string $hex_string
* @return string (raw binary)
* @throws \RangeException
*/
public static function hexDecode(
#[\SensitiveParameter]
string $hex_string
): string {
return Hex::decode($hex_string);
}
/**
* Convert a binary string into a hexadecimal string without cache-timing
* leaks
*
* @param string $bin_string (raw binary)
* @return string
* @throws TypeError
*/
public static function hexEncodeUpper(
#[\SensitiveParameter]
string $bin_string
): string {
return Hex::encodeUpper($bin_string);
}
/**
* Convert a binary string into a hexadecimal string without cache-timing
* leaks
*
* @param string $bin_string (raw binary)
* @return string
*/
public static function hexDecodeUpper(
#[\SensitiveParameter]
string $bin_string
): string {
return Hex::decode($bin_string);
}
}

View File

@@ -0,0 +1,151 @@
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;
use RangeException;
use TypeError;
/**
* Copyright (c) 2016 - 2022 Paragon Initiative Enterprises.
* Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* Class Hex
* @package ParagonIE\ConstantTime
*/
abstract class Hex implements EncoderInterface
{
/**
* Convert a binary string into a hexadecimal string without cache-timing
* leaks
*
* @param string $binString (raw binary)
* @return string
* @throws TypeError
*/
public static function encode(
#[\SensitiveParameter]
string $binString
): string {
$hex = '';
$len = Binary::safeStrlen($binString);
for ($i = 0; $i < $len; ++$i) {
/** @var array<int, int> $chunk */
$chunk = \unpack('C', $binString[$i]);
$c = $chunk[1] & 0xf;
$b = $chunk[1] >> 4;
$hex .= \pack(
'CC',
(87 + $b + ((($b - 10) >> 8) & ~38)),
(87 + $c + ((($c - 10) >> 8) & ~38))
);
}
return $hex;
}
/**
* Convert a binary string into a hexadecimal string without cache-timing
* leaks, returning uppercase letters (as per RFC 4648)
*
* @param string $binString (raw binary)
* @return string
* @throws TypeError
*/
public static function encodeUpper(
#[\SensitiveParameter]
string $binString
): string {
$hex = '';
$len = Binary::safeStrlen($binString);
for ($i = 0; $i < $len; ++$i) {
/** @var array<int, int> $chunk */
$chunk = \unpack('C', $binString[$i]);
$c = $chunk[1] & 0xf;
$b = $chunk[1] >> 4;
$hex .= \pack(
'CC',
(55 + $b + ((($b - 10) >> 8) & ~6)),
(55 + $c + ((($c - 10) >> 8) & ~6))
);
}
return $hex;
}
/**
* Convert a hexadecimal string into a binary string without cache-timing
* leaks
*
* @param string $encodedString
* @param bool $strictPadding
* @return string (raw binary)
* @throws RangeException
*/
public static function decode(
#[\SensitiveParameter]
string $encodedString,
bool $strictPadding = false
): string {
$hex_pos = 0;
$bin = '';
$c_acc = 0;
$hex_len = Binary::safeStrlen($encodedString);
$state = 0;
if (($hex_len & 1) !== 0) {
if ($strictPadding) {
throw new RangeException(
'Expected an even number of hexadecimal characters'
);
} else {
$encodedString = '0' . $encodedString;
++$hex_len;
}
}
/** @var array<int, int> $chunk */
$chunk = \unpack('C*', $encodedString);
while ($hex_pos < $hex_len) {
++$hex_pos;
$c = $chunk[$hex_pos];
$c_num = $c ^ 48;
$c_num0 = ($c_num - 10) >> 8;
$c_alpha = ($c & ~32) - 55;
$c_alpha0 = (($c_alpha - 10) ^ ($c_alpha - 16)) >> 8;
if (($c_num0 | $c_alpha0) === 0) {
throw new RangeException(
'Expected hexadecimal character'
);
}
$c_val = ($c_num0 & $c_num) | ($c_alpha & $c_alpha0);
if ($state === 0) {
$c_acc = $c_val * 16;
} else {
$bin .= \pack('C', $c_acc | $c_val);
}
$state ^= 1;
}
return $bin;
}
}

View File

@@ -0,0 +1,206 @@
<?php
declare(strict_types=1);
namespace ParagonIE\ConstantTime;
use TypeError;
/**
* Copyright (c) 2016 - 2022 Paragon Initiative Enterprises.
* Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/**
* Class RFC4648
*
* This class conforms strictly to the RFC
*
* @package ParagonIE\ConstantTime
*/
abstract class RFC4648
{
/**
* RFC 4648 Base64 encoding
*
* "foo" -> "Zm9v"
*
* @param string $str
* @return string
*
* @throws TypeError
*/
public static function base64Encode(
#[\SensitiveParameter]
string $str
): string {
return Base64::encode($str);
}
/**
* RFC 4648 Base64 decoding
*
* "Zm9v" -> "foo"
*
* @param string $str
* @return string
*
* @throws TypeError
*/
public static function base64Decode(
#[\SensitiveParameter]
string $str
): string {
return Base64::decode($str, true);
}
/**
* RFC 4648 Base64 (URL Safe) encoding
*
* "foo" -> "Zm9v"
*
* @param string $str
* @return string
*
* @throws TypeError
*/
public static function base64UrlSafeEncode(
#[\SensitiveParameter]
string $str
): string {
return Base64UrlSafe::encode($str);
}
/**
* RFC 4648 Base64 (URL Safe) decoding
*
* "Zm9v" -> "foo"
*
* @param string $str
* @return string
*
* @throws TypeError
*/
public static function base64UrlSafeDecode(
#[\SensitiveParameter]
string $str
): string {
return Base64UrlSafe::decode($str, true);
}
/**
* RFC 4648 Base32 encoding
*
* "foo" -> "MZXW6==="
*
* @param string $str
* @return string
*
* @throws TypeError
*/
public static function base32Encode(
#[\SensitiveParameter]
string $str
): string {
return Base32::encodeUpper($str);
}
/**
* RFC 4648 Base32 encoding
*
* "MZXW6===" -> "foo"
*
* @param string $str
* @return string
*
* @throws TypeError
*/
public static function base32Decode(
#[\SensitiveParameter]
string $str
): string {
return Base32::decodeUpper($str, true);
}
/**
* RFC 4648 Base32-Hex encoding
*
* "foo" -> "CPNMU==="
*
* @param string $str
* @return string
*
* @throws TypeError
*/
public static function base32HexEncode(
#[\SensitiveParameter]
string $str
): string {
return Base32::encodeUpper($str);
}
/**
* RFC 4648 Base32-Hex decoding
*
* "CPNMU===" -> "foo"
*
* @param string $str
* @return string
*
* @throws TypeError
*/
public static function base32HexDecode(
#[\SensitiveParameter]
string $str
): string {
return Base32::decodeUpper($str, true);
}
/**
* RFC 4648 Base16 decoding
*
* "foo" -> "666F6F"
*
* @param string $str
* @return string
*
* @throws TypeError
*/
public static function base16Encode(
#[\SensitiveParameter]
string $str
): string {
return Hex::encodeUpper($str);
}
/**
* RFC 4648 Base16 decoding
*
* "666F6F" -> "foo"
*
* @param string $str
* @return string
*/
public static function base16Decode(
#[\SensitiveParameter]
string $str
): string {
return Hex::decode($str, true);
}
}

View File

@@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 Paragon Initiative Enterprises
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,5 @@
#!/usr/bin/env bash
basedir=$( dirname $( readlink -f ${BASH_SOURCE[0]} ) )
php -dphar.readonly=0 "$basedir/other/build_phar.php" $*

View File

@@ -0,0 +1,34 @@
{
"name": "paragonie/random_compat",
"description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
"keywords": [
"csprng",
"random",
"polyfill",
"pseudorandom"
],
"license": "MIT",
"type": "library",
"authors": [
{
"name": "Paragon Initiative Enterprises",
"email": "security@paragonie.com",
"homepage": "https://paragonie.com"
}
],
"support": {
"issues": "https://github.com/paragonie/random_compat/issues",
"email": "info@paragonie.com",
"source": "https://github.com/paragonie/random_compat"
},
"require": {
"php": ">= 7"
},
"require-dev": {
"vimeo/psalm": "^1",
"phpunit/phpunit": "4.*|5.*"
},
"suggest": {
"ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
}
}

View File

@@ -0,0 +1,5 @@
-----BEGIN PUBLIC KEY-----
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEEd+wCqJDrx5B4OldM0dQE0ZMX+lx1ZWm
pui0SUqD4G29L3NGsz9UhJ/0HjBdbnkhIK5xviT0X5vtjacF6ajgcCArbTB+ds+p
+h7Q084NuSuIpNb6YPfoUFgC/CL9kAoc
-----END PUBLIC KEY-----

View File

@@ -0,0 +1,11 @@
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.22 (MingW32)
iQEcBAABAgAGBQJWtW1hAAoJEGuXocKCZATaJf0H+wbZGgskK1dcRTsuVJl9IWip
QwGw/qIKI280SD6/ckoUMxKDCJiFuPR14zmqnS36k7N5UNPnpdTJTS8T11jttSpg
1LCmgpbEIpgaTah+cELDqFCav99fS+bEiAL5lWDAHBTE/XPjGVCqeehyPYref4IW
NDBIEsvnHPHPLsn6X5jq4+Yj5oUixgxaMPiR+bcO4Sh+RzOVB6i2D0upWfRXBFXA
NNnsg9/zjvoC7ZW73y9uSH+dPJTt/Vgfeiv52/v41XliyzbUyLalf02GNPY+9goV
JHG1ulEEBJOCiUD9cE1PUIJwHA/HqyhHIvV350YoEFiHl8iSwm7SiZu5kPjaq74=
=B6+8
-----END PGP SIGNATURE-----

View File

@@ -0,0 +1,32 @@
<?php
/**
* Random_* Compatibility Library
* for using the new PHP 7 random_* API in PHP 5 projects
*
* @version 2.99.99
* @released 2018-06-06
*
* The MIT License (MIT)
*
* Copyright (c) 2015 - 2018 Paragon Initiative Enterprises
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// NOP

View File

@@ -0,0 +1,57 @@
<?php
$dist = dirname(__DIR__).'/dist';
if (!is_dir($dist)) {
mkdir($dist, 0755);
}
if (file_exists($dist.'/random_compat.phar')) {
unlink($dist.'/random_compat.phar');
}
$phar = new Phar(
$dist.'/random_compat.phar',
FilesystemIterator::CURRENT_AS_FILEINFO | \FilesystemIterator::KEY_AS_FILENAME,
'random_compat.phar'
);
rename(
dirname(__DIR__).'/lib/random.php',
dirname(__DIR__).'/lib/index.php'
);
$phar->buildFromDirectory(dirname(__DIR__).'/lib');
rename(
dirname(__DIR__).'/lib/index.php',
dirname(__DIR__).'/lib/random.php'
);
/**
* If we pass an (optional) path to a private key as a second argument, we will
* sign the Phar with OpenSSL.
*
* If you leave this out, it will produce an unsigned .phar!
*/
if ($argc > 1) {
if (!@is_readable($argv[1])) {
echo 'Could not read the private key file:', $argv[1], "\n";
exit(255);
}
$pkeyFile = file_get_contents($argv[1]);
$private = openssl_get_privatekey($pkeyFile);
if ($private !== false) {
$pkey = '';
openssl_pkey_export($private, $pkey);
$phar->setSignatureAlgorithm(Phar::OPENSSL, $pkey);
/**
* Save the corresponding public key to the file
*/
if (!@is_readable($dist.'/random_compat.phar.pubkey')) {
$details = openssl_pkey_get_details($private);
file_put_contents(
$dist.'/random_compat.phar.pubkey',
$details['key']
);
}
} else {
echo 'An error occurred reading the private key from OpenSSL.', "\n";
exit(255);
}
}

Some files were not shown because too many files have changed in this diff Show More