Begin RHPZ Server.

This commit is contained in:
2026-01-29 18:43:58 +01:00
commit b49424b6ee
25 changed files with 1772 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
.env
.idea
node_modules
vendor

18
composer.json Normal file
View File

@@ -0,0 +1,18 @@
{
"name": "romhackplaza/file-server",
"type": "project",
"authors": [
{
"name": "Benjamin",
"email": "benjamin@cartememoire.org"
}
],
"autoload": {
"psr-4": {
"RomhackPlazaFS\\": "src/"
}
},
"require": {
"vlucas/phpdotenv": "^5.6"
}
}

492
composer.lock generated Normal file
View File

@@ -0,0 +1,492 @@
{
"_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": "66c99d67287c11e948f7517be5bc239c",
"packages": [
{
"name": "graham-campbell/result-type",
"version": "v1.1.4",
"source": {
"type": "git",
"url": "https://github.com/GrahamCampbell/Result-Type.git",
"reference": "e01f4a821471308ba86aa202fed6698b6b695e3b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/e01f4a821471308ba86aa202fed6698b6b695e3b",
"reference": "e01f4a821471308ba86aa202fed6698b6b695e3b",
"shasum": ""
},
"require": {
"php": "^7.2.5 || ^8.0",
"phpoption/phpoption": "^1.9.5"
},
"require-dev": {
"phpunit/phpunit": "^8.5.41 || ^9.6.22 || ^10.5.45 || ^11.5.7"
},
"type": "library",
"autoload": {
"psr-4": {
"GrahamCampbell\\ResultType\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Graham Campbell",
"email": "hello@gjcampbell.co.uk",
"homepage": "https://github.com/GrahamCampbell"
}
],
"description": "An Implementation Of The Result Type",
"keywords": [
"Graham Campbell",
"GrahamCampbell",
"Result Type",
"Result-Type",
"result"
],
"support": {
"issues": "https://github.com/GrahamCampbell/Result-Type/issues",
"source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.4"
},
"funding": [
{
"url": "https://github.com/GrahamCampbell",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type",
"type": "tidelift"
}
],
"time": "2025-12-27T19:43:20+00:00"
},
{
"name": "phpoption/phpoption",
"version": "1.9.5",
"source": {
"type": "git",
"url": "https://github.com/schmittjoh/php-option.git",
"reference": "75365b91986c2405cf5e1e012c5595cd487a98be"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/schmittjoh/php-option/zipball/75365b91986c2405cf5e1e012c5595cd487a98be",
"reference": "75365b91986c2405cf5e1e012c5595cd487a98be",
"shasum": ""
},
"require": {
"php": "^7.2.5 || ^8.0"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8.2",
"phpunit/phpunit": "^8.5.44 || ^9.6.25 || ^10.5.53 || ^11.5.34"
},
"type": "library",
"extra": {
"bamarni-bin": {
"bin-links": true,
"forward-command": false
},
"branch-alias": {
"dev-master": "1.9-dev"
}
},
"autoload": {
"psr-4": {
"PhpOption\\": "src/PhpOption/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "Johannes M. Schmitt",
"email": "schmittjoh@gmail.com",
"homepage": "https://github.com/schmittjoh"
},
{
"name": "Graham Campbell",
"email": "hello@gjcampbell.co.uk",
"homepage": "https://github.com/GrahamCampbell"
}
],
"description": "Option Type for PHP",
"keywords": [
"language",
"option",
"php",
"type"
],
"support": {
"issues": "https://github.com/schmittjoh/php-option/issues",
"source": "https://github.com/schmittjoh/php-option/tree/1.9.5"
},
"funding": [
{
"url": "https://github.com/GrahamCampbell",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption",
"type": "tidelift"
}
],
"time": "2025-12-27T19:41:33+00:00"
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.33.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638",
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638",
"shasum": ""
},
"require": {
"php": ">=7.2"
},
"provide": {
"ext-ctype": "*"
},
"suggest": {
"ext-ctype": "For best performance"
},
"type": "library",
"extra": {
"thanks": {
"url": "https://github.com/symfony/polyfill",
"name": "symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Ctype\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Gert de Pagter",
"email": "BackEndTea@gmail.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for ctype functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"ctype",
"polyfill",
"portable"
],
"support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-09-09T11:45:10+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.33.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493",
"reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493",
"shasum": ""
},
"require": {
"ext-iconv": "*",
"php": ">=7.2"
},
"provide": {
"ext-mbstring": "*"
},
"suggest": {
"ext-mbstring": "For best performance"
},
"type": "library",
"extra": {
"thanks": {
"url": "https://github.com/symfony/polyfill",
"name": "symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Mbstring\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for the Mbstring extension",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"mbstring",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-12-23T08:48:59+00:00"
},
{
"name": "symfony/polyfill-php80",
"version": "v1.33.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
"reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
"reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
"shasum": ""
},
"require": {
"php": ">=7.2"
},
"type": "library",
"extra": {
"thanks": {
"url": "https://github.com/symfony/polyfill",
"name": "symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Php80\\": ""
},
"classmap": [
"Resources/stubs"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Ion Bazan",
"email": "ion.bazan@gmail.com"
},
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2025-01-02T08:10:11+00:00"
},
{
"name": "vlucas/phpdotenv",
"version": "v5.6.3",
"source": {
"type": "git",
"url": "https://github.com/vlucas/phpdotenv.git",
"reference": "955e7815d677a3eaa7075231212f2110983adecc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/955e7815d677a3eaa7075231212f2110983adecc",
"reference": "955e7815d677a3eaa7075231212f2110983adecc",
"shasum": ""
},
"require": {
"ext-pcre": "*",
"graham-campbell/result-type": "^1.1.4",
"php": "^7.2.5 || ^8.0",
"phpoption/phpoption": "^1.9.5",
"symfony/polyfill-ctype": "^1.26",
"symfony/polyfill-mbstring": "^1.26",
"symfony/polyfill-php80": "^1.26"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8.2",
"ext-filter": "*",
"phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2"
},
"suggest": {
"ext-filter": "Required to use the boolean validator."
},
"type": "library",
"extra": {
"bamarni-bin": {
"bin-links": true,
"forward-command": false
},
"branch-alias": {
"dev-master": "5.6-dev"
}
},
"autoload": {
"psr-4": {
"Dotenv\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Graham Campbell",
"email": "hello@gjcampbell.co.uk",
"homepage": "https://github.com/GrahamCampbell"
},
{
"name": "Vance Lucas",
"email": "vance@vancelucas.com",
"homepage": "https://github.com/vlucas"
}
],
"description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.",
"keywords": [
"dotenv",
"env",
"environment"
],
"support": {
"issues": "https://github.com/vlucas/phpdotenv/issues",
"source": "https://github.com/vlucas/phpdotenv/tree/v5.6.3"
},
"funding": [
{
"url": "https://github.com/GrahamCampbell",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv",
"type": "tidelift"
}
],
"time": "2025-12-27T19:49:13+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {},
"platform-dev": {},
"plugin-api-version": "2.9.0"
}

View File

@@ -0,0 +1 @@
fv

4
public/index.php Normal file
View File

@@ -0,0 +1,4 @@
<?php
define( 'ROMHACKPLAZA_FS', true );
require_once __DIR__ . "/../src/bootstrap.php";

View File

@@ -0,0 +1,59 @@
<?php
namespace RomhackPlazaFS\Endpoints;
use RomhackPlazaFS\Exceptions\InvalidTokenException;
use RomhackPlazaFS\Exceptions\TokenExpiredException;
use RomhackPlazaFS\Exceptions\WrongServerException;
use RomhackPlazaFS\RomhackPlazaFS;
defined( 'ROMHACKPLAZA_FS' ) || exit;
abstract class Abstract_Endpoint {
use \RomhackPlazaFS\SecurityTrait;
use \RomhackPlazaFS\SwifferTrait;
protected RomhackPlazaFS $fs;
protected ?array $token_info = null;
public function exec( RomhackPlazaFS &$FS ) {
$this->fs = &$FS;
if( $this->private_endpoint() )
RomhackPlazaFS::allow_only_for_main_site();
if( $this->require_token() )
$this->check_token();
$this->get_post_params();
$this->process();
}
private function check_token( string $token_name = "zeus" ){
$token = $this->fs->give_token( $token_name );
if( $token === "" )
RomhackPlazaFS::fatal_stop( 400, "Zeus is dead... Really sad..." );
try{
$this->token_info = $this->verify_token( $token ) ?? null;
} catch ( InvalidTokenException $e ){
RomhackPlazaFS::fatal_stop( 400, "You can't reach Zeus, sad..." );
} catch ( TokenExpiredException $e ){
RomhackPlazaFS::fatal_stop( 400, "Zeus is not here anymore, sad...");
} catch ( WrongServerException $e ){
RomhackPlazaFS::fatal_stop( 400, "Why you want to reach Zeus ?");
}
if( $this->token_info['action'] !== $this->fs->endpoint_requested )
RomhackPlazaFS::fatal_stop( 400, "There are not two Zeus..." );
}
abstract protected function private_endpoint(): bool;
abstract protected function require_token(): bool;
abstract protected function get_post_params(): void;
abstract protected function process(): void;
}

View File

@@ -0,0 +1,58 @@
<?php
namespace RomhackPlazaFS\Endpoints;
use RomhackPlazaFS\Library\Data;
use RomhackPlazaFS\RomhackPlazaFS;
defined( 'ROMHACKPLAZA_FS' ) || exit;
class Checkexistence extends Abstract_Endpoint {
private int $post_id;
private string $custom_post_type;
private string $file_name;
protected function private_endpoint(): bool
{
return false;
}
protected function require_token(): bool
{
return true;
}
protected function get_post_params(): void {
$this->post_id = $this->swiffer_post( "post_id", "int" );
if( $this->post_id < 1 )
RomhackPlazaFS::invalid_parameter_stop( "post_id", "POST" );
$this->custom_post_type = $this->swiffer_post( "post_type", "string" );
if( $this->custom_post_type === "" )
RomhackPlazaFS::invalid_parameter_stop( "post_type", "POST" );
$this->file_name = $this->swiffer_post( "filename", "filename" );
if( $this->file_name === "" )
RomhackPlazaFS::invalid_parameter_stop( "filename", "POST" );
}
protected function process(): void {
$file_exists = Data::setup(
$this->custom_post_type,
$this->post_id,
$this->file_name
)->file_exists();
$resp = [
'file_exists' => $file_exists,
];
RomhackPlazaFS::success_response( $resp );
}
}

View File

@@ -0,0 +1,66 @@
<?php
namespace RomhackPlazaFS\Endpoints;
use RomhackPlazaFS\Library\Data;
use RomhackPlazaFS\RomhackPlazaFS;
defined( 'ROMHACKPLAZA_FS' ) || exit;
class Deletefile extends Abstract_Endpoint {
private int $post_id;
private string $custom_post_type;
private string $file_name;
protected function private_endpoint(): bool
{
return false;
}
protected function require_token(): bool
{
return false;
}
protected function get_post_params(): void {
$this->post_id = $this->swiffer_post( "post_id", "int" );
if( $this->post_id < 1 )
RomhackPlazaFS::invalid_parameter_stop( "post_id", "POST" );
$this->custom_post_type = $this->swiffer_post( "post_type", "string" );
if( $this->custom_post_type === "" )
RomhackPlazaFS::invalid_parameter_stop( "post_type", "POST" );
$this->file_name = $this->swiffer_post( "filename", "filename" );
if( $this->file_name === "" )
RomhackPlazaFS::invalid_parameter_stop( "filename", "POST" );
}
protected function process(): void {
$data = Data::setup(
$this->custom_post_type,
$this->post_id,
$this->file_name,
);
if( !$data->file_exists() )
RomhackPlazaFS::fatal_stop( 404, "File not found." );
$new_data = Data::setup(
$this->custom_post_type,
$this->post_id,
$this->file_name,
Data::F_DELETED_FILE
);
if( $data->change_data_location( $new_data ) )
RomhackPlazaFS::success_response( [ 'status' => Data::STATUS_DELETED ] );
RomhackPlazaFS::fatal_stop( 500, "Error during file marking" );
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace RomhackPlazaFS\Endpoints;
use RomhackPlazaFS\Library\Data;
use RomhackPlazaFS\RomhackPlazaFS;
defined( 'ROMHACKPLAZA_FS' ) || exit;
class Downloadfilelist extends Abstract_Endpoint {
private int $post_id;
private string $custom_post_type;
protected function private_endpoint(): bool
{
return false;
}
protected function require_token(): bool
{
return false;
}
protected function get_post_params(): void {
$this->post_id = $this->swiffer_post( "post_id", "int" );
if( $this->post_id < 1 )
RomhackPlazaFS::invalid_parameter_stop( "post_id", "POST" );
$this->custom_post_type = $this->swiffer_post( "post_type", "string" );
if( $this->custom_post_type === "" )
RomhackPlazaFS::invalid_parameter_stop( "post_type", "POST" );
}
protected function process(): void {
$data = Data::setup(
$this->custom_post_type,
$this->post_id
);
$archived = Data::setup(
$this->custom_post_type,
$this->post_id,
flags: Data::F_ARCHIVED_FILE
);
$response = [];
$response['files'] = $data->list_files( $this->custom_post_type, $this->post_id );
$response['archived'] = $archived->list_files( $this->custom_post_type, $this->post_id );
RomhackPlazaFS::success_response( $response );
}
}

View File

@@ -0,0 +1,62 @@
<?php
namespace RomhackPlazaFS\Endpoints;
use RomhackPlazaFS\Library\Data;
use RomhackPlazaFS\RomhackPlazaFS;
defined( 'ROMHACKPLAZA_FS' ) || exit;
class Editorfilelist extends Abstract_Endpoint {
private int $post_id;
private string $custom_post_type;
protected function private_endpoint(): bool
{
return false;
}
protected function require_token(): bool
{
return false;
}
protected function get_post_params(): void {
$this->post_id = $this->swiffer_post( "post_id", "int" );
if( $this->post_id < 1 )
RomhackPlazaFS::invalid_parameter_stop( "post_id", "POST" );
$this->custom_post_type = $this->swiffer_post( "post_type", "string" );
if( $this->custom_post_type === "" )
RomhackPlazaFS::invalid_parameter_stop( "post_type", "POST" );
}
protected function process(): void {
$data = Data::setup(
$this->custom_post_type,
$this->post_id
);
$private = Data::setup(
$this->custom_post_type,
$this->post_id,
flags: Data::F_PRIVATE_FILE
);
$archived = Data::setup(
$this->custom_post_type,
$this->post_id,
flags: Data::F_ARCHIVED_FILE
);
$response = [];
$response['files'] = $data->list_files( $this->custom_post_type, $this->post_id );
$response['private'] = $private->list_files( $this->custom_post_type, $this->post_id );
$response['archived'] = $archived->list_files( $this->custom_post_type, $this->post_id );
RomhackPlazaFS::success_response( $response );
}
}

View File

@@ -0,0 +1,79 @@
<?php
namespace RomhackPlazaFS\Endpoints;
use RomhackPlazaFS\Library\Data;
use RomhackPlazaFS\RomhackPlazaFS;
defined( 'ROMHACKPLAZA_FS' ) || exit;
class Fileexplorer extends Abstract_Endpoint {
private int $post_id;
private string $custom_post_type;
private string $file_name;
private string $is_private;
private string $is_archived;
protected function private_endpoint(): bool
{
return false;
}
protected function require_token(): bool
{
return false;
}
protected function get_post_params(): void {
$this->post_id = $this->swiffer_post( "post_id", "int" );
if( $this->post_id < 1 )
RomhackPlazaFS::invalid_parameter_stop( "post_id", "POST" );
$this->custom_post_type = $this->swiffer_post( "post_type", "string" );
if( $this->custom_post_type === "" )
RomhackPlazaFS::invalid_parameter_stop( "post_type", "POST" );
$this->file_name = $this->swiffer_post( "filename", "filename" );
if( $this->file_name === "" )
RomhackPlazaFS::invalid_parameter_stop( "filename", "POST" );
$this->is_private = $this->swiffer_post( "is_private", "string" );
if( $this->is_private !== "no" && $this->is_private !== "yes" )
$this->is_private = "no";
$this->is_archived = $this->swiffer_post( "is_archived", "string" );
if( $this->is_archived !== "no" && $this->is_archived !== "yes" )
$this->is_archived = "no";
}
protected function process(): void {
$flags = 0;
if( $this->is_private === "yes" )
$flags |= Data::F_PRIVATE_FILE;
else if( $this->is_archived === "yes" )
$flags |= Data::F_ARCHIVED_FILE;
$data = Data::setup(
$this->custom_post_type,
$this->post_id,
$this->file_name,
$flags
);
if( !$data->file_exists() )
RomhackPlazaFS::fatal_stop( 404, "File not found." );
$response = [];
$response['files'] = $data->list_files( $this->custom_post_type, $this->post_id );
$response['private'] = $private->list_files( $this->custom_post_type, $this->post_id );
$response['archived'] = $archived->list_files( $this->custom_post_type, $this->post_id );
RomhackPlazaFS::success_response( $response );
}
}

View File

@@ -0,0 +1,48 @@
<?php
namespace RomhackPlazaFS\Endpoints;
use RomhackPlazaFS\RomhackPlazaFS;
defined( 'ROMHACKPLAZA_FS' ) || exit;
class Generatetestkey extends Abstract_Endpoint {
private string $action;
protected function private_endpoint(): bool
{
return false;
}
protected function require_token(): bool
{
return false;
}
protected function get_post_params(): void {
$this->action = $this->swiffer_get( "want_action", "string" );
if( $this->action === "" )
RomhackPlazaFS::fatal_stop( 400, "Invalid want_action GET parameter." );
}
protected function process(): void {
$info = [
'user_id' => 1,
'to' => $_ENV['ROMHACKPLAZAFS_URL'],
'action' => $this->action,
'generated_at' => time(),
'expires_at' => time() + 900,
'romhackplaza' => \bin2hex( random_bytes( 16 ) ),
];
$json = json_encode( $info );
$sig = hash_hmac( 'sha256', $json, $_ENV['ROMHACKPLAZAFS_SECRET_KEY'] );
$end = base64_encode( $json ) . "|" . $sig;
RomhackPlazaFS::success_response( [ 'zeus' => $end ] );
}
}

View File

@@ -0,0 +1,70 @@
<?php
namespace RomhackPlazaFS\Endpoints;
use RomhackPlazaFS\Library\Data;
use RomhackPlazaFS\RomhackPlazaFS;
defined( 'ROMHACKPLAZA_FS' ) || exit;
class Markasarchived extends Abstract_Endpoint {
private int $post_id;
private string $custom_post_type;
private string $file_name;
protected function private_endpoint(): bool
{
return false;
}
protected function require_token(): bool
{
return false;
}
protected function get_post_params(): void {
$this->post_id = $this->swiffer_post( "post_id", "int" );
if( $this->post_id < 1 )
RomhackPlazaFS::invalid_parameter_stop( "post_id", "POST" );
$this->custom_post_type = $this->swiffer_post( "post_type", "string" );
if( $this->custom_post_type === "" )
RomhackPlazaFS::invalid_parameter_stop( "post_type", "POST" );
$this->file_name = $this->swiffer_post( "filename", "filename" );
if( $this->file_name === "" )
RomhackPlazaFS::invalid_parameter_stop( "filename", "POST" );
}
protected function process(): void {
$data = Data::setup(
$this->custom_post_type,
$this->post_id,
$this->file_name,
);
if( !$data->file_exists() )
RomhackPlazaFS::fatal_stop( 404, "File not found." );
$new_data = Data::setup(
$this->custom_post_type,
$this->post_id,
$this->file_name,
Data::F_ARCHIVED_FILE
);
if( $new_data->file_exists() )
RomhackPlazaFS::fatal_stop( 500, "File already exists. You can't archive that have the same name than an other archived file." );
if( $data->change_data_location( $new_data ) )
RomhackPlazaFS::success_response( [ 'status' => Data::STATUS_ARCHIVED ] );
RomhackPlazaFS::fatal_stop( 500, "Error during file marking" );
}
}

View File

@@ -0,0 +1,70 @@
<?php
namespace RomhackPlazaFS\Endpoints;
use RomhackPlazaFS\Library\Data;
use RomhackPlazaFS\RomhackPlazaFS;
defined( 'ROMHACKPLAZA_FS' ) || exit;
class Markasprivate extends Abstract_Endpoint {
private int $post_id;
private string $custom_post_type;
private string $file_name;
protected function private_endpoint(): bool
{
return false;
}
protected function require_token(): bool
{
return false;
}
protected function get_post_params(): void {
$this->post_id = $this->swiffer_post( "post_id", "int" );
if( $this->post_id < 1 )
RomhackPlazaFS::invalid_parameter_stop( "post_id", "POST" );
$this->custom_post_type = $this->swiffer_post( "post_type", "string" );
if( $this->custom_post_type === "" )
RomhackPlazaFS::invalid_parameter_stop( "post_type", "POST" );
$this->file_name = $this->swiffer_post( "filename", "filename" );
if( $this->file_name === "" )
RomhackPlazaFS::invalid_parameter_stop( "filename", "POST" );
}
protected function process(): void {
$data = Data::setup(
$this->custom_post_type,
$this->post_id,
$this->file_name
);
if( !$data->file_exists() )
RomhackPlazaFS::fatal_stop( 404, "File not found." );
$new_data = Data::setup(
$this->custom_post_type,
$this->post_id,
$this->file_name,
Data::F_PRIVATE_FILE
);
if( $new_data->file_exists() )
RomhackPlazaFS::fatal_stop( 500, "File already exists. Can't replace. Please delete private file before." );
if( $data->change_data_location( $new_data ) )
RomhackPlazaFS::success_response( [ 'status' => Data::STATUS_PRIVATE ] );
RomhackPlazaFS::fatal_stop( 500, "Error during file marking" );
}
}

View File

@@ -0,0 +1,70 @@
<?php
namespace RomhackPlazaFS\Endpoints;
use RomhackPlazaFS\Library\Data;
use RomhackPlazaFS\RomhackPlazaFS;
defined( 'ROMHACKPLAZA_FS' ) || exit;
class Markaspublic extends Abstract_Endpoint {
private int $post_id;
private string $custom_post_type;
private string $file_name;
protected function private_endpoint(): bool
{
return false;
}
protected function require_token(): bool
{
return false;
}
protected function get_post_params(): void {
$this->post_id = $this->swiffer_post( "post_id", "int" );
if( $this->post_id < 1 )
RomhackPlazaFS::invalid_parameter_stop( "post_id", "POST" );
$this->custom_post_type = $this->swiffer_post( "post_type", "string" );
if( $this->custom_post_type === "" )
RomhackPlazaFS::invalid_parameter_stop( "post_type", "POST" );
$this->file_name = $this->swiffer_post( "filename", "filename" );
if( $this->file_name === "" )
RomhackPlazaFS::invalid_parameter_stop( "filename", "POST" );
}
protected function process(): void {
$data = Data::setup(
$this->custom_post_type,
$this->post_id,
$this->file_name,
Data::F_PRIVATE_FILE
);
if( !$data->file_exists() )
RomhackPlazaFS::fatal_stop( 404, "File not found." );
$new_data = Data::setup(
$this->custom_post_type,
$this->post_id,
$this->file_name
);
if( $new_data->file_exists() )
RomhackPlazaFS::fatal_stop( 500, "File already exists. Can't replace. Please delete published file before." );
if( $data->change_data_location( $new_data ) )
RomhackPlazaFS::success_response( [ 'status' => Data::STATUS_PUBLISH ] );
RomhackPlazaFS::fatal_stop( 500, "Error during file marking" );
}
}

View File

@@ -0,0 +1,66 @@
<?php
namespace RomhackPlazaFS\Endpoints;
use RomhackPlazaFS\Library\Data;
use RomhackPlazaFS\RomhackPlazaFS;
defined( 'ROMHACKPLAZA_FS' ) || exit;
class Uploadchunk extends Abstract_Endpoint {
private int $post_id;
private string $custom_post_type;
private string $file_name;
private int $current_chunk;
private int $total_chunks;
protected function private_endpoint(): bool
{
return false;
}
protected function require_token(): bool
{
return false;
}
protected function get_post_params(): void {
$this->post_id = $this->swiffer_post( "post_id", "int" );
if( $this->post_id < 1 )
RomhackPlazaFS::invalid_parameter_stop( "post_id", "POST" );
$this->custom_post_type = $this->swiffer_post( "post_type", "string" );
if( $this->custom_post_type === "" )
RomhackPlazaFS::invalid_parameter_stop( "post_type", "POST" );
$this->file_name = $this->swiffer_post( "filename", "filename" );
if( $this->file_name === "" )
RomhackPlazaFS::invalid_parameter_stop( "filename", "POST" );
$this->current_chunk = $this->swiffer_post( "current_chunk", "int" );
$this->total_chunks = $this->swiffer_post( "total_chunks", "int" );
if( $this->total_chunks < $this->current_chunk )
RomhackPlazaFS::invalid_parameter_stop( "current_chunk", "POST" );
}
protected function process(): void {
$data = Data::setup(
$this->custom_post_type,
$this->post_id,
$this->file_name
);
$data->file_path_to_temp();
if( $data->write( $this->current_chunk, $this->total_chunks ) )
RomhackPlazaFS::success_response( [ 'chunk' => $this->current_chunk, 'total_chunks' => $this->total_chunks, 'uploaded' => true ] );
RomhackPlazaFS::fatal_stop( 500, "Error during file writing" );
}
}

View File

@@ -0,0 +1,6 @@
<?php
namespace RomhackPlazaFS\Exceptions;
defined( 'ROMHACKPLAZA_FS' ) || exit;
class InvalidArgumentException extends \Exception {}

View File

@@ -0,0 +1,6 @@
<?php
namespace RomhackPlazaFS\Exceptions;
defined( 'ROMHACKPLAZA_FS' ) || exit;
class InvalidTokenException extends \Exception {}

View File

@@ -0,0 +1,6 @@
<?php
namespace RomhackPlazaFS\Exceptions;
defined( 'ROMHACKPLAZA_FS' ) || exit;
class TokenExpiredException extends \Exception {}

View File

@@ -0,0 +1,6 @@
<?php
namespace RomhackPlazaFS\Exceptions;
defined( 'ROMHACKPLAZA_FS' ) || exit;
class WrongServerException extends \Exception {}

269
src/Library/Data.php Normal file
View File

@@ -0,0 +1,269 @@
<?php
namespace RomhackPlazaFS\Library;
use RomhackPlazaFS\RomhackPlazaFS;
defined( 'ROMHACKPLAZA_FS' ) || exit;
final class Data {
const F_PRIVATE_FILE = 1 << 1;
const F_DELETED_FILE = 1 << 2;
const F_ARCHIVED_FILE = 1 << 3;
const F_TEMPORARY_FILE = 1 << 4;
// ...
const string STATUS_PUBLISH = "publish";
const string STATUS_PRIVATE = "private";
const string STATUS_ARCHIVED = "archived";
const string STATUS_DELETED = "deleted";
// ...
const string TRASH_SUBFOLDER = "trash/";
const string PRIVATE_SUBFOLDER = "private/";
const string ARCHIVE_SUBFOLDER = "archive/";
const int CHUNK_SIZE = 8192;
public private(set) string $folder_path;
public private(set) ?string $file_path;
public private(set) ?string $old_file_path;
public private(set) int $flags;
public static function setup(
string $custom_post_type,
int $post_id,
?string $file_name = null,
int $flags = 0
){
$folder = $_ENV['ROMHACKPLAZAFS_FILE_FILES_PATH'] . $custom_post_type . '/' . $post_id . '/';
if( $flags & self::F_PRIVATE_FILE )
$folder .= self::PRIVATE_SUBFOLDER;
else if( $flags & self::F_ARCHIVED_FILE )
$folder .= self::ARCHIVE_SUBFOLDER;
else if( $flags & self::F_DELETED_FILE )
$folder = $_ENV['ROMHACKPLAZAFS_FILE_PATH'] . self::TRASH_SUBFOLDER;
self::check_folder_exists( $folder );
if( $file_name !== null ){
$file = $folder . $file_name;
} else {
$file = null;
}
return new self( $folder, $file, $flags );
}
private static function check_folder_exists(string $folder_path){
if( !file_exists($folder_path) ){
if( !mkdir($folder_path, 0777, true) )
RomhackPlazaFS::fatal_stop( 500, "Could not create directory." );
else {
self::change_folder_permissions( $folder_path );
}
}
}
private static function check_folder_permissions(string $folder_path){
$folder_permission = substr(
sprintf('%o', fileperms($folder_path)), -4
);
if( $folder_permission != "0777" && $folder_permission != "0755" )
self::change_folder_permissions( $folder_path );
}
private static function change_folder_permissions(string $folder_path)
{
try {
chmod($folder_path, 0755);
} catch ( \Exception $e ) {
// Ignore.
}
}
public function __construct(
string $folder_path,
?string $file_path,
int $flags = 0
){
$this->folder_path = $folder_path;
$this->file_path = $file_path;
$this->old_file_path = $file_path;
$this->flags = $flags;
}
public function file_path_to_temp(): void {
$this->old_file_path = $this->file_path;
$this->file_path .= '.part';
$this->flags |= self::F_TEMPORARY_FILE;
}
public function file_path_to_real(): void {
$this->file_path = $this->old_file_path;
$this->flags &= ~self::F_TEMPORARY_FILE;
}
public function file_exists(): bool {
if( $this->file_path === null )
return false;
return file_exists($this->file_path);
}
public function size(): int {
if( !$this->file_exists() )
return 0;
return filesize($this->file_path);
}
public function pretty_size(): string {
$bytes = $this->size();
if ($bytes >= 1073741824) {
$bytes = number_format($bytes / 1073741824, 2) . ' GB';
} elseif ($bytes >= 1048576) {
$bytes = number_format($bytes / 1048576, 2) . ' MB';
} elseif ($bytes >= 1024) {
$bytes = number_format($bytes / 1024, 2) . ' KB';
} else {
$bytes = ($bytes == 0) ? '0 bytes' : $bytes . ' bytes';
}
return $bytes;
}
public function status(): string
{
if( !$this->file_exists() )
return self::STATUS_DELETED;
if( $this->flags & self::F_PRIVATE_FILE )
return self::STATUS_PRIVATE;
if( $this->flags & self::F_ARCHIVED_FILE )
return self::ARCHIVE_SUBFOLDER;
return self::STATUS_PUBLISH;
}
public function write( int $current_chunk, int $total_chunks ): bool {
if( $this->file_path === null )
RomhackPlazaFS::fatal_stop( 500, "Filepath is not registred.");
if( $this->flags & !self::F_TEMPORARY_FILE )
RomhackPlazaFS::fatal_stop( 500, "Filepath is not temporary.");
if( !isset( $_FILES['file'] ) )
RomhackPlazaFS::invalid_parameter_stop( "file" );
$in = fopen($_FILES['file']['tmp_name'], 'rb');
if( !$in )
RomhackPlazaFS::fatal_stop( 500, "Could not open input file." );
$out = fopen( $this->file_path, 'ab' );
if( !$out )
RomhackPlazaFS::fatal_stop( 500, "Could not open output file." );
while( $chunk = fread( $in, self::CHUNK_SIZE ) )
fwrite( $out, $chunk );
fclose( $in );
fclose( $out );
$this->rename_if_final_chunk( $current_chunk, $total_chunks );
return true;
}
private function rename_if_final_chunk( int $current_chunk, int $total_chunks )
{
if( $this->file_path === null || $this->old_file_path === null )
RomhackPlazaFS::fatal_stop( 500, "Filepath is not registred.");
if( $current_chunk + 1 === $total_chunks )
rename( $this->file_path, $this->old_file_path );
}
public function change_data_location( Data &$new_data_location ): bool {
if( $this->file_path === null || $new_data_location->file_path === null )
RomhackPlazaFS::fatal_stop( 500, "Filepath is not registred.");
if( !rename( $this->file_path, $new_data_location->file_path ) )
RomhackPlazaFS::fatal_stop( 500, "The file hasn't been moved.");
$this->file_path = $new_data_location->file_path;
return true;
}
public function can_be_explored(): bool {
if( $this->file_path === null )
return false;
return in_array( mime_content_type( $this->file_path ), [ 'application/zip', 'application/x-rar', 'application/x-7z-compressed' ] );
}
public function can_be_patched(): bool {
if( $this->file_path === null )
return false;
$file_arr = explode('.', $this->file_path);
$file_ext = end($file_arr );
return mime_content_type( $this->file_path ) === 'application/zip' || in_array( $file_ext, RomhackPlazaFS::dotenv_array( "ROMHACKPLAZAFS_PATCHER_ALLOWED_EXT" ) );
}
private function _return_list_files( array $files_array, string $custom_post_type, int $post_id ){
$return_files = array_map( function( $file_name ) use ( $custom_post_type, $post_id ){
$download_url = RomhackPlazaFS::build_url( 'Download', [ 'filename' => $file_name, 'post_type' => $custom_post_type, 'entry_id' => $post_id ] );
$file = new self( $this->folder_path, $this->folder_path . $file_name, $this->flags );
return [
'name' => $file_name,
'size' => $file->size(),
'pretty_size' => $file->pretty_size(),
'status' => $file->status(),
'download' => $download_url,
'explorer' => $file->can_be_explored() ? RomhackPlazaFS::build_url( "FileExplorer", [ 'filename' => urlencode( $file_name ), 'post_type' => $custom_post_type, 'entry_id' => $post_id ] ) : null,
'patcher' => $file->can_be_patched() ? RomhackPlazaFS::build_patcher_url( $download_url, $post_id ) : null,
];
}, array_values( $files_array ) );
return $return_files;
}
public function list_files( string $custom_post_type = "", int $post_id = 0 ): array {
if( !\file_exists( $this->folder_path ) )
return [];
$files = array_diff( scandir( $this->folder_path ), [ '.', '..', rtrim( self::PRIVATE_SUBFOLDER, '/' ), rtrim( self::ARCHIVE_SUBFOLDER, '/' ) ] );
return $this->_return_list_files( $files, $custom_post_type, $post_id );
}
}

121
src/RomhackPlazaFS.php Normal file
View File

@@ -0,0 +1,121 @@
<?php
namespace RomhackPlazaFS;
use RomhackPlazaFS\Endpoints\Abstract_Endpoint;
defined( 'ROMHACKPLAZA_FS' ) || exit;
final class RomhackPlazaFS {
use SwifferTrait;
final public private(set) bool $is_index;
public private(set) string $endpoint_requested;
final public static bool $response_type_defined = false;
public static function allow_only_for_main_site(){
header( "Access-Control-Allow-Origin: " . $_ENV['ROMHACKPLAZAFS_MAIN_URL'] );
}
public static function response_type( string $type ){
if(!self::$response_type_defined){
header( "Content-Type: $type" );
self::$response_type_defined = true;
}
}
public static function response( bool $success, int $html_code, array $fields = [], string $error_message = "" ){
$arr = [];
$arr["success"] = $success;
$arr["code"] = $html_code;
if( $html_code !== 200 )
$arr["error"] = $error_message;
else
$arr = array_merge( $arr, $fields );
echo json_encode( $arr );
die();
}
public static function success_response( array $fields ){
self::response_type( 'application/json' );
http_response_code( 200 );
self::response( true, 200, $fields );
}
public static function fatal_stop( int $html_code, string $error_message = "" ){
self::response_type( 'application/json' );
http_response_code( $html_code );
self::response( false, $html_code, error_message: $error_message );
}
public static function invalid_parameter_stop( string $parameter_name, string $method = "" ){
self::fatal_stop( 400, "Invalid $method $parameter_name");
}
public static function build_url( string $action, array $params = [] ): string {
$url = $_ENV['ROMHACKPLAZAFS_URL'] . '?action=' . $action;
foreach ( $params as $key => $value ) {
$url .= '&' . $key . '=' . $value;
}
return $url;
}
public static function build_patcher_url( string $download_url, int $post_id )
{
return $_ENV['ROMHACKPLAZAFS_PATCHER_URL'] . '?download=' . base64_encode( $download_url ) . '&entry=' . $post_id;
}
public static function dotenv_array( string $env_name ){
return explode( " ", $_ENV[$env_name] );
}
public function __construct(){
$this->endpoint_requested = $this->swiffer_get( 'action', 'string' );
$this->is_index = str_contains( $_SERVER['REQUEST_URI'], 'index.php' );
if( $this->is_index )
$this->fs_api();
}
public function give_token( string $token_name ): string {
$token = $this->swiffer_post( $token_name, 'string' );
if( $token === "" )
$token = $this->swiffer_get( $token_name, 'string' );
return $token;
}
private function fs_api(){
if( $this->endpoint_requested === "" )
self::fatal_stop( 400, "action GET parameter not found." );
$this->endpoint_requested = preg_replace('/[^a-zA-Z0-9]/', '', $this->endpoint_requested);
$endpoint_class = 'RomhackPlazaFS\\Endpoints\\'. ucfirst( strtolower( $this->endpoint_requested ) );
if( !class_exists( $endpoint_class ) )
self::fatal_stop( 404, "This endpoint is not defined." );
$endpoint = new $endpoint_class();
if( !$endpoint instanceof Abstract_Endpoint )
self::fatal_stop( 500, "This endpoint is not valid." );
$endpoint->exec( $this );
}
}

53
src/SecurityTrait.php Normal file
View File

@@ -0,0 +1,53 @@
<?php
namespace RomhackPlazaFS;
use RomhackPlazaFS\Exceptions\InvalidTokenException;
use RomhackPlazaFS\Exceptions\TokenExpiredException;
use RomhackPlazaFS\Exceptions\WrongServerException;
defined( 'ROMHACKPLAZA_FS' ) || exit;
trait SecurityTrait {
/**
* @throws WrongServerException
* @throws TokenExpiredException
* @throws InvalidTokenException
*/
public function verify_token(string $token ){
try{
$enc_info = explode( "|", $token, 2 );
if( count( $enc_info ) < 2 )
throw new \Exception();
} catch ( \Exception $e ){
RomhackPlazaFS::fatal_stop( 400, "There are some missing informations for reaching Zeus...");
}
$json = base64_decode( $enc_info[0] );
$resig = hash_hmac( 'sha256', $json, $_ENV['ROMHACKPLAZAFS_SECRET_KEY'] );
if( !hash_equals( $resig, $enc_info[1] ) )
throw new InvalidTokenException();
/**
* 'user_id' => WordPress user ID.
* 'to' => Server URL
* 'action' => The action he wants to do.
* 'generated_at' => Timestamp generated value.
* 'expires_at' => Timestamp theory expiration value. (5 minutes...),
* 'romhackplaza' => Random value.
*/
$info = json_decode( $json, true );
if( $info['expires_at'] < time() )
throw new TokenExpiredException();
if( $info['to'] !== $_ENV['ROMHACKPLAZAFS_URL'] )
throw new WrongServerException();
return $info;
}
}

57
src/SwifferTrait.php Normal file
View File

@@ -0,0 +1,57 @@
<?php
namespace RomhackPlazaFS;
use RomhackPlazaFS\Exceptions\InvalidArgumentException;
defined( 'ROMHACKPLAZA_FS' ) || exit;
trait SwifferTrait {
public function swiffer(
int $input,
string $name,
string $type
): mixed {
try {
switch ($type) {
case 'int':
$val = filter_input($input, $name, FILTER_VALIDATE_INT);
if ($val === false) {
throw new InvalidArgumentException( $name );
}
return intval($val);
break;
case 'string':
$val = trim( $input === INPUT_GET ? ( $_GET[$name] ?? "" ) : ( $_POST[$name] ?? "" ) );
$val = htmlspecialchars( $val, ENT_QUOTES | ENT_HTML5 );
return $val;
break;
case 'filename':
$val = trim( $input === INPUT_GET ? ( $_GET[$name] ?? "" ) : ( $_POST[$name] ?? "" ) );
$val = basename( $val );
return $val;
break;
}
} catch (InvalidArgumentException $e) {
\RomhackPlazaFS\RomhackPlazaFS::fatal_stop( 400, "Invalid argument : " . $e->getMessage() );
}
return null;
}
public function swiffer_get( string $name, string $type ): mixed {
return $this->swiffer( INPUT_GET, $name, $type );
}
public function swiffer_post( string $name, string $type ): mixed {
return $this->swiffer( INPUT_POST, $name, $type );
}
}

25
src/bootstrap.php Normal file
View File

@@ -0,0 +1,25 @@
<?php
namespace RomhackPlazaFS;
defined( 'ROMHACKPLAZA_FS' ) || exit;
require_once __DIR__ . "/../vendor/autoload.php";
function _romhackplazafs_define_constants(){
if( !defined( 'ROMHACKPLAZA' ) )
define( 'ROMHACKPLAZA', "0.0.1" );
}
function _romhackplazafs_read_env(){
$dotenv = \Dotenv\Dotenv::createImmutable( __DIR__ . "/../" );
$dotenv->load();
}
_romhackplazafs_define_constants();
_romhackplazafs_read_env();
$_romhackplazafs = new RomhackPlazaFS();