web-base/Core/Objects/Router/StaticFileRoute.class.php

132 lines
3.9 KiB
PHP
Raw Normal View History

2022-06-01 09:47:31 +02:00
<?php
2022-11-18 18:06:46 +01:00
namespace Core\Objects\Router;
2022-06-01 09:47:31 +02:00
2022-11-27 12:33:27 +01:00
use Core\Driver\SQL\SQL;
2022-11-18 18:06:46 +01:00
use Core\Objects\Context;
2024-03-28 11:56:17 +01:00
use Core\Objects\DatabaseEntity\Attribute\Transient;
2022-11-27 12:33:27 +01:00
use Core\Objects\DatabaseEntity\Route;
2022-11-18 18:06:46 +01:00
use Core\Objects\Search\Searchable;
use Core\Objects\Search\SearchQuery;
use Core\Objects\Search\SearchResult;
2022-11-27 12:33:27 +01:00
use JetBrains\PhpStorm\Pure;
2022-08-20 22:17:17 +02:00
2022-11-27 12:33:27 +01:00
class StaticFileRoute extends Route {
2022-06-01 09:47:31 +02:00
2022-08-20 22:17:17 +02:00
use Searchable;
2024-03-28 11:56:17 +01:00
#[Transient]
2022-06-01 09:47:31 +02:00
private int $code;
public function __construct(string $pattern, bool $exact, string $path, int $code = 200) {
2022-11-27 12:33:27 +01:00
parent::__construct("static", $pattern, $path, $exact);
2022-06-01 09:47:31 +02:00
$this->code = $code;
2022-11-27 12:33:27 +01:00
$this->extra = json_encode($this->code);
}
2022-11-27 15:58:44 +01:00
protected function readExtra() {
parent::readExtra();
2022-11-27 12:33:27 +01:00
$this->code = json_decode($this->extra);
2022-06-01 09:47:31 +02:00
}
2022-11-27 15:58:44 +01:00
public function preInsert(array &$row) {
parent::preInsert($row);
$this->extra = json_encode($this->code);
}
2022-06-01 09:47:31 +02:00
public function call(Router $router, array $params): string {
http_response_code($this->code);
2022-08-20 22:17:17 +02:00
$this->serveStatic($this->getAbsolutePath(), $router);
return "";
2022-06-01 09:47:31 +02:00
}
2022-11-27 12:33:27 +01:00
#[Pure] private function getPath(): string {
return $this->getTarget();
}
2022-06-01 09:47:31 +02:00
protected function getArgs(): array {
2022-11-27 12:33:27 +01:00
return array_merge(parent::getArgs(), [$this->getPath(), $this->code]);
2022-06-01 09:47:31 +02:00
}
2022-08-20 22:17:17 +02:00
public function getAbsolutePath(): string {
2022-11-27 12:33:27 +01:00
return WEBROOT . DIRECTORY_SEPARATOR . $this->getPath();
2022-08-20 22:17:17 +02:00
}
2022-08-20 22:17:17 +02:00
public static function serveStatic(string $path, ?Router $router = null) {
if (!startsWith($path, WEBROOT . DIRECTORY_SEPARATOR)) {
http_response_code(406);
echo "<b>Access restricted, requested file outside web root:</b> " . htmlspecialchars($path);
}
if (!file_exists($path) || !is_file($path) || !is_readable($path)) {
http_response_code(500);
echo "<b>Unable to read file:</b> " . htmlspecialchars($path);
}
$pathInfo = pathinfo($path);
2022-06-20 19:52:31 +02:00
if ($router !== null) {
$ext = $pathInfo["extension"] ?? "";
2022-06-20 19:52:31 +02:00
if (!$router->getContext()->getSettings()->isExtensionAllowed($ext)) {
http_response_code(406);
echo "<b>Access restricted:</b> Extension '" . htmlspecialchars($ext) . "' not allowed to serve.";
}
}
$size = filesize($path);
$mimeType = mime_content_type($path);
header("Content-Type: $mimeType");
header("Content-Length: $size");
header('Accept-Ranges: bytes');
if (strcasecmp($_SERVER["REQUEST_METHOD"], "HEAD") !== 0) {
$handle = fopen($path, "rb");
if ($handle === false) {
http_response_code(500);
echo "<b>Unable to read file:</b> " . htmlspecialchars($path);
}
$offset = 0;
$length = $size;
if (isset($_SERVER['HTTP_RANGE'])) {
preg_match('/bytes=(\d+)-(\d+)?/', $_SERVER['HTTP_RANGE'], $matches);
$offset = intval($matches[1]);
$length = intval($matches[2]) - $offset;
http_response_code(206);
header('Content-Range: bytes ' . $offset . '-' . ($offset + $length) . '/' . $size);
}
downloadFile($handle, $offset, $length);
}
}
2022-08-20 22:17:17 +02:00
public function doSearch(Context $context, SearchQuery $query): array {
$results = [];
$path = $this->getAbsolutePath();
if (is_file($path) && is_readable($path)) {
$pathInfo = pathinfo($path);
$extension = $pathInfo["extension"] ?? "";
$fileName = $pathInfo["filename"] ?? "";
if ($context->getSettings()->isExtensionAllowed($extension)) {
$mimeType = mime_content_type($path);
if (startsWith($mimeType, "text/")) {
$document = @file_get_contents($path);
if ($document) {
if ($mimeType === "text/html") {
$results = Searchable::searchHtml($document, $query);
} else {
$results = Searchable::searchText($document, $query);
}
}
}
}
$results = array_map(function ($res) use ($fileName) {
return new SearchResult($this->getPattern(), $fileName, $res["text"]);
}, $results);
}
return $results;
}
2022-06-01 09:47:31 +02:00
}