From 463bd267a850d498c145cbfe6112a1e957d56447 Mon Sep 17 00:00:00 2001 From: Dendy Faist Date: Mon, 10 Jul 2023 07:10:28 +0200 Subject: [PATCH] Implement JSON interface to item and JSON itself --- item.php | 155 ++++++++++++++++++++++++++++++++++ json.php | 34 ++++++++ main.php | 239 +++++----------------------------------------------- mediadb.php | 40 +++++++++ 4 files changed, 251 insertions(+), 217 deletions(-) create mode 100644 item.php create mode 100644 json.php create mode 100644 mediadb.php diff --git a/item.php b/item.php new file mode 100644 index 0000000..136e334 --- /dev/null +++ b/item.php @@ -0,0 +1,155 @@ +map(function ($line) use (&$ret, $id) { + if ($line[0] == $id) { + $ret = Item::fromLine($line); + return true; + } + + return false; + }); + + return $ret; + } + + public static function fromLine(array $line): Item + { + $ret = new Item(); + + $ret->hash = array_shift($line); + $ret->tags = $line; + $ret->extensionFromTags(); + + return $ret; + } + + // ::upload() requires the PHP file upload ARRAY from $_FILES + public static function upload(array $php_file): Item + { + $from_path = $php_file['tmp_name'] ?? null; + + // --- CHECKS --- + if (!is_string($from_path)) + Json::error('Passed invalid upload structure')->die(); + if (!is_uploaded_file($from_path)) + Json::error('Trying to upload illegal or non-existent file')->die(); + + // --- INITIALIZE --- + $ret = new Item(); + $ret->hash = hash_file('sha256', $from_path); + $ret->extensionFromUpload($php_file); + + // --- ACTUALLY GRAB FILE --- + $new_path = $ret->getPath(); + if (file_exists($new_path)) + Json::error('File already exists')->die(); + if (!move_uploaded_file($from_path, $new_path)) + Json::error('Failed to move uploaded file')->die(); + + return $ret; + } + + private function extensionFromTags() + { + foreach ($this->tags as $i_tag) { + $parts = explode(':', $i_tag); + if ($parts[0] == 'format') { + $this->extension = $parts[1]; + return; + } + } + } + + // Assumes is_uploaded_file has been done + private function extensionFromUpload(array $php_file) + { + $path = $php_file['tmp_name'] ?? false; + $name = $php_file['name'] ?? null; + + // Try to get extension from mimetype + $ext = (new finfo(FILEINFO_EXTENSION))->file($path); + + // REVIEW: Maybe change it so it doesn't have to be separated like this but + // do somethign like ['mime/type', null] be equivalent to this? + if (array_search($ext, ['webp', 'png', 'jpeg']) !== false) { + $this->extension = $ext; + return; + } + + // If it doesn't work, try to figure it out from the original extension + if ($ext == '???') { + // We mustn't accept everything straight away, so we're only accepting + // mime-type and original extension combinations that make sense + $whitelist = [ + ['application/zip', 'kra'], + ['application/zip', 'krz'], + ]; + + // NOTE: The original extension has to be grabbed from the original name + $mime = mime_content_type($path); + $ext = pathinfo($name, PATHINFO_EXTENSION); + + if (array_search([$mime, $ext], $whitelist) !== false) { + $this->extension = $ext; + return; + } + } + + Json::error('File mime-type and or extension not allowed.')->die(); + } + + public function getHash(): string + { + return $this->hash; + } + public function getExtension(): string + { + return $this->extension; + } + + public function getPath(bool $absolute = false): string + { + return sprintf( + '%s/%s.%s', + $GLOBALS[$absolute ? 'path_media' : 'path_media'], + $this->hash, + $this->extension + ); + } + public function getUri(): string + { + // TODO: Implement relative + return sprintf( + '%s/%s/%s.%s', + $GLOBALS['base_root'], + $GLOBALS['base_media'], + $this->hash, + $this->extension, + ); + } + + public function jsonSerialize(): array + { + return [ + 'id' => $this->hash, + 'link' => $this->getUri(), + 'tags' => $this->tags, + ]; + } + + public function getJson(): Json + { + return Json::new($this); + } +} diff --git a/json.php b/json.php new file mode 100644 index 0000000..b98c5a0 --- /dev/null +++ b/json.php @@ -0,0 +1,34 @@ +struct = $struct; + } + + public static function new(mixed $struct): Json + { + return new Json($struct); + } + + public static function error(mixed $msg): Json + { + return new Json([ + 'type' => 'error', + 'msg' => $msg, + ]); + } + + public function jsonSerialize(): array + { + return $this->struct; + } + + public function die() + { + print(json_encode($this->struct)); + die(); + } +} diff --git a/main.php b/main.php index e13c649..6340cf7 100644 --- a/main.php +++ b/main.php @@ -1,9 +1,5 @@ post_item(), ['/item', 'GET'] => get_item(), - //['/search', 'GET'] => get_search(), + ['/search', 'GET'] => get_search(), default => Json::error('Not implemented') })->die(); @@ -52,222 +53,26 @@ function post_item() return $item->getJson(); }; -class MediaDB +function get_search() { - private $handler; - private bool $finished = false; - private array $lines = []; + $query = $_GET['q'] ?? ''; + $limit = intval($_GET['limit'] ?? '20'); + // TODO: Implement page - public function __construct() - { - // Just initialize the file handler - $this->handler = fopen($GLOBALS['path_mediadb'], 'r'); - if ($this->handler === false) - Json::error('Error opening media DB'); - } + // If intval returns 0, it's an error + if ($limit == 0) return Json::error('Invalid limit parameter'); + if ($query !== '') return Json::error('Search not implemented yet'); - private function getLine(): ?array - { - $ret = fgetcsv($this->handler, 0, ' '); - if ($ret === false) - return null; + $list = []; - return $ret; - } + $db = new MediaDB(); + $db->map(function ($line) use (&$list, $limit) { + if (count($list) >= $limit) return true; - public function map(callable $func) - { - // First read from what's already in memory - foreach ($this->lines as $i_line) { - if ($func($i_line) === false) return; - } + $list[] = Item::fromLine($line); - // REVIEW: Maybe this isn't needed cuz the while would be as eficient - // anyway? + return false; + }); - // If we already got to the end, there's no sense in carrying on - if ($this->finished) return; - - // If we run out, read from the file and append to array so the next lookup - // is fast - while ($line = $this->getLine()) { - $this->lines[] = $line; - - if ($func($line) === false) return; - } - - // Set the flag so we don't reach this point two times - $this->finished = true; - } -} - -class Item -{ - // TODO: Change to id here in all occurrences - private string $hash; - private string $extension; - private array $tags = []; - - public static function load(MediaDB $db, string $id): ?Item - { - $ret = null; - - $db->map(function ($line) use (&$ret, $id) { - if ($line[0] == $id) { - $ret = new Item(); - $ret->hash = array_shift($line); - $ret->tags = $line; - $ret->extensionFromTags(); - - return true; - } - - return false; - }); - - return $ret; - } - - - // ::upload() requires the PHP file upload ARRAY from $_FILES - public static function upload(array $php_file): Item - { - $from_path = $php_file['tmp_name'] ?? null; - - // --- CHECKS --- - if (!is_string($from_path)) - Json::error('Passed invalid upload structure')->die(); - if (!is_uploaded_file($from_path)) - Json::error('Trying to upload illegal or non-existent file')->die(); - - // --- INITIALIZE --- - $ret = new Item(); - $ret->hash = hash_file('sha256', $from_path); - $ret->extensionFromUpload($php_file); - - // --- ACTUALLY GRAB FILE --- - $new_path = $ret->getPath(); - if (file_exists($new_path)) - Json::error('File already exists')->die(); - if (!move_uploaded_file($from_path, $new_path)) - Json::error('Failed to move uploaded file')->die(); - - return $ret; - } - - private function extensionFromTags() - { - foreach ($this->tags as $i_tag) { - $parts = explode(':', $i_tag); - if ($parts[0] == 'format') { - $this->extension = $parts[1]; - return; - } - } - } - - // Assumes is_uploaded_file has been done - private function extensionFromUpload(array $php_file) - { - $path = $php_file['tmp_name'] ?? false; - $name = $php_file['name'] ?? null; - - // Try to get extension from mimetype - $ext = (new finfo(FILEINFO_EXTENSION))->file($path); - - // REVIEW: Maybe change it so it doesn't have to be separated like this but - // do somethign like ['mime/type', null] be equivalent to this? - if (array_search($ext, ['webp', 'png', 'jpeg']) !== false) { - $this->extension = $ext; - return; - } - - // If it doesn't work, try to figure it out from the original extension - if ($ext == '???') { - // We mustn't accept everything straight away, so we're only accepting - // mime-type and original extension combinations that make sense - $whitelist = [ - ['application/zip', 'kra'], - ['application/zip', 'krz'], - ]; - - // NOTE: The original extension has to be grabbed from the original name - $mime = mime_content_type($path); - $ext = pathinfo($name, PATHINFO_EXTENSION); - - if (array_search([$mime, $ext], $whitelist) !== false) { - $this->extension = $ext; - return; - } - } - - Json::error('File mime-type and or extension not allowed.')->die(); - } - - public function getHash(): string - { - return $this->hash; - } - public function getExtension(): string - { - return $this->extension; - } - - public function getPath(bool $absolute = false): string - { - return sprintf( - '%s/%s.%s', - $GLOBALS[$absolute ? 'path_media' : 'path_media'], - $this->hash, - $this->extension - ); - } - public function getUri(): string - { - // TODO: Implement relative - return sprintf( - '%s/%s/%s.%s', - $GLOBALS['base_root'], - $GLOBALS['base_media'], - $this->hash, - $this->extension, - ); - } - - public function getJson(): Json - { - return Json::new([ - 'id' => $this->hash, - 'link' => $this->getUri(), - 'tags' => $this->tags, - ]); - } -} - -class Json -{ - function __construct( - private mixed $struct - ) { - $this->struct = $struct; - } - - public static function new(mixed $struct): Json - { - return new Json($struct); - } - - public static function error(mixed $msg): Json - { - return new Json([ - 'type' => 'error', - 'msg' => $msg, - ]); - } - - public function die() - { - print(json_encode($this->struct)); - die(); - } + return Json::new($list); } diff --git a/mediadb.php b/mediadb.php new file mode 100644 index 0000000..1ef9759 --- /dev/null +++ b/mediadb.php @@ -0,0 +1,40 @@ +handler = fopen($GLOBALS['path_mediadb'], 'r'); + if ($this->handler === false) + Json::error('Error opening media DB'); + } + + private function getLine(): ?array + { + $ret = fgetcsv($this->handler, 0, ' '); + if ($ret === false) + return null; + + return $ret; + } + + public function map(callable $func) + { + // First read from what's already in memory + foreach ($this->lines as $i_line) { + if ($func($i_line) === true) return; + } + + // If we run out, read from the file and append to array so the next lookup + // is fast + while ($line = $this->getLine()) { + $this->lines[] = $line; + + if ($func($line) === true) return; + } + } +}