WebApp production ver
BIN
Documentation/Img/RAID0_SpeedTest-New.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
Documentation/Img/RAID0_SpeedTest.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
Documentation/Img/RAID1_SpeedTest-New.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
Documentation/Img/RAID1_SpeedTest.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
Documentation/Img/RAID5_SpeedTest-New.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
Documentation/Img/RAID5_SpeedTest.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
Documentation/Rocnikova prace Michal Sedlak.pdf
Normal file
BIN
Documentation/Technická dokumentace - RAID Pole.docx
Normal file
BIN
Documentation/Technická dokumentace - RAID Pole.pdf
Normal file
BIN
Documentation/USB RAID pole.pptx
Normal file
BIN
Documentation/matice-rizik.pdf
Normal file
BIN
Documentation/msedlak_itb4.pdf
Normal file
50
Documentation/usbraidlogin.sql
Normal 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 */;
|
||||
BIN
Documentation/Šablona MPO.docx
Normal file
2
LICENSE
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
5
Web/betatest/composer.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"require": {
|
||||
"phpseclib/phpseclib": "^3.0"
|
||||
}
|
||||
}
|
||||
246
Web/betatest/composer.lock
generated
Normal 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"
|
||||
}
|
||||
248
Web/betatest/content/adminpanel.php
Normal 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();
|
||||
?>
|
||||
157
Web/betatest/content/changepassword.php
Normal 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>
|
||||
52
Web/betatest/content/css/adminpanel.css
Normal 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;
|
||||
}
|
||||
56
Web/betatest/content/css/changepassword.css
Normal 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;
|
||||
}
|
||||
}
|
||||
35
Web/betatest/content/ftp/config.php
Normal 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);
|
||||
41
Web/betatest/content/ftp/createdir.php
Normal 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;
|
||||
68
Web/betatest/content/ftp/createfile.php
Normal 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;
|
||||
128
Web/betatest/content/ftp/css/index.css
Normal 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%;
|
||||
}
|
||||
54
Web/betatest/content/ftp/css/open.css
Normal 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;
|
||||
}
|
||||
253
Web/betatest/content/ftp/css/pdf.css
Normal 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;
|
||||
}
|
||||
124
Web/betatest/content/ftp/css/serverstat.css
Normal 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;
|
||||
}
|
||||
}
|
||||
86
Web/betatest/content/ftp/css/view.css
Normal 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%;
|
||||
}
|
||||
}
|
||||
39
Web/betatest/content/ftp/delete.php
Normal 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;
|
||||
217
Web/betatest/content/ftp/download.php
Normal 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;
|
||||
}
|
||||
?>
|
||||
244
Web/betatest/content/ftp/extract.php
Normal 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);
|
||||
}
|
||||
?>
|
||||
338
Web/betatest/content/ftp/index.php
Normal 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];
|
||||
}
|
||||
?>
|
||||
288
Web/betatest/content/ftp/js/index.js
Normal 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);
|
||||
});
|
||||
});
|
||||
204
Web/betatest/content/ftp/open.php
Normal 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>
|
||||
331
Web/betatest/content/ftp/pdf.php
Normal 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>
|
||||
48
Web/betatest/content/ftp/rename.php
Normal 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;
|
||||
}
|
||||
?>
|
||||
560
Web/betatest/content/ftp/serverstat.php
Normal 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>
|
||||
340
Web/betatest/content/ftp/shared.php
Normal 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>
|
||||
130
Web/betatest/content/ftp/upload.php
Normal 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;
|
||||
473
Web/betatest/content/ftp/view.php
Normal 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];
|
||||
}
|
||||
?>
|
||||
83
Web/betatest/content/login.php
Normal 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();
|
||||
}
|
||||
?>
|
||||
23
Web/betatest/content/logout.php
Normal 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
47
Web/betatest/css/index.css
Normal 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;
|
||||
}
|
||||
61
Web/betatest/css/preloader.css
Normal 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;
|
||||
}
|
||||
BIN
Web/betatest/img/GitHub_Logo.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
Web/betatest/img/GitHub_Logo_White.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
Web/betatest/img/favicon.ico
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
Web/betatest/img/freelo-logo-rgb-on-dark.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
Web/betatest/img/freelo-logo-rgb.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
79
Web/betatest/index.php
Normal 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
1
Web/betatest/js/bootstrap.bundle.js.map
Normal file
7
Web/betatest/js/bootstrap.bundle.min.js
vendored
Normal file
1
Web/betatest/js/bootstrap.bundle.min.js.map
Normal file
4447
Web/betatest/js/bootstrap.esm.js
vendored
Normal file
1
Web/betatest/js/bootstrap.esm.js.map
Normal file
7
Web/betatest/js/bootstrap.esm.min.js
vendored
Normal file
1
Web/betatest/js/bootstrap.esm.min.js.map
Normal file
4494
Web/betatest/js/bootstrap.js
vendored
Normal file
1
Web/betatest/js/bootstrap.js.map
Normal file
7
Web/betatest/js/bootstrap.min.js
vendored
Normal file
1
Web/betatest/js/bootstrap.min.js.map
Normal file
40
Web/betatest/js/theme.js
Normal 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
@@ -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();
|
||||
579
Web/betatest/vendor/composer/ClassLoader.php
vendored
Normal 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);
|
||||
}
|
||||
}
|
||||
359
Web/betatest/vendor/composer/InstalledVersions.php
vendored
Normal 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
@@ -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.
|
||||
10
Web/betatest/vendor/composer/autoload_classmap.php
vendored
Normal 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',
|
||||
);
|
||||
10
Web/betatest/vendor/composer/autoload_files.php
vendored
Normal 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',
|
||||
);
|
||||
9
Web/betatest/vendor/composer/autoload_namespaces.php
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
// autoload_namespaces.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
);
|
||||
11
Web/betatest/vendor/composer/autoload_psr4.php
vendored
Normal 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'),
|
||||
);
|
||||
50
Web/betatest/vendor/composer/autoload_real.php
vendored
Normal 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;
|
||||
}
|
||||
}
|
||||
48
Web/betatest/vendor/composer/autoload_static.php
vendored
Normal 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);
|
||||
}
|
||||
}
|
||||
242
Web/betatest/vendor/composer/installed.json
vendored
Normal 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": []
|
||||
}
|
||||
50
Web/betatest/vendor/composer/installed.php
vendored
Normal 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,
|
||||
),
|
||||
),
|
||||
);
|
||||
26
Web/betatest/vendor/composer/platform_check.php
vendored
Normal 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
|
||||
);
|
||||
}
|
||||
48
Web/betatest/vendor/paragonie/constant_time_encoding/LICENSE.txt
vendored
Normal 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.
|
||||
|
||||
88
Web/betatest/vendor/paragonie/constant_time_encoding/README.md
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
# Constant-Time Encoding
|
||||
|
||||
[](https://github.com/paragonie/constant_time_encoding/actions)
|
||||
[](https://github.com/paragonie/constant_time_encoding/actions)
|
||||
[](https://packagist.org/packages/paragonie/constant_time_encoding)
|
||||
[](https://packagist.org/packages/paragonie/constant_time_encoding)
|
||||
[](https://packagist.org/packages/paragonie/constant_time_encoding)
|
||||
[](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).
|
||||
56
Web/betatest/vendor/paragonie/constant_time_encoding/composer.json
vendored
Normal 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/"
|
||||
}
|
||||
}
|
||||
}
|
||||
541
Web/betatest/vendor/paragonie/constant_time_encoding/src/Base32.php
vendored
Normal 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;
|
||||
}
|
||||
}
|
||||
111
Web/betatest/vendor/paragonie/constant_time_encoding/src/Base32Hex.php
vendored
Normal 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);
|
||||
}
|
||||
}
|
||||
319
Web/betatest/vendor/paragonie/constant_time_encoding/src/Base64.php
vendored
Normal 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);
|
||||
}
|
||||
}
|
||||
88
Web/betatest/vendor/paragonie/constant_time_encoding/src/Base64DotSlash.php
vendored
Normal 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);
|
||||
}
|
||||
}
|
||||
82
Web/betatest/vendor/paragonie/constant_time_encoding/src/Base64DotSlashOrdered.php
vendored
Normal 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);
|
||||
}
|
||||
}
|
||||
95
Web/betatest/vendor/paragonie/constant_time_encoding/src/Base64UrlSafe.php
vendored
Normal 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);
|
||||
}
|
||||
}
|
||||
93
Web/betatest/vendor/paragonie/constant_time_encoding/src/Binary.php
vendored
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
52
Web/betatest/vendor/paragonie/constant_time_encoding/src/EncoderInterface.php
vendored
Normal 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;
|
||||
}
|
||||
298
Web/betatest/vendor/paragonie/constant_time_encoding/src/Encoding.php
vendored
Normal 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);
|
||||
}
|
||||
}
|
||||
151
Web/betatest/vendor/paragonie/constant_time_encoding/src/Hex.php
vendored
Normal 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;
|
||||
}
|
||||
}
|
||||
206
Web/betatest/vendor/paragonie/constant_time_encoding/src/RFC4648.php
vendored
Normal 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);
|
||||
}
|
||||
}
|
||||
22
Web/betatest/vendor/paragonie/random_compat/LICENSE
vendored
Normal 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.
|
||||
|
||||
5
Web/betatest/vendor/paragonie/random_compat/build-phar.sh
vendored
Normal 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" $*
|
||||
34
Web/betatest/vendor/paragonie/random_compat/composer.json
vendored
Normal 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."
|
||||
}
|
||||
}
|
||||
5
Web/betatest/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEEd+wCqJDrx5B4OldM0dQE0ZMX+lx1ZWm
|
||||
pui0SUqD4G29L3NGsz9UhJ/0HjBdbnkhIK5xviT0X5vtjacF6ajgcCArbTB+ds+p
|
||||
+h7Q084NuSuIpNb6YPfoUFgC/CL9kAoc
|
||||
-----END PUBLIC KEY-----
|
||||
11
Web/betatest/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey.asc
vendored
Normal 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-----
|
||||
32
Web/betatest/vendor/paragonie/random_compat/lib/random.php
vendored
Normal 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
|
||||
57
Web/betatest/vendor/paragonie/random_compat/other/build_phar.php
vendored
Normal 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);
|
||||
}
|
||||
}
|
||||