mirror of https://github.com/YGGverse/next.git
yggverse
7 months ago
8 changed files with 706 additions and 386 deletions
@ -0,0 +1,263 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace Yggverse\Next\Controller; |
||||||
|
|
||||||
|
use \Ratchet\MessageComponentInterface; |
||||||
|
|
||||||
|
class Nex implements MessageComponentInterface |
||||||
|
{ |
||||||
|
private \Yggverse\Next\Model\Environment $_environment; |
||||||
|
private \Yggverse\Next\Model\Filesystem $_filesystem; |
||||||
|
|
||||||
|
public function __construct( |
||||||
|
\Yggverse\Next\Model\Environment $environment, |
||||||
|
\Yggverse\Next\Model\Filesystem $filesystem |
||||||
|
) { |
||||||
|
// Init environment |
||||||
|
$this->_environment = $environment; |
||||||
|
|
||||||
|
// Init filesystem |
||||||
|
$this->_filesystem = $filesystem; |
||||||
|
|
||||||
|
// Check port is defined |
||||||
|
if (!$this->_environment->get('port')) |
||||||
|
{ |
||||||
|
// Set protocol defaults |
||||||
|
$this->_environment->set('port', 1900); |
||||||
|
} |
||||||
|
|
||||||
|
// Dump event |
||||||
|
if ($this->_environment->get('dump')) |
||||||
|
{ |
||||||
|
print( |
||||||
|
str_replace( |
||||||
|
[ |
||||||
|
'{time}', |
||||||
|
'{host}', |
||||||
|
'{port}', |
||||||
|
'{root}' |
||||||
|
], |
||||||
|
[ |
||||||
|
(string) date('c'), |
||||||
|
(string) $this->_environment->get('host'), |
||||||
|
(string) $this->_environment->get('port'), |
||||||
|
(string) $this->_filesystem->root() |
||||||
|
], |
||||||
|
_('[{time}] [init] server started at {host}:{port}{root}') |
||||||
|
) . PHP_EOL |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public function onOpen( |
||||||
|
\Ratchet\ConnectionInterface $connection |
||||||
|
) { |
||||||
|
// Dump event |
||||||
|
if ($this->_environment->get('dump')) |
||||||
|
{ |
||||||
|
print( |
||||||
|
str_replace( |
||||||
|
[ |
||||||
|
'{time}', |
||||||
|
'{host}', |
||||||
|
'{crid}' |
||||||
|
], |
||||||
|
[ |
||||||
|
(string) date('c'), |
||||||
|
(string) $connection->remoteAddress, |
||||||
|
(string) $connection->resourceId |
||||||
|
], |
||||||
|
_('[{time}] [open] incoming connection {host}#{crid}') |
||||||
|
) . PHP_EOL |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public function onMessage( |
||||||
|
\Ratchet\ConnectionInterface $connection, |
||||||
|
$request |
||||||
|
) { |
||||||
|
// Define response |
||||||
|
$response = null; |
||||||
|
|
||||||
|
// Filter request |
||||||
|
$request = trim( |
||||||
|
$request |
||||||
|
); |
||||||
|
|
||||||
|
// Build absolute realpath |
||||||
|
$realpath = $this->_filesystem->absolute( |
||||||
|
$request |
||||||
|
); |
||||||
|
|
||||||
|
// Make sure realpath valid to continue |
||||||
|
if ($this->_filesystem->valid($realpath)) |
||||||
|
{ |
||||||
|
// Route |
||||||
|
switch (true) |
||||||
|
{ |
||||||
|
// File request |
||||||
|
case $file = $this->_filesystem->file($realpath): |
||||||
|
|
||||||
|
// Return file content |
||||||
|
$response = $file; |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
// Directory request |
||||||
|
case $list = $this->_filesystem->list($realpath): |
||||||
|
|
||||||
|
// Try index file on defined |
||||||
|
if ($index = $this->_filesystem->file($realpath . $this->_environment->get('file'))) |
||||||
|
{ |
||||||
|
// Return index file content |
||||||
|
$response = $index; |
||||||
|
} |
||||||
|
|
||||||
|
// Listing enabled |
||||||
|
else if ($this->_environment->get('list')) |
||||||
|
{ |
||||||
|
// FS map |
||||||
|
$line = []; |
||||||
|
|
||||||
|
foreach ($list as $item) |
||||||
|
{ |
||||||
|
// Build gemini text link |
||||||
|
$link = ['=>']; |
||||||
|
|
||||||
|
if ($item['name']) |
||||||
|
{ |
||||||
|
$link[] = $item['file'] ? $item['name'] |
||||||
|
: $item['name'] . '/'; |
||||||
|
} |
||||||
|
|
||||||
|
if ($item['time'] && $this->_environment->get('time')) |
||||||
|
{ |
||||||
|
$link[] = date('Y-m-d', $item['time']); |
||||||
|
} |
||||||
|
|
||||||
|
// Append link to the new line |
||||||
|
$line[] = implode(' ', $link); |
||||||
|
} |
||||||
|
|
||||||
|
// Merge lines to response |
||||||
|
$response = implode( |
||||||
|
PHP_EOL, |
||||||
|
$line |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Dump event |
||||||
|
if ($this->_environment->get('dump')) |
||||||
|
{ |
||||||
|
// Print debug from template |
||||||
|
print( |
||||||
|
str_ireplace( |
||||||
|
[ |
||||||
|
'{time}', |
||||||
|
'{host}', |
||||||
|
'{crid}', |
||||||
|
'{path}', |
||||||
|
'{real}', |
||||||
|
'{size}' |
||||||
|
], |
||||||
|
[ |
||||||
|
(string) date('c'), |
||||||
|
(string) $connection->remoteAddress, |
||||||
|
(string) $connection->resourceId, |
||||||
|
(string) str_replace( |
||||||
|
'%', |
||||||
|
'%%', |
||||||
|
$request |
||||||
|
), |
||||||
|
(string) str_replace( |
||||||
|
'%', |
||||||
|
'%%', |
||||||
|
$realpath |
||||||
|
), |
||||||
|
(string) mb_strlen( |
||||||
|
$response |
||||||
|
) |
||||||
|
], |
||||||
|
_('[{time}] [message] incoming connection {host}#{crid} "{path}" > "{real}" {size} bytes') |
||||||
|
) . PHP_EOL |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
// Noting to return? |
||||||
|
if (empty($response)) |
||||||
|
{ |
||||||
|
// Try failure file on defined |
||||||
|
if ($fail = $this->_filesystem->file($this->_environment->get('fail'))) |
||||||
|
{ |
||||||
|
$response = $fail; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Send response |
||||||
|
$connection->send( |
||||||
|
$response |
||||||
|
); |
||||||
|
|
||||||
|
// Disconnect |
||||||
|
$connection->close(); |
||||||
|
} |
||||||
|
|
||||||
|
public function onClose( |
||||||
|
\Ratchet\ConnectionInterface $connection |
||||||
|
) { |
||||||
|
// Dump event |
||||||
|
if ($this->_environment->get('dump')) |
||||||
|
{ |
||||||
|
print( |
||||||
|
str_replace( |
||||||
|
[ |
||||||
|
'{time}', |
||||||
|
'{host}', |
||||||
|
'{crid}' |
||||||
|
], |
||||||
|
[ |
||||||
|
(string) date('c'), |
||||||
|
(string) $connection->remoteAddress, |
||||||
|
(string) $connection->resourceId |
||||||
|
], |
||||||
|
_('[{time}] [close] incoming connection {host}#{crid}') |
||||||
|
) . PHP_EOL |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public function onError( |
||||||
|
\Ratchet\ConnectionInterface $connection, |
||||||
|
\Exception $exception |
||||||
|
) { |
||||||
|
// Dump event |
||||||
|
if ($this->_environment->get('dump')) |
||||||
|
{ |
||||||
|
print( |
||||||
|
str_replace( |
||||||
|
[ |
||||||
|
'{time}', |
||||||
|
'{host}', |
||||||
|
'{crid}', |
||||||
|
'{info}' |
||||||
|
], |
||||||
|
[ |
||||||
|
(string) date('c'), |
||||||
|
(string) $connection->remoteAddress, |
||||||
|
(string) $connection->resourceId, |
||||||
|
(string) $exception->getMessage() |
||||||
|
], |
||||||
|
_('[{time}] [error] incoming connection {host}#{crid} reason: {info}') |
||||||
|
) . PHP_EOL |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
// Disconnect |
||||||
|
$connection->close(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,84 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Yggverse\Next\Model; |
||||||
|
|
||||||
|
class Environment |
||||||
|
{ |
||||||
|
private array $_config; |
||||||
|
|
||||||
|
public function __construct( |
||||||
|
array $argv, |
||||||
|
array $default = [] |
||||||
|
) { |
||||||
|
foreach ($default as $key => $value) |
||||||
|
{ |
||||||
|
$this->_config[$key] = (string) $value; |
||||||
|
} |
||||||
|
|
||||||
|
foreach ($argv as $value) |
||||||
|
{ |
||||||
|
if (preg_match('/^(?<key>[^=]+)=(?<value>.*)$/', $value, $argument)) |
||||||
|
{ |
||||||
|
$this->_config[mb_strtolower($argument['key'])] = (string) $argument['value']; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public function get( |
||||||
|
string $key |
||||||
|
): mixed |
||||||
|
{ |
||||||
|
$key = mb_strtolower( |
||||||
|
$key |
||||||
|
); |
||||||
|
|
||||||
|
return isset($this->_config[$key]) ? $this->_config[$key] |
||||||
|
: null; |
||||||
|
} |
||||||
|
|
||||||
|
public function set( |
||||||
|
string $key, |
||||||
|
string $value, |
||||||
|
bool $semantic = true |
||||||
|
): void |
||||||
|
{ |
||||||
|
if ($semantic) |
||||||
|
{ |
||||||
|
$_value = mb_strtolower( |
||||||
|
$value |
||||||
|
); |
||||||
|
|
||||||
|
switch (true) |
||||||
|
{ |
||||||
|
case in_array( |
||||||
|
$_value, |
||||||
|
[ |
||||||
|
'1', |
||||||
|
'yes', |
||||||
|
'true', |
||||||
|
'enable' |
||||||
|
] |
||||||
|
): $value = true; |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case in_array( |
||||||
|
$_value, |
||||||
|
[ |
||||||
|
'0', |
||||||
|
'no', |
||||||
|
'null', |
||||||
|
'false', |
||||||
|
'disable' |
||||||
|
] |
||||||
|
): $value = false; |
||||||
|
|
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
$this->_config[mb_strtolower($key)] = $value; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,253 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace Yggverse\Next\Model; |
||||||
|
|
||||||
|
class Filesystem |
||||||
|
{ |
||||||
|
private string $_root; |
||||||
|
|
||||||
|
public function __construct(?string $path) |
||||||
|
{ |
||||||
|
// Require path value to continue |
||||||
|
if (empty($path)) |
||||||
|
{ |
||||||
|
throw new \Exception( |
||||||
|
_('root path required!') |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
// Require absolute path |
||||||
|
if (!str_starts_with($path, DIRECTORY_SEPARATOR)) |
||||||
|
{ |
||||||
|
throw new \Exception( |
||||||
|
_('root path not absolute!') |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
// Exclude symlinks and relative entities, append slash |
||||||
|
if (!$realpath = $this->_realpath($path)) |
||||||
|
{ |
||||||
|
throw new \Exception( |
||||||
|
_('could not build root realpath!') |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
// Root must be directory |
||||||
|
if (!is_dir($realpath)) |
||||||
|
{ |
||||||
|
throw new \Exception( |
||||||
|
_('root path is not directory!') |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
// Check root path does not contain hidden context |
||||||
|
if (str_contains($realpath, DIRECTORY_SEPARATOR . '.')) |
||||||
|
{ |
||||||
|
throw new \Exception( |
||||||
|
_('root path must not contain hidden context!') |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
// Done! |
||||||
|
$this->_root = $realpath; |
||||||
|
} |
||||||
|
|
||||||
|
public function root(): string |
||||||
|
{ |
||||||
|
return $this->_root; |
||||||
|
} |
||||||
|
|
||||||
|
public function file(?string $realpath): ?string |
||||||
|
{ |
||||||
|
if (!$this->valid($realpath)) |
||||||
|
{ |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
if (!is_file($realpath)) |
||||||
|
{ |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
return file_get_contents( |
||||||
|
$realpath |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
public function list( |
||||||
|
?string $realpath, |
||||||
|
string $sort = 'name', |
||||||
|
int $order = SORT_ASC, |
||||||
|
int $method = SORT_STRING | SORT_NATURAL | SORT_FLAG_CASE |
||||||
|
): ?array |
||||||
|
{ |
||||||
|
// Validate requested path |
||||||
|
if (!$this->valid($realpath)) |
||||||
|
{ |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
// Make sure requested path is directory |
||||||
|
if (!is_dir($realpath)) |
||||||
|
{ |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
// Begin list builder |
||||||
|
$directories = []; |
||||||
|
$files = []; |
||||||
|
|
||||||
|
foreach ((array) scandir($realpath) as $name) |
||||||
|
{ |
||||||
|
// Skip system locations |
||||||
|
if (empty($name) || $name == '.') |
||||||
|
{ |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
// Build destination path |
||||||
|
if (!$path = $this->_realpath($realpath . $name)) |
||||||
|
{ |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
// Validate destination path |
||||||
|
if (!$this->valid($path)) |
||||||
|
{ |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
// Context |
||||||
|
switch (true) |
||||||
|
{ |
||||||
|
case is_dir($path): |
||||||
|
|
||||||
|
$directories[] = |
||||||
|
[ |
||||||
|
'file' => false, |
||||||
|
'name' => $name, |
||||||
|
'path' => $path, |
||||||
|
'time' => filemtime( |
||||||
|
$path |
||||||
|
) |
||||||
|
]; |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case is_file($path): |
||||||
|
|
||||||
|
$files[] = |
||||||
|
[ |
||||||
|
'file' => true, |
||||||
|
'name' => $name, |
||||||
|
'path' => $path, |
||||||
|
'time' => filemtime( |
||||||
|
$path |
||||||
|
) |
||||||
|
]; |
||||||
|
|
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Sort order |
||||||
|
array_multisort( |
||||||
|
array_column( |
||||||
|
$directories, |
||||||
|
$sort |
||||||
|
), |
||||||
|
$order, |
||||||
|
$method, |
||||||
|
$directories |
||||||
|
); |
||||||
|
|
||||||
|
// Sort files by name ASC |
||||||
|
array_multisort( |
||||||
|
array_column( |
||||||
|
$directories, |
||||||
|
$sort |
||||||
|
), |
||||||
|
$order, |
||||||
|
$method, |
||||||
|
$directories |
||||||
|
); |
||||||
|
|
||||||
|
// Merge list |
||||||
|
return array_merge( |
||||||
|
$directories, |
||||||
|
$files |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
public function valid(?string $realpath): bool |
||||||
|
{ |
||||||
|
if (empty($realpath)) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if ($realpath != $this->_realpath($realpath)) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if (!str_starts_with($realpath, $this->_root)) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if (str_contains($realpath, DIRECTORY_SEPARATOR . '.')) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if (!is_readable($realpath)) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
// Return absolute realpath with root constructed |
||||||
|
public function absolute(?string $path): ?string |
||||||
|
{ |
||||||
|
if (!$realpath = $this->_realpath($this->_root . $path)) |
||||||
|
{ |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
return $realpath; |
||||||
|
} |
||||||
|
|
||||||
|
// PHP::realpath extension appending slash to dir paths |
||||||
|
private function _realpath(?string $path): ?string |
||||||
|
{ |
||||||
|
if (empty($path)) |
||||||
|
{ |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
if (!$realpath = realpath($path)) |
||||||
|
{ |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
if (!is_readable($realpath)) |
||||||
|
{ |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
if (is_dir($realpath)) |
||||||
|
{ |
||||||
|
$realpath = rtrim( |
||||||
|
$realpath, |
||||||
|
DIRECTORY_SEPARATOR |
||||||
|
) . DIRECTORY_SEPARATOR; |
||||||
|
} |
||||||
|
|
||||||
|
return $realpath; |
||||||
|
} |
||||||
|
} |
@ -1,355 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
// Load dependencies |
|
||||||
require_once __DIR__ . |
|
||||||
DIRECTORY_SEPARATOR . '..'. |
|
||||||
DIRECTORY_SEPARATOR . 'vendor' . |
|
||||||
DIRECTORY_SEPARATOR . 'autoload.php'; |
|
||||||
|
|
||||||
// Parse startup arguments |
|
||||||
foreach ((array) $argv as $item) |
|
||||||
{ |
|
||||||
if (preg_match('/^(?<key>[^=]+)=(?<value>.*)$/', $item, $argument)) |
|
||||||
{ |
|
||||||
switch ($argument['key']) |
|
||||||
{ |
|
||||||
case 'host': |
|
||||||
|
|
||||||
define( |
|
||||||
'NEXT_HOST', |
|
||||||
(string) $argument['value'] |
|
||||||
); |
|
||||||
|
|
||||||
break; |
|
||||||
|
|
||||||
case 'port': |
|
||||||
|
|
||||||
define( |
|
||||||
'NEXT_PORT', |
|
||||||
(int) $argument['value'] |
|
||||||
); |
|
||||||
|
|
||||||
break; |
|
||||||
|
|
||||||
case 'path': |
|
||||||
|
|
||||||
$path = rtrim( |
|
||||||
(string) $argument['value'], |
|
||||||
DIRECTORY_SEPARATOR |
|
||||||
) . DIRECTORY_SEPARATOR; |
|
||||||
|
|
||||||
if (!str_starts_with($path, DIRECTORY_SEPARATOR)) |
|
||||||
{ |
|
||||||
print( |
|
||||||
_('absolute path required') |
|
||||||
) . PHP_EOL; |
|
||||||
|
|
||||||
exit; |
|
||||||
} |
|
||||||
|
|
||||||
if (!is_dir($path) || !is_readable($path)) |
|
||||||
{ |
|
||||||
print( |
|
||||||
_('path not accessible') |
|
||||||
) . PHP_EOL; |
|
||||||
|
|
||||||
exit; |
|
||||||
} |
|
||||||
|
|
||||||
define( |
|
||||||
'NEXT_PATH', |
|
||||||
(string) $path |
|
||||||
); |
|
||||||
|
|
||||||
break; |
|
||||||
|
|
||||||
case 'file': |
|
||||||
|
|
||||||
define( |
|
||||||
'NEXT_FILE', |
|
||||||
(string) $argument['value'] |
|
||||||
); |
|
||||||
|
|
||||||
break; |
|
||||||
|
|
||||||
case 'fail': |
|
||||||
|
|
||||||
$fail = (string) $argument['value']; |
|
||||||
|
|
||||||
if (!str_starts_with($fail, DIRECTORY_SEPARATOR)) |
|
||||||
{ |
|
||||||
print( |
|
||||||
_('absolute path required') |
|
||||||
) . PHP_EOL; |
|
||||||
|
|
||||||
exit; |
|
||||||
} |
|
||||||
|
|
||||||
if (!is_file($fail) || !is_readable($fail)) |
|
||||||
{ |
|
||||||
print( |
|
||||||
_('fail template not accessible') |
|
||||||
) . PHP_EOL; |
|
||||||
|
|
||||||
exit; |
|
||||||
} |
|
||||||
|
|
||||||
define( |
|
||||||
'NEXT_FAIL', |
|
||||||
(string) file_get_contents( |
|
||||||
$fail |
|
||||||
) |
|
||||||
); |
|
||||||
|
|
||||||
break; |
|
||||||
|
|
||||||
case 'list': |
|
||||||
|
|
||||||
define( |
|
||||||
'NEXT_LIST', |
|
||||||
in_array( |
|
||||||
mb_strtolower( |
|
||||||
(string) $argument['value'] |
|
||||||
), |
|
||||||
[ |
|
||||||
'true', |
|
||||||
'yes', |
|
||||||
'1' |
|
||||||
] |
|
||||||
) |
|
||||||
); |
|
||||||
|
|
||||||
break; |
|
||||||
|
|
||||||
case 'size': |
|
||||||
|
|
||||||
define( |
|
||||||
'NEXT_SIZE', |
|
||||||
(int) $argument['value'] |
|
||||||
); |
|
||||||
|
|
||||||
break; |
|
||||||
|
|
||||||
case 'dump': |
|
||||||
|
|
||||||
define( |
|
||||||
'NEXT_DUMP', |
|
||||||
(string) $argument['value'] |
|
||||||
); |
|
||||||
|
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Validate required arguments and set optional defaults |
|
||||||
if (!defined('NEXT_HOST')) define('NEXT_HOST', '127.0.0.1'); |
|
||||||
|
|
||||||
if (!defined('NEXT_PORT')) define('NEXT_PORT', 1900); |
|
||||||
|
|
||||||
if (!defined('NEXT_PATH')) |
|
||||||
{ |
|
||||||
print( |
|
||||||
_('path required') |
|
||||||
) . PHP_EOL; |
|
||||||
|
|
||||||
exit; |
|
||||||
} |
|
||||||
|
|
||||||
if (!defined('NEXT_FILE')) define('NEXT_FILE', false); |
|
||||||
|
|
||||||
if (!defined('NEXT_LIST')) define('NEXT_LIST', true); |
|
||||||
|
|
||||||
if (!defined('NEXT_SIZE')) define('NEXT_SIZE', 1024); |
|
||||||
|
|
||||||
if (!defined('NEXT_FAIL')) define('NEXT_FAIL', 'fail'); |
|
||||||
|
|
||||||
if (!defined('NEXT_DUMP')) define('NEXT_DUMP', '[{time}] [{code}] {host}:{port} {path} {real} {size} bytes'); |
|
||||||
|
|
||||||
// Init server |
|
||||||
$server = new \Yggverse\Nex\Server( |
|
||||||
NEXT_HOST, |
|
||||||
NEXT_PORT, |
|
||||||
NEXT_SIZE |
|
||||||
); |
|
||||||
|
|
||||||
$server->start( |
|
||||||
function ( |
|
||||||
string $request, |
|
||||||
string $connect |
|
||||||
): ?string |
|
||||||
{ |
|
||||||
// Define response |
|
||||||
$response = null; |
|
||||||
|
|
||||||
// Filter request |
|
||||||
$request = trim( |
|
||||||
$request |
|
||||||
); |
|
||||||
|
|
||||||
$request = empty($request) ? '/' : $request; |
|
||||||
|
|
||||||
// Build realpath |
|
||||||
$realpath = realpath( |
|
||||||
NEXT_PATH . |
|
||||||
urldecode( |
|
||||||
filter_var( |
|
||||||
$request, |
|
||||||
FILTER_SANITIZE_URL |
|
||||||
) |
|
||||||
) |
|
||||||
); |
|
||||||
|
|
||||||
// Make sure directory path ending with slash |
|
||||||
if (is_dir($realpath)) |
|
||||||
{ |
|
||||||
$realpath = rtrim( |
|
||||||
$realpath, |
|
||||||
DIRECTORY_SEPARATOR |
|
||||||
) . DIRECTORY_SEPARATOR; |
|
||||||
} |
|
||||||
|
|
||||||
// Validate realpath exists, started with path defined and does not contain hidden entities |
|
||||||
if ($realpath && str_starts_with($realpath, NEXT_PATH) && !str_contains($realpath, DIRECTORY_SEPARATOR . '.')) |
|
||||||
{ |
|
||||||
// Try directory |
|
||||||
if (is_dir($realpath)) |
|
||||||
{ |
|
||||||
// Try index file on enabled |
|
||||||
if (NEXT_FILE && file_exists($realpath . NEXT_FILE) && is_readable($realpath . NEXT_FILE)) |
|
||||||
{ |
|
||||||
// Update realpath returned on default file response |
|
||||||
$realpath = $realpath . NEXT_FILE; |
|
||||||
|
|
||||||
$response = file_get_contents( |
|
||||||
$realpath |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
// Try directory listing on enabled |
|
||||||
else if (NEXT_LIST) |
|
||||||
{ |
|
||||||
$directories = []; |
|
||||||
|
|
||||||
$files = []; |
|
||||||
|
|
||||||
foreach ((array) scandir($realpath) as $filename) |
|
||||||
{ |
|
||||||
// Process system entities |
|
||||||
if (str_starts_with($filename, '.')) |
|
||||||
{ |
|
||||||
// Parent navigation |
|
||||||
if ($filename == '..' && $parent = realpath($realpath . $filename)) |
|
||||||
{ |
|
||||||
$parent = rtrim( |
|
||||||
$parent, |
|
||||||
DIRECTORY_SEPARATOR |
|
||||||
) . DIRECTORY_SEPARATOR; |
|
||||||
|
|
||||||
if (str_starts_with($parent, NEXT_PATH)) |
|
||||||
{ |
|
||||||
$directories[$filename] = '=> ../'; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
continue; // skip everything else |
|
||||||
} |
|
||||||
|
|
||||||
// Directory |
|
||||||
if (is_dir($realpath . $filename)) |
|
||||||
{ |
|
||||||
if (is_readable($realpath . $filename)) |
|
||||||
{ |
|
||||||
$directories[$filename] = sprintf( |
|
||||||
'=> %s/', |
|
||||||
urlencode( |
|
||||||
$filename |
|
||||||
) |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
// File |
|
||||||
if (is_readable($realpath . $filename)) |
|
||||||
{ |
|
||||||
$files[$filename] = sprintf( |
|
||||||
'=> %s', |
|
||||||
urlencode( |
|
||||||
$filename |
|
||||||
) |
|
||||||
); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Sort by keys ASC |
|
||||||
ksort( |
|
||||||
$directories, |
|
||||||
SORT_STRING | SORT_FLAG_CASE | SORT_NATURAL |
|
||||||
); |
|
||||||
|
|
||||||
ksort( |
|
||||||
$files, |
|
||||||
SORT_STRING | SORT_FLAG_CASE | SORT_NATURAL |
|
||||||
); |
|
||||||
|
|
||||||
// Merge items |
|
||||||
$response = implode( |
|
||||||
PHP_EOL, |
|
||||||
array_merge( |
|
||||||
$directories, |
|
||||||
$files |
|
||||||
) |
|
||||||
); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Try file |
|
||||||
else |
|
||||||
{ |
|
||||||
$response = file_get_contents( |
|
||||||
$realpath |
|
||||||
); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Dump request on enabled |
|
||||||
if (NEXT_DUMP) |
|
||||||
{ |
|
||||||
// Build connection URL #72811 |
|
||||||
$url = sprintf( |
|
||||||
'nex://%s', |
|
||||||
$connect |
|
||||||
); |
|
||||||
|
|
||||||
// Print dump from template |
|
||||||
printf( |
|
||||||
str_ireplace( |
|
||||||
[ |
|
||||||
'{time}', |
|
||||||
'{code}', |
|
||||||
'{host}', |
|
||||||
'{port}', |
|
||||||
'{path}', |
|
||||||
'{real}', |
|
||||||
'{size}' |
|
||||||
], |
|
||||||
[ |
|
||||||
(string) date('c'), |
|
||||||
(string) (int) is_string($response), |
|
||||||
(string) parse_url($url, PHP_URL_HOST), |
|
||||||
(string) parse_url($url, PHP_URL_PORT), |
|
||||||
(string) str_replace('%', '%%', $request), |
|
||||||
(string) str_replace('%', '%%', empty($realpath) ? '!' : $realpath), |
|
||||||
(string) mb_strlen((string) $response) |
|
||||||
], |
|
||||||
NEXT_DUMP |
|
||||||
) . PHP_EOL |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
// Send response |
|
||||||
return is_string($response) ? $response : NEXT_FAIL; |
|
||||||
} |
|
||||||
); |
|
@ -0,0 +1,59 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
// Load dependencies |
||||||
|
require_once __DIR__ . |
||||||
|
DIRECTORY_SEPARATOR . '..'. |
||||||
|
DIRECTORY_SEPARATOR . 'vendor' . |
||||||
|
DIRECTORY_SEPARATOR . 'autoload.php'; |
||||||
|
|
||||||
|
// Init environment |
||||||
|
$environment = new \Yggverse\Next\Model\Environment( |
||||||
|
$argv, |
||||||
|
json_decode( |
||||||
|
file_get_contents( |
||||||
|
__DIR__ . |
||||||
|
DIRECTORY_SEPARATOR . '..'. |
||||||
|
DIRECTORY_SEPARATOR . 'default.json' |
||||||
|
), |
||||||
|
true |
||||||
|
) |
||||||
|
); |
||||||
|
|
||||||
|
// Init filesystem |
||||||
|
$filesystem = new \Yggverse\Next\Model\Filesystem( |
||||||
|
$environment->get('path') |
||||||
|
); |
||||||
|
|
||||||
|
// Start server |
||||||
|
try |
||||||
|
{ |
||||||
|
switch ($environment->get('type')) |
||||||
|
{ |
||||||
|
case 'nex': |
||||||
|
|
||||||
|
$server = \Ratchet\Server\IoServer::factory( |
||||||
|
new \Yggverse\Next\Controller\Nex( |
||||||
|
$environment, |
||||||
|
$filesystem |
||||||
|
), |
||||||
|
$environment->get('port'), |
||||||
|
$environment->get('host') |
||||||
|
); |
||||||
|
|
||||||
|
$server->run(); |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
default: |
||||||
|
|
||||||
|
throw new \Exception( |
||||||
|
_('valid server type required!') |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Show help |
||||||
|
catch (\Exception $exception) |
||||||
|
{ |
||||||
|
// @TODO |
||||||
|
} |
Loading…
Reference in new issue