getArray('cors:origins'); if(empty($allowed)) return true; return in_array($origin, $allowed); } $isApiDomain = $_SERVER['HTTP_HOST'] === $cfg->getString('domain:api'); $router = new HttpFx; $router->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'); } }); if($isApiDomain) { $router->use('/', function($response, $request) { if($request->hasHeader('Origin')) $response->setHeader('Access-Control-Allow-Credentials', 'true'); }); $router->use('/', function($response, $request) { if($request->getMethod() === 'OPTIONS') { $response->setHeader('Access-Control-Allow-Headers', 'Authorization'); $response->setHeader('Access-Control-Allow-Methods', 'OPTIONS, GET, POST, DELETE'); return 204; } }); $router->use('/', function($response, $request) use ($db, $cfg) { $auth = $request->getHeaderLine('Authorization'); if(empty($auth)) { $mszAuth = (string)$request->getCookie('msz_auth'); if(!empty($mszAuth)) $auth = 'Misuzu ' . $mszAuth; } if(!empty($auth)) { $authParts = explode(' ', $auth, 2); $authMethod = strval($authParts[0] ?? ''); $authToken = strval($authParts[1] ?? ''); $authClients = $cfg->getArray('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(); } }); $router->get('/eeprom.js', function($response) { $response->accelRedirect('/js/eeprom-v1.0.js'); $response->setContentType('application/javascript; charset=utf-8'); }); $router->get('/stats.json', function() use ($db) { $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); return [ 'size' => $totalSize, 'files' => $fileCount, 'types' => $uniqueTypes, 'members' => $userCount, ]; }); $router->get('/', function($response) { $response->accelRedirect('/index.html'); $response->setContentType('text/html; charset=utf-8'); }); $router->post('/uploads', function($response, $request) use ($db) { if(!$request->isFormContent()) return 400; $content = $request->getContent(); try { $appInfo = Application::byId($db, (int)$content->getParam('src', FILTER_VALIDATE_INT)); } catch(ApplicationNotFoundException $ex) { return 404; } 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) { \Sentry\captureException($ex); return 500; } try { $file->moveTo($fileInfo->getPath()); } catch(\RuntimeException $ex) { \Sentry\captureException($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; } }); $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)); }); } $router->dispatch();