2020-05-08 22:53:21 +00:00
< ? php
namespace EEPROM ;
2022-07-06 20:42:26 +00:00
use Index\Http\HttpFx ;
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
require_once __DIR__ . '/../eeprom.php' ;
2020-05-08 22:53:21 +00:00
2023-10-31 16:19:58 +00:00
set_exception_handler ( function ( \Throwable $ex ) {
\Sentry\captureException ( $ex );
ob_clean ();
http_response_code ( 500 );
if ( PRM_DEBUG ) {
header ( 'Content-Type: text/plain; charset=utf-8' );
echo ( string ) $ex ;
} else echo '500' ;
exit ;
});
2020-05-08 22:53:21 +00:00
function eepromOriginAllowed ( string $origin ) : bool {
2023-10-31 16:10:32 +00:00
global $cfg ;
2020-05-08 22:53:21 +00:00
$origin = mb_strtolower ( parse_url ( $origin , PHP_URL_HOST ));
if ( $origin === $_SERVER [ 'HTTP_HOST' ])
return true ;
2023-10-31 16:10:32 +00:00
$allowed = $cfg -> getArray ( 'cors:origins' );
2020-05-08 22:53:21 +00:00
if ( empty ( $allowed ))
return true ;
return in_array ( $origin , $allowed );
}
2023-10-31 16:10:32 +00:00
$isApiDomain = $_SERVER [ 'HTTP_HOST' ] === $cfg -> getString ( 'domain:api' );
2022-07-06 20:42:26 +00:00
$router = new HttpFx ;
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
$router -> use ( '/' , function ( $response ) {
$response -> setPoweredBy ( 'EEPROM' );
});
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
$router -> use ( '/' , function ( $response , $request ) {
$origin = $request -> getHeaderLine ( 'Origin' );
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
if ( ! empty ( $origin )) {
if ( ! eepromOriginAllowed ( $origin ))
return 403 ;
2020-05-12 18:30:22 +00:00
2022-07-06 20:42:26 +00:00
$response -> setHeader ( 'Access-Control-Allow-Origin' , $origin );
$response -> setHeader ( 'Vary' , 'Origin' );
2020-05-08 22:53:21 +00:00
}
2022-07-06 20:42:26 +00:00
});
if ( $isApiDomain ) {
$router -> use ( '/' , function ( $response , $request ) {
2023-03-09 21:04:43 +00:00
if ( $request -> hasHeader ( 'Origin' ))
2022-07-06 20:42:26 +00:00
$response -> setHeader ( 'Access-Control-Allow-Credentials' , 'true' );
2023-03-09 21:04:43 +00:00
});
$router -> use ( '/' , function ( $response , $request ) {
if ( $request -> getMethod () === 'OPTIONS' ) {
2022-07-06 20:42:26 +00:00
$response -> setHeader ( 'Access-Control-Allow-Headers' , 'Authorization' );
$response -> setHeader ( 'Access-Control-Allow-Methods' , 'OPTIONS, GET, POST, DELETE' );
return 204 ;
}
});
2023-10-31 16:10:32 +00:00
$router -> use ( '/' , function ( $response , $request ) use ( $db , $cfg ) {
2022-07-06 20:42:26 +00:00
$auth = $request -> getHeaderLine ( 'Authorization' );
2023-03-09 21:04:43 +00:00
if ( empty ( $auth )) {
$mszAuth = ( string ) $request -> getCookie ( 'msz_auth' );
if ( ! empty ( $mszAuth ))
$auth = 'Misuzu ' . $mszAuth ;
}
2022-07-06 20:42:26 +00:00
if ( ! empty ( $auth )) {
$authParts = explode ( ' ' , $auth , 2 );
$authMethod = strval ( $authParts [ 0 ] ? ? '' );
$authToken = strval ( $authParts [ 1 ] ? ? '' );
2023-10-31 16:10:32 +00:00
$authClients = $cfg -> getArray ( 'auth:clients' );
2022-07-06 20:42:26 +00:00
foreach ( $authClients as $client ) {
$client = new $client ;
if ( $client -> getName () !== $authMethod )
continue ;
$authUserId = $client -> verifyToken ( $authToken );
break ;
}
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
if ( isset ( $authUserId ) && $authUserId > 0 )
User :: byId ( $db , $authUserId ) -> setActive ();
}
});
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
$router -> get ( '/eeprom.js' , function ( $response ) {
$response -> accelRedirect ( '/js/eeprom-v1.0.js' );
$response -> setContentType ( 'application/javascript; charset=utf-8' );
});
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
$router -> get ( '/stats.json' , function () use ( $db ) {
$fileCount = 0 ;
$userCount = 0 ;
$totalSize = 0 ;
$uniqueTypes = 0 ;
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
$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' );
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
if ( $uploadStats -> next ()) {
$fileCount = $uploadStats -> getInteger ( 0 );
$totalSize = $uploadStats -> getInteger ( 1 );
$uniqueTypes = $uploadStats -> getInteger ( 2 );
2020-05-08 22:53:21 +00:00
}
2022-07-06 20:42:26 +00:00
$userStats = $db -> query ( 'SELECT COUNT(`user_id`) AS `amount` FROM `prm_users` WHERE `user_restricted` IS NULL' );
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
if ( $userStats -> next ())
$userCount = $userStats -> getInteger ( 0 );
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
return [
'size' => $totalSize ,
'files' => $fileCount ,
'types' => $uniqueTypes ,
'members' => $userCount ,
];
});
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
$router -> get ( '/' , function ( $response ) {
$response -> accelRedirect ( '/index.html' );
$response -> setContentType ( 'text/html; charset=utf-8' );
});
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
$router -> post ( '/uploads' , function ( $response , $request ) use ( $db ) {
if ( ! $request -> isFormContent ())
return 400 ;
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
$content = $request -> getContent ();
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
try {
$appInfo = Application :: byId ( $db , ( int ) $content -> getParam ( 'src' , FILTER_VALIDATE_INT ));
} catch ( ApplicationNotFoundException $ex ) {
return 404 ;
}
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
if ( ! User :: hasActive ())
return 401 ;
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
$userInfo = User :: active ();
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
if ( $userInfo -> isRestricted ())
return 403 ;
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
try {
$file = $content -> getUploadedFile ( 'file' );
} catch ( \RuntimeException $ex ) {
return 400 ;
}
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
$maxFileSize = $appInfo -> getSizeLimit ();
if ( $appInfo -> allowSizeMultiplier ())
$maxFileSize *= $userInfo -> getSizeMultiplier ();
2022-07-05 19:07:24 +00:00
2022-07-06 20:42:26 +00:00
$localFile = $file -> getLocalFileName ();
$fileSize = filesize ( $localFile );
2022-07-05 19:07:24 +00:00
2022-07-06 20:42:26 +00:00
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 ;
}
2022-07-05 19:07:24 +00:00
2022-07-06 20:42:26 +00:00
$hash = hash_file ( 'sha256' , $localFile );
$fileInfo = Upload :: byHash ( $db , $hash );
2022-07-05 19:07:24 +00:00
2022-07-06 20:42:26 +00:00
if ( $fileInfo !== null ) {
if ( $fileInfo -> isDMCA ())
return 451 ;
2022-07-05 19:07:24 +00:00
2022-07-06 20:42:26 +00:00
if ( $fileInfo -> getUserId () !== $userInfo -> getId ()
|| $fileInfo -> getApplicationId () !== $appInfo -> getId ())
unset ( $fileInfo );
}
2022-07-05 19:07:24 +00:00
2022-07-06 20:42:26 +00:00
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 ) {
2023-10-31 16:19:58 +00:00
\Sentry\captureException ( $ex );
2022-07-06 20:42:26 +00:00
return 500 ;
}
2022-07-05 19:07:24 +00:00
2022-07-06 20:42:26 +00:00
try {
$file -> moveTo ( $fileInfo -> getPath ());
} catch ( \RuntimeException $ex ) {
2023-10-31 16:19:58 +00:00
\Sentry\captureException ( $ex );
2022-07-06 20:42:26 +00:00
return 500 ;
}
}
2022-07-05 19:07:24 +00:00
2022-07-06 20:42:26 +00:00
$response -> setStatusCode ( 201 );
$response -> setHeader ( 'Content-Type' , 'application/json; charset=utf-8' );
return $fileInfo ;
});
2022-07-05 19:07:24 +00:00
2022-07-06 20:42:26 +00:00
$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 ;
}
2022-07-05 19:07:24 +00:00
2022-07-06 20:42:26 +00:00
if ( $uploadInfo -> isDMCA ()) {
$response -> setContent ( 'File is unavailable for copyright reasons.' );
return 451 ;
}
2022-07-05 19:07:24 +00:00
2022-07-06 20:42:26 +00:00
if ( $uploadInfo -> isDeleted () || $uploadInfo -> hasExpired ()) {
$response -> setContent ( 'File not found.' );
return 404 ;
}
2022-07-05 19:07:24 +00:00
2022-07-06 20:42:26 +00:00
if ( ! User :: hasActive ())
return 401 ;
2022-07-05 19:07:24 +00:00
2022-07-06 20:42:26 +00:00
if ( User :: active () -> isRestricted () || User :: active () -> getId () !== $uploadInfo -> getUserId ())
return 403 ;
2020-05-12 15:19:18 +00:00
2022-07-06 20:42:26 +00:00
$uploadInfo -> delete ( $db , false );
return 204 ;
});
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
$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' ;
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
if ( $fileExt !== '' && $fileExt !== 't' && $fileExt !== 'json' )
return 404 ;
2022-07-06 16:58:40 +00:00
2022-07-06 20:42:26 +00:00
try {
$uploadInfo = Upload :: byId ( $db , $fileId );
} catch ( UploadNotFoundException $ex ) {
$response -> setContent ( 'File not found.' );
return 404 ;
}
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
if ( $isJson )
return $uploadInfo ;
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
if ( $uploadInfo -> isDMCA ()) {
$response -> setContent ( 'File is unavailable for copyright reasons.' );
return 451 ;
}
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
if ( $uploadInfo -> isDeleted () || $uploadInfo -> hasExpired ()) {
$response -> setContent ( 'File not found.' );
return 404 ;
}
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
if ( ! is_file ( $uploadInfo -> getPath ())) {
$response -> setContent ( 'Data is missing.' );
return 404 ;
}
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
if ( ! $isThumbnail ) {
$uploadInfo -> bumpAccess ( $db );
$uploadInfo -> bumpExpiry ( $db );
}
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
$fileName = $uploadInfo -> getName ();
$contentType = $uploadInfo -> getType ();
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
if ( $contentType === 'application/octet-stream' || str_starts_with ( $contentType , 'text/' ))
$contentType = 'text/plain' ;
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
$sourceDir = basename ( $isThumbnail ? PRM_THUMBS : PRM_UPLOADS );
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
if ( $isThumbnail && $uploadInfo -> supportsThumbnail ()) {
if ( ! is_file ( $uploadInfo -> getThumbPath ()))
$uploadInfo -> createThumbnail ();
$contentType = 'image/jpeg' ;
$fileName = pathinfo ( $fileName , PATHINFO_FILENAME ) . '-thumb.jpg' ;
}
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
$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' )),
]);
});
2020-05-08 22:53:21 +00:00
}
2022-07-06 20:42:26 +00:00
} else {
$router -> use ( '/' , function ( $response , $request ) {
if ( $request -> getMethod () === 'OPTIONS' ) {
$response -> setHeader ( 'Access-Control-Allow-Methods' , 'OPTIONS, GET' );
return 204 ;
}
});
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
$router -> get ( '/:filename' , function ( $response , $request , $fileName ) use ( $db ) {
$pathInfo = pathinfo ( $fileName );
$fileId = $pathInfo [ 'filename' ];
$fileExt = $pathInfo [ 'extension' ] ? ? '' ;
$isThumbnail = $fileExt === 't' ;
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
if ( $fileExt !== '' && $fileExt !== 't' )
return 404 ;
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
try {
$uploadInfo = Upload :: byId ( $db , $fileId );
} catch ( UploadNotFoundException $ex ) {
$response -> setContent ( 'File not found.' );
return 404 ;
}
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
if ( $uploadInfo -> isDMCA ()) {
$response -> setContent ( 'File is unavailable for copyright reasons.' );
return 451 ;
}
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
if ( $uploadInfo -> isDeleted () || $uploadInfo -> hasExpired ()) {
$response -> setContent ( 'File not found.' );
return 404 ;
2020-05-08 22:53:21 +00:00
}
2022-07-06 20:42:26 +00:00
if ( ! is_file ( $uploadInfo -> getPath ())) {
$response -> setContent ( 'Data is missing.' );
return 404 ;
}
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
if ( ! $isThumbnail ) {
$uploadInfo -> bumpAccess ( $db );
$uploadInfo -> bumpExpiry ( $db );
2020-05-08 22:53:21 +00:00
}
2022-07-06 20:42:26 +00:00
$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' ;
2020-05-08 22:53:21 +00:00
}
2022-07-06 20:42:26 +00:00
$response -> accelRedirect ( sprintf ( '/%s/%s' , $sourceDir , $uploadInfo -> getId ()));
$response -> setContentType ( $contentType );
$response -> setFileName ( addslashes ( $fileName ));
});
2020-05-08 22:53:21 +00:00
}
2022-07-06 20:42:26 +00:00
$router -> dispatch ();