2023-07-10 05:10:28 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
class Item implements JsonSerializable
|
|
|
|
{
|
2023-07-10 05:48:40 +00:00
|
|
|
|
|
|
|
// TODO: create a persist() method that updates the line if the offset where
|
|
|
|
// it originally existed in is given by 'ftell'. If it doesn't have an
|
|
|
|
// ID, then try to find it in the file and if it doesn't exist, append
|
|
|
|
//
|
|
|
|
// TODO: Maybe implement tags as an associative array or somethign?
|
|
|
|
//
|
2023-07-10 05:10:28 +00:00
|
|
|
// TODO: Change to id here in all occurrences
|
2023-07-10 05:48:40 +00:00
|
|
|
//
|
|
|
|
|
2023-07-10 05:10:28 +00:00
|
|
|
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 = 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 ---
|
2023-07-10 05:48:40 +00:00
|
|
|
$item = new Item();
|
|
|
|
$item->hash = hash_file('sha256', $from_path);
|
|
|
|
$item->extensionFromUpload($php_file);
|
2023-07-10 05:10:28 +00:00
|
|
|
|
|
|
|
// --- ACTUALLY GRAB FILE ---
|
2023-07-10 05:48:40 +00:00
|
|
|
$new_path = $item->getPath();
|
2023-07-10 05:10:28 +00:00
|
|
|
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();
|
|
|
|
|
2023-07-10 05:48:40 +00:00
|
|
|
MediaDB::add($item);
|
|
|
|
|
|
|
|
return $item;
|
2023-07-10 05:10:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private function extensionFromTags()
|
|
|
|
{
|
|
|
|
foreach ($this->tags as $i_tag) {
|
|
|
|
$parts = explode(':', $i_tag);
|
|
|
|
if ($parts[0] == 'format') {
|
|
|
|
$this->extension = $parts[1];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2023-07-10 05:48:40 +00:00
|
|
|
|
|
|
|
Json::error('Detected file without file format tag')->die();
|
2023-07-10 05:10:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
2023-07-10 05:48:40 +00:00
|
|
|
goto success;
|
2023-07-10 05:10:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
2023-07-10 05:48:40 +00:00
|
|
|
goto success;
|
2023-07-10 05:10:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Json::error('File mime-type and or extension not allowed.')->die();
|
2023-07-10 05:48:40 +00:00
|
|
|
|
|
|
|
success:
|
|
|
|
$this->extension = $ext;
|
|
|
|
$this->tags[] = 'format:' . $ext;
|
|
|
|
|
|
|
|
return;
|
2023-07-10 05:10:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public function getHash(): string
|
|
|
|
{
|
|
|
|
return $this->hash;
|
|
|
|
}
|
2023-07-10 05:48:40 +00:00
|
|
|
|
2023-07-10 05:10:28 +00:00
|
|
|
public function getExtension(): string
|
|
|
|
{
|
|
|
|
return $this->extension;
|
|
|
|
}
|
|
|
|
|
2023-07-10 05:48:40 +00:00
|
|
|
public function getTags(): array
|
|
|
|
{
|
|
|
|
return $this->tags;
|
|
|
|
}
|
|
|
|
|
2023-07-10 05:10:28 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|