forked from flashii/eeprom
362 lines
10 KiB
PHP
362 lines
10 KiB
PHP
<?php
|
|
namespace EEPROM;
|
|
|
|
require_once __DIR__ . '/../startup.php';
|
|
|
|
$reqMethod = $_SERVER['REQUEST_METHOD'];
|
|
$reqPath = '/' . trim(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH), '/');
|
|
|
|
header('X-Powered-By: EEPROM');
|
|
|
|
function eepromOriginAllowed(string $origin): bool {
|
|
$origin = mb_strtolower(parse_url($origin, PHP_URL_HOST));
|
|
|
|
if($origin === $_SERVER['HTTP_HOST'])
|
|
return true;
|
|
|
|
$allowed = Config::get('CORS', 'origins', []);
|
|
if(empty($allowed))
|
|
return true;
|
|
|
|
return in_array($origin, $allowed);
|
|
}
|
|
|
|
function eepromByteSymbol(int $bytes, bool $decimal = true, array $symbols = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']): string {
|
|
if($bytes < 1)
|
|
return '0 B';
|
|
|
|
$divider = $decimal ? 1000 : 1024;
|
|
$exp = floor(log($bytes) / log($divider));
|
|
$bytes = $bytes / pow($divider, $exp);
|
|
$symbol = $symbols[$exp];
|
|
|
|
return sprintf("%.2f %s%sB", $bytes, $symbol, $symbol !== '' && !$decimal ? 'i' : '');
|
|
}
|
|
|
|
if($_SERVER['HTTP_HOST'] === Config::get('Uploads', 'short_domain')) {
|
|
$reqMethod = 'GET'; // short domain is read only, prevent deleting
|
|
$reqPath = '/uploads/' . trim($reqPath, '/');
|
|
$isShortDomain = true;
|
|
}
|
|
|
|
if(!empty($_SERVER['HTTP_ORIGIN'])) {
|
|
if(!eepromOriginAllowed($_SERVER['HTTP_ORIGIN'])) {
|
|
http_response_code(403);
|
|
return;
|
|
}
|
|
|
|
header('Access-Control-Allow-Origin: ' . $_SERVER['HTTP_ORIGIN']);
|
|
header('Vary: Origin');
|
|
}
|
|
|
|
if($reqMethod === 'OPTIONS') {
|
|
http_response_code(204);
|
|
if(isset($isShortDomain))
|
|
header('Access-Control-Allow-Methods: OPTIONS, GET');
|
|
else {
|
|
header('Access-Control-Allow-Credentials: true');
|
|
header('Access-Control-Allow-Headers: Authorization');
|
|
header('Access-Control-Allow-Methods: OPTIONS, GET, POST, DELETE');
|
|
}
|
|
return;
|
|
}
|
|
|
|
if(!isset($isShortDomain) && !empty($_SERVER['HTTP_AUTHORIZATION'])) {
|
|
$authParts = explode(' ', $_SERVER['HTTP_AUTHORIZATION'], 2);
|
|
$authMethod = strval($authParts[0] ?? '');
|
|
$authToken = strval($authParts[1] ?? '');
|
|
|
|
$authClients = Config::get('Auth', 'clients', []);
|
|
|
|
foreach($authClients as $client) {
|
|
$client = new $client;
|
|
if($client->getName() !== $authMethod)
|
|
continue;
|
|
$authUserId = $client->verifyToken($authToken);
|
|
break;
|
|
}
|
|
|
|
if(isset($authUserId) && $authUserId > 0)
|
|
User::byId($authUserId)->setActive();
|
|
}
|
|
|
|
if(preg_match('#^/uploads/([a-zA-Z0-9-_]{32})(\.t)?/?$#', $reqPath, $matches)) {
|
|
$getNormal = empty($matches[2]);
|
|
$getThumbnail = isset($matches[2]) && $matches[2] === '.t';
|
|
|
|
try {
|
|
$uploadInfo = Upload::byId($matches[1]);
|
|
} catch(UploadNotFoundException $ex) {
|
|
http_response_code(404);
|
|
echo 'File not found.';
|
|
return;
|
|
}
|
|
|
|
if($uploadInfo->isDMCA()) {
|
|
http_response_code(451);
|
|
echo 'File is unavailable for copyright reasons.';
|
|
return;
|
|
}
|
|
|
|
if($uploadInfo->isDeleted() || $uploadInfo->hasExpired()) {
|
|
http_response_code(404);
|
|
echo 'File not found.';
|
|
return;
|
|
}
|
|
|
|
if($reqMethod === 'DELETE') {
|
|
if(!User::hasActive()) {
|
|
http_response_code(401);
|
|
return;
|
|
}
|
|
|
|
if(User::active()->isRestricted()
|
|
|| User::active()->getId() !== $uploadInfo->getUserId()) {
|
|
http_response_code(403);
|
|
return;
|
|
}
|
|
|
|
http_response_code(204);
|
|
$uploadInfo->delete(false);
|
|
return;
|
|
}
|
|
|
|
if(!is_file($uploadInfo->getPath())) {
|
|
http_response_code(404);
|
|
echo 'Data is missing.';
|
|
return;
|
|
}
|
|
|
|
if($getNormal) {
|
|
$uploadInfo->bumpAccess();
|
|
$uploadInfo->bumpExpiry();
|
|
}
|
|
|
|
$fileName = $uploadInfo->getName();
|
|
$contentType = $uploadInfo->getType();
|
|
|
|
if($contentType === 'application/octet-stream' || substr($contentType, 0, 5) === 'text/')
|
|
$contentType = 'text/plain';
|
|
|
|
$sourceDir = basename($getThumbnail ? PRM_THUMBS : PRM_UPLOADS);
|
|
|
|
if($getThumbnail && $uploadInfo->supportsThumbnail()) {
|
|
if(!is_file($uploadInfo->getThumbPath()))
|
|
$uploadInfo->createThumbnail();
|
|
$contentType = 'image/jpeg';
|
|
$fileName = pathinfo($fileName, PATHINFO_FILENAME) . '-thumb.jpg';
|
|
}
|
|
|
|
header(sprintf('X-Accel-Redirect: /%s/%s', $sourceDir, $uploadInfo->getId()));
|
|
header(sprintf('Content-Type: %s', $contentType));
|
|
header(sprintf('Content-Disposition: inline; filename="%s"', addslashes($fileName)));
|
|
return;
|
|
}
|
|
|
|
if(preg_match('#^/uploads/([a-zA-Z0-9-_]{32})\.json/?$#', $reqPath, $matches)) {
|
|
if(isset($isShortDomain)) {
|
|
http_response_code(404);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
$uploadInfo = Upload::byId($matches[1]);
|
|
} catch(UploadNotFoundException $ex) {
|
|
http_response_code(404);
|
|
return;
|
|
}
|
|
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
echo json_encode($uploadInfo);
|
|
return;
|
|
}
|
|
|
|
if($reqPath === '/eeprom.js' && is_file(PRM_ROOT . '/eeprom.js')) {
|
|
header('Content-Type: application/javascript; charset=utf-8');
|
|
echo file_get_contents(PRM_ROOT . '/eeprom.js');
|
|
return;
|
|
}
|
|
|
|
header('Content-Type: text/plain; charset=us-ascii');
|
|
|
|
if($reqPath === '/' || $reqPath === '/stats' || $reqPath === '/html') {
|
|
$fileCount = 0;
|
|
$userCount = 0;
|
|
$totalSize = 0;
|
|
$uniqueTypes = 0;
|
|
|
|
$getUploadStats = DB::prepare('
|
|
SELECT
|
|
COUNT(`upload_id`) AS `amount`,
|
|
SUM(`upload_size`) AS `size`,
|
|
COUNT(DISTINCT `upload_type`) AS `types`
|
|
FROM `prm_uploads`
|
|
WHERE `upload_deleted` IS NULL
|
|
AND `upload_dmca` IS NULL
|
|
');
|
|
$getUploadStats->execute();
|
|
$uploadStats = $getUploadStats->execute() ? $getUploadStats->fetchObject() : null;
|
|
|
|
if(!empty($uploadStats)) {
|
|
$fileCount = intval($uploadStats->amount);
|
|
$totalSize = intval($uploadStats->size ?? 0);
|
|
$uniqueTypes = intval($uploadStats->types ?? 0);
|
|
}
|
|
|
|
$getUserStats = DB::prepare('
|
|
SELECT COUNT(`user_id`) AS `amount`
|
|
FROM `prm_users`
|
|
WHERE `user_restricted` IS NULL
|
|
');
|
|
$getUserStats->execute();
|
|
$userStats = $getUserStats->execute() ? $getUserStats->fetchObject() : null;
|
|
|
|
if(!empty($userStats)) {
|
|
$userCount = intval($userStats->amount);
|
|
}
|
|
|
|
if($reqPath === '/stats') {
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
echo json_encode([
|
|
'files_size' => $totalSize,
|
|
'files_count' => $fileCount,
|
|
'files_types' => $uniqueTypes,
|
|
'users_count' => $userCount,
|
|
]);
|
|
return;
|
|
}
|
|
|
|
$totalSizeFmt = eepromByteSymbol($totalSize);
|
|
|
|
if($reqPath === '/html') {
|
|
header('Content-Type: text/html; charset=utf-8');
|
|
echo <<<HTML
|
|
<!doctype html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8"/>
|
|
<title>Flashii EEPROM</title>
|
|
<style type="text/css">
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<pre>
|
|
________________ ____ ____ __ ___
|
|
/ ____/ ____/ __ \/ __ \/ __ \/ |/ /
|
|
/ __/ / __/ / /_/ / /_/ / / / / /|_/ /
|
|
/ /___/ /___/ ____/ _, _/ /_/ / / / /
|
|
/_____/_____/_/ /_/ |_|\____/_/ /_/
|
|
|
|
Currently serving {$totalSizeFmt} ({$totalSize} bytes) of {$fileCount} files in {$uniqueTypes} unique file types from {$userCount} users.
|
|
</pre>
|
|
</body>
|
|
</html>
|
|
HTML;
|
|
return;
|
|
}
|
|
|
|
echo <<<ASCII
|
|
________________ ____ ____ __ ___
|
|
/ ____/ ____/ __ \/ __ \/ __ \/ |/ /
|
|
/ __/ / __/ / /_/ / /_/ / / / / /|_/ /
|
|
/ /___/ /___/ ____/ _, _/ /_/ / / / /
|
|
/_____/_____/_/ /_/ |_|\____/_/ /_/
|
|
|
|
Currently serving {$totalSizeFmt} ({$totalSize} bytes) of {$fileCount} files in {$uniqueTypes} unique file types from {$userCount} users.
|
|
\r\n
|
|
ASCII;
|
|
return;
|
|
}
|
|
|
|
if($reqPath === '/uploads') {
|
|
if($reqMethod !== 'POST') {
|
|
http_response_code(405);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
$appInfo = Application::byId(
|
|
filter_input(INPUT_POST, 'src', FILTER_VALIDATE_INT)
|
|
);
|
|
} catch(ApplicationNotFoundException $ex) {
|
|
http_response_code(404);
|
|
return;
|
|
}
|
|
|
|
if(!User::hasActive()) {
|
|
http_response_code(401);
|
|
return;
|
|
}
|
|
|
|
$userInfo = User::active();
|
|
|
|
if($userInfo->isRestricted()) {
|
|
http_response_code(403);
|
|
return;
|
|
}
|
|
|
|
if(empty($_FILES['file']['tmp_name']) || !is_file($_FILES['file']['tmp_name'])) {
|
|
http_response_code(400);
|
|
return;
|
|
}
|
|
|
|
$maxFileSize = $appInfo->getSizeLimit();
|
|
if($appInfo->allowSizeMultiplier())
|
|
$maxFileSize *= $userInfo->getSizeMultiplier();
|
|
|
|
$fileSize = filesize($_FILES['file']['tmp_name']);
|
|
|
|
if($_FILES['file']['size'] !== $fileSize || $fileSize > $maxFileSize) {
|
|
http_response_code(413);
|
|
header('Access-Control-Expose-Headers: X-EEPROM-Max-Size');
|
|
header('X-EEPROM-Max-Size: ' . $maxFileSize);
|
|
return;
|
|
}
|
|
|
|
$hash = hash_file('sha256', $_FILES['file']['tmp_name']);
|
|
$fileInfo = Upload::byHash($hash);
|
|
|
|
if($fileInfo !== null) {
|
|
if($fileInfo->isDMCA()) {
|
|
http_response_code(451);
|
|
return;
|
|
}
|
|
|
|
if($fileInfo->getUserId() !== $userInfo->getId()
|
|
|| $fileInfo->getApplicationId() !== $appInfo->getId())
|
|
unset($fileInfo);
|
|
}
|
|
|
|
if(!empty($fileInfo)) {
|
|
if($fileInfo->isDeleted())
|
|
$fileInfo->restore();
|
|
} else {
|
|
try {
|
|
$fileInfo = Upload::create(
|
|
$appInfo, $userInfo,
|
|
$_FILES['file']['name'],
|
|
mime_content_type($_FILES['file']['tmp_name']),
|
|
$fileSize, $hash,
|
|
$appInfo->getExpiry(), true
|
|
);
|
|
} catch(UploadCreationFailedException $ex) {
|
|
http_response_code(500);
|
|
return;
|
|
}
|
|
|
|
if(!move_uploaded_file($_FILES['file']['tmp_name'], $fileInfo->getPath())) {
|
|
http_response_code(500);
|
|
return;
|
|
}
|
|
}
|
|
|
|
http_response_code(201);
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
echo json_encode($fileInfo);
|
|
return;
|
|
}
|
|
|
|
if(is_file('../_manage.php') && include_once '../_manage.php')
|
|
return;
|
|
|
|
http_response_code(404);
|