diff --git a/debug-pages/test-v1.0.html b/debug-pages/test-v1.0.html
new file mode 100644
index 0000000..6d4b9d1
--- /dev/null
+++ b/debug-pages/test-v1.0.html
@@ -0,0 +1,112 @@
+
+
+
+
+
diff --git a/eeprom.php b/eeprom.php
index 19964c6..3dbcf5b 100644
--- a/eeprom.php
+++ b/eeprom.php
@@ -13,6 +13,7 @@ define('PRM_DEBUG', is_file(PRM_ROOT . '/.debug'));
define('PRM_PUBLIC', PRM_ROOT . '/public');
define('PRM_SOURCE', PRM_ROOT . '/src');
define('PRM_LIBRARY', PRM_ROOT . '/lib');
+define('PRM_DEBUG_PAGES', PRM_ROOT . '/debug-pages');
define('PRM_UPLOADS', PRM_PUBLIC . '/data');
define('PRM_THUMBS', PRM_PUBLIC . '/thumb');
diff --git a/lib/index b/lib/index
index 8a5423f..9d99e10 160000
--- a/lib/index
+++ b/lib/index
@@ -1 +1 @@
-Subproject commit 8a5423fea397e2f2adca0b9f46d1e5c21fd13c44
+Subproject commit 9d99e10541cf8ba7407be0b53323cfa3ebfc37f5
diff --git a/public/index.php b/public/index.php
index e8e2eda..88eba66 100644
--- a/public/index.php
+++ b/public/index.php
@@ -1,13 +1,10 @@
use('/', function($response) {
+ $response->setPoweredBy('EEPROM');
+});
+
+$router->use('/', function($response, $request) {
+ $origin = $request->getHeaderLine('Origin');
+
+ if(!empty($origin)) {
+ if(!eepromOriginAllowed($origin))
+ return 403;
+
+ $response->setHeader('Access-Control-Allow-Origin', $origin);
+ $response->setHeader('Vary', 'Origin');
}
+});
- 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($db, $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($db, $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($isApiDomain) {
+ $router->use('/', function($response, $request) {
+ if($request->getMethod() === 'OPTIONS') {
+ $response->setHeader('Access-Control-Allow-Credentials', 'true');
+ $response->setHeader('Access-Control-Allow-Headers', 'Authorization');
+ $response->setHeader('Access-Control-Allow-Methods', 'OPTIONS, GET, POST, DELETE');
+ return 204;
}
+ });
- if(User::active()->isRestricted()
- || User::active()->getId() !== $uploadInfo->getUserId()) {
- http_response_code(403);
- return;
- }
+ $router->use('/', function($response, $request) use ($db) {
+ $auth = $request->getHeaderLine('Authorization');
+ if(!empty($auth)) {
+ $authParts = explode(' ', $auth, 2);
+ $authMethod = strval($authParts[0] ?? '');
+ $authToken = strval($authParts[1] ?? '');
- http_response_code(204);
- $uploadInfo->delete($db, false);
- return;
- }
+ $authClients = Config::get('Auth', 'clients', []);
- if(!is_file($uploadInfo->getPath())) {
- http_response_code(404);
- echo 'Data is missing.';
- return;
- }
-
- if($getNormal) {
- $uploadInfo->bumpAccess($db);
- $uploadInfo->bumpExpiry($db);
- }
-
- $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($db, $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') {
- header('Content-Type: application/javascript; charset=utf-8');
- echo file_get_contents(PRM_ROOT . '/js/eeprom-v1.0.js');
- return;
-}
-
-if($reqPath === '/test-v1.0.html' && PRM_DEBUG) {
- header('Content-Type: text/html; charset=utf-8');
-
- $cookie = htmlspecialchars((string)filter_input(INPUT_COOKIE, 'msz_auth'));
-
- echo <<
-
-
-
-
-TEST;
- return;
-}
-
-header('Content-Type: text/plain; charset=us-ascii');
-
-if($reqPath === '/' || $reqPath === '/stats.json') {
- $fileCount = 0;
- $userCount = 0;
- $totalSize = 0;
- $uniqueTypes = 0;
-
- $uploadStats = $db->query('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');
-
- if($uploadStats->next()) {
- $fileCount = $uploadStats->getInteger(0);
- $totalSize = $uploadStats->getInteger(1);
- $uniqueTypes = $uploadStats->getInteger(2);
- }
-
- $userStats = $db->query('SELECT COUNT(`user_id`) AS `amount` FROM `prm_users` WHERE `user_restricted` IS NULL');
-
- if($userStats->next())
- $userCount = $userStats->getInteger(0);
-
- if($reqPath === '/stats.json') {
- header('Content-Type: application/json; charset=utf-8');
- echo json_encode([
+ return [
'size' => $totalSize,
'files' => $fileCount,
'types' => $uniqueTypes,
'members' => $userCount,
- ]);
- return;
- }
+ ];
+ });
- header('Content-Type: text/html; charset=utf-8');
- header('X-Accel-Redirect: /index.html');
- return;
-}
+ $router->get('/', function($response) {
+ $response->accelRedirect('/index.html');
+ $response->setContentType('text/html; charset=utf-8');
+ });
-if($reqPath === '/uploads') {
- if($reqMethod !== 'POST') {
- http_response_code(405);
- return;
- }
+ $router->post('/uploads', function($response, $request) use ($db) {
+ if(!$request->isFormContent())
+ return 400;
- try {
- $appInfo = Application::byId($db, filter_input(INPUT_POST, 'src', FILTER_VALIDATE_INT));
- } catch(ApplicationNotFoundException $ex) {
- http_response_code(404);
- return;
- }
+ $content = $request->getContent();
- 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($db, $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($db);
- } else {
try {
- $fileInfo = Upload::create(
- $db, $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;
+ $appInfo = Application::byId($db, (int)$content->getParam('src', FILTER_VALIDATE_INT));
+ } catch(ApplicationNotFoundException $ex) {
+ return 404;
}
- if(!move_uploaded_file($_FILES['file']['tmp_name'], $fileInfo->getPath())) {
- http_response_code(500);
- return;
+ if(!User::hasActive())
+ return 401;
+
+ $userInfo = User::active();
+
+ if($userInfo->isRestricted())
+ return 403;
+
+ try {
+ $file = $content->getUploadedFile('file');
+ } catch(\RuntimeException $ex) {
+ return 400;
}
+
+ $maxFileSize = $appInfo->getSizeLimit();
+ if($appInfo->allowSizeMultiplier())
+ $maxFileSize *= $userInfo->getSizeMultiplier();
+
+ $localFile = $file->getLocalFileName();
+ $fileSize = filesize($localFile);
+
+ if($file->getSize() !== $fileSize || $fileSize > $maxFileSize) {
+ $response->setHeader('Access-Control-Expose-Headers', 'X-EEPROM-Max-Size');
+ $response->setHeader('X-EEPROM-Max-Size', $maxFileSize);
+ return 413;
+ }
+
+ $hash = hash_file('sha256', $localFile);
+ $fileInfo = Upload::byHash($db, $hash);
+
+ if($fileInfo !== null) {
+ if($fileInfo->isDMCA())
+ return 451;
+
+ if($fileInfo->getUserId() !== $userInfo->getId()
+ || $fileInfo->getApplicationId() !== $appInfo->getId())
+ unset($fileInfo);
+ }
+
+ if(!empty($fileInfo)) {
+ if($fileInfo->isDeleted())
+ $fileInfo->restore($db);
+ } else {
+ try {
+ $fileInfo = Upload::create(
+ $db, $appInfo, $userInfo,
+ $file->getSuggestedFileName(),
+ mime_content_type($localFile),
+ $fileSize, $hash,
+ $appInfo->getExpiry(), true
+ );
+ } catch(UploadCreationFailedException $ex) {
+ return 500;
+ }
+
+ try {
+ $file->moveTo($fileInfo->getPath());
+ } catch(\RuntimeException $ex) {
+ return 500;
+ }
+ }
+
+ $response->setStatusCode(201);
+ $response->setHeader('Content-Type', 'application/json; charset=utf-8');
+ return $fileInfo;
+ });
+
+ $router->delete('/uploads/:fileid', function($response, $request, $fileId) use ($db) {
+ try {
+ $uploadInfo = Upload::byId($db, $fileId);
+ } catch(UploadNotFoundException $ex) {
+ $response->setContent('File not found.');
+ return 404;
+ }
+
+ if($uploadInfo->isDMCA()) {
+ $response->setContent('File is unavailable for copyright reasons.');
+ return 451;
+ }
+
+ if($uploadInfo->isDeleted() || $uploadInfo->hasExpired()) {
+ $response->setContent('File not found.');
+ return 404;
+ }
+
+ if(!User::hasActive())
+ return 401;
+
+ if(User::active()->isRestricted() || User::active()->getId() !== $uploadInfo->getUserId())
+ return 403;
+
+ $uploadInfo->delete($db, false);
+ return 204;
+ });
+
+ $router->get('/uploads/:filename', function($response, $request, $fileName) use ($db) {
+ $pathInfo = pathinfo($fileName);
+ $fileId = $pathInfo['filename'];
+ $fileExt = $pathInfo['extension'] ?? '';
+ $isThumbnail = $fileExt === 't';
+ $isJson = $fileExt === 'json';
+
+ if($fileExt !== '' && $fileExt !== 't' && $fileExt !== 'json')
+ return 404;
+
+ try {
+ $uploadInfo = Upload::byId($db, $fileId);
+ } catch(UploadNotFoundException $ex) {
+ $response->setContent('File not found.');
+ return 404;
+ }
+
+ if($isJson)
+ return $uploadInfo;
+
+ if($uploadInfo->isDMCA()) {
+ $response->setContent('File is unavailable for copyright reasons.');
+ return 451;
+ }
+
+ if($uploadInfo->isDeleted() || $uploadInfo->hasExpired()) {
+ $response->setContent('File not found.');
+ return 404;
+ }
+
+ if(!is_file($uploadInfo->getPath())) {
+ $response->setContent('Data is missing.');
+ return 404;
+ }
+
+ if(!$isThumbnail) {
+ $uploadInfo->bumpAccess($db);
+ $uploadInfo->bumpExpiry($db);
+ }
+
+ $fileName = $uploadInfo->getName();
+ $contentType = $uploadInfo->getType();
+
+ if($contentType === 'application/octet-stream' || str_starts_with($contentType, 'text/'))
+ $contentType = 'text/plain';
+
+ $sourceDir = basename($isThumbnail ? PRM_THUMBS : PRM_UPLOADS);
+
+ if($isThumbnail && $uploadInfo->supportsThumbnail()) {
+ if(!is_file($uploadInfo->getThumbPath()))
+ $uploadInfo->createThumbnail();
+ $contentType = 'image/jpeg';
+ $fileName = pathinfo($fileName, PATHINFO_FILENAME) . '-thumb.jpg';
+ }
+
+ $response->accelRedirect(sprintf('/%s/%s', $sourceDir, $uploadInfo->getId()));
+ $response->setContentType($contentType);
+ $response->setFileName(addslashes($fileName));
+ });
+
+ if(PRM_DEBUG) {
+ $router->get('/test-v1.0.html', function($response) {
+ $response->setContentType('text/html; charset=utf-8');
+ return strtr(file_get_contents(PRM_DEBUG_PAGES . '/test-v1.0.html'), [
+ ':cookie' => htmlspecialchars((string)filter_input(INPUT_COOKIE, 'msz_auth')),
+ ]);
+ });
}
+} else {
+ $router->use('/', function($response, $request) {
+ if($request->getMethod() === 'OPTIONS') {
+ $response->setHeader('Access-Control-Allow-Methods', 'OPTIONS, GET');
+ return 204;
+ }
+ });
- http_response_code(201);
- header('Content-Type: application/json; charset=utf-8');
- echo json_encode($fileInfo);
- return;
+ $router->get('/:filename', function($response, $request, $fileName) use ($db) {
+ $pathInfo = pathinfo($fileName);
+ $fileId = $pathInfo['filename'];
+ $fileExt = $pathInfo['extension'] ?? '';
+ $isThumbnail = $fileExt === 't';
+
+ if($fileExt !== '' && $fileExt !== 't')
+ return 404;
+
+ try {
+ $uploadInfo = Upload::byId($db, $fileId);
+ } catch(UploadNotFoundException $ex) {
+ $response->setContent('File not found.');
+ return 404;
+ }
+
+ if($uploadInfo->isDMCA()) {
+ $response->setContent('File is unavailable for copyright reasons.');
+ return 451;
+ }
+
+ if($uploadInfo->isDeleted() || $uploadInfo->hasExpired()) {
+ $response->setContent('File not found.');
+ return 404;
+ }
+
+ if(!is_file($uploadInfo->getPath())) {
+ $response->setContent('Data is missing.');
+ return 404;
+ }
+
+ if(!$isThumbnail) {
+ $uploadInfo->bumpAccess($db);
+ $uploadInfo->bumpExpiry($db);
+ }
+
+ $fileName = $uploadInfo->getName();
+ $contentType = $uploadInfo->getType();
+
+ if($contentType === 'application/octet-stream' || str_starts_with($contentType, 'text/'))
+ $contentType = 'text/plain';
+
+ $sourceDir = basename($isThumbnail ? PRM_THUMBS : PRM_UPLOADS);
+
+ if($isThumbnail && $uploadInfo->supportsThumbnail()) {
+ if(!is_file($uploadInfo->getThumbPath()))
+ $uploadInfo->createThumbnail();
+ $contentType = 'image/jpeg';
+ $fileName = pathinfo($fileName, PATHINFO_FILENAME) . '-thumb.jpg';
+ }
+
+ $response->accelRedirect(sprintf('/%s/%s', $sourceDir, $uploadInfo->getId()));
+ $response->setContentType($contentType);
+ $response->setFileName(addslashes($fileName));
+ });
}
-http_response_code(404);
+$router->dispatch();
diff --git a/js/eeprom-v1.0.js b/public/js/eeprom-v1.0.js
similarity index 100%
rename from js/eeprom-v1.0.js
rename to public/js/eeprom-v1.0.js