2020-05-08 22:53:21 +00:00
< ? php
namespace EEPROM ;
2023-11-07 21:33:46 +00:00
use RuntimeException ;
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-11-09 18:56:48 +00:00
function eepromUploadInfo ( Uploads\UploadInfo $uploadInfo ) : array {
global $eeprom ;
$uploadsCtx = $eeprom -> getUploadsContext ();
2023-11-07 22:05:14 +00:00
return [
'id' => $uploadInfo -> getId (),
2023-11-09 18:56:48 +00:00
'url' => $uploadsCtx -> getFileUrlV1 ( $uploadInfo ),
'urlf' => $uploadsCtx -> getFileUrlV1 ( $uploadInfo , true ),
'thumb' => $uploadsCtx -> getThumbnailUrlV1 ( $uploadInfo ),
2023-11-07 22:05:14 +00:00
'name' => $uploadInfo -> getName (),
2023-11-09 18:56:48 +00:00
'type' => $uploadInfo -> getMediaTypeString (),
'size' => $uploadInfo -> getDataSize (),
'user' => ( int ) $uploadInfo -> getUserId (),
'appl' => ( int ) $uploadInfo -> getAppId (),
'hash' => $uploadInfo -> getHashString (),
'created' => str_replace ( '+00:00' , 'Z' , $uploadInfo -> getCreatedAt () -> format ( \DateTime :: ATOM )),
'accessed' => $uploadInfo -> hasBeenAccessed () ? str_replace ( '+00:00' , 'Z' , $uploadInfo -> getAccessedAt () -> format ( \DateTime :: ATOM )) : null ,
'expires' => $uploadInfo -> hasExpired () ? str_replace ( '+00:00' , 'Z' , $uploadInfo -> getExpiredAt () -> format ( \DateTime :: ATOM )) : null ,
// These can never be reached, and in situation where they technically could it's because of an outdated local record
'deleted' => null ,
'dmca' => null ,
2023-11-07 22:05:14 +00:00
];
}
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 ) {
2023-11-09 18:56:48 +00:00
// this is illegal, don't do this
$userInfo = null ;
2022-07-06 20:42:26 +00:00
$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 ) {
2023-11-09 18:56:48 +00:00
global $userInfo , $eeprom ;
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 )
2023-11-09 18:56:48 +00:00
$userInfo = $eeprom -> getUsersContext () -> getUser ( $authUserId );
2022-07-06 20:42:26 +00:00
}
});
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 ) {
2023-11-09 18:56:48 +00:00
global $userInfo , $eeprom ;
2022-07-06 20:42:26 +00:00
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 {
2023-11-09 18:56:48 +00:00
$appInfo = $eeprom -> getAppsContext () -> getApp ( $content -> getParam ( 'src' , FILTER_VALIDATE_INT ));
2023-11-07 21:33:46 +00:00
} catch ( RuntimeException $ex ) {
2022-07-06 20:42:26 +00:00
return 404 ;
}
2020-05-08 22:53:21 +00:00
2023-11-09 18:56:48 +00:00
if ( $userInfo === null )
2022-07-06 20:42:26 +00:00
return 401 ;
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' );
2023-11-07 21:33:46 +00:00
} catch ( RuntimeException $ex ) {
2022-07-06 20:42:26 +00:00
return 400 ;
}
2020-05-08 22:53:21 +00:00
2023-11-09 18:56:48 +00:00
$maxFileSize = $appInfo -> getDataSizeLimit ();
2022-07-06 20:42:26 +00:00
if ( $appInfo -> allowSizeMultiplier ())
2023-11-09 18:56:48 +00:00
$maxFileSize *= $userInfo -> getDataSizeMultiplier ();
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
2023-11-09 18:56:48 +00:00
$uploadsCtx = $eeprom -> getUploadsContext ();
$uploadsData = $uploadsCtx -> getUploadsData ();
2022-07-06 20:42:26 +00:00
$hash = hash_file ( 'sha256' , $localFile );
2023-11-07 21:33:46 +00:00
// this is stupid: dmca status is stored as a file record rather than in a separate table requiring this hack ass garbage
2023-11-09 18:56:48 +00:00
$uploadInfo = $uploadsData -> getUpload ( appInfo : $appInfo , userInfo : $userInfo , hashString : $hash )
? ? $uploadsData -> getUpload ( hashString : $hash );
2022-07-05 19:07:24 +00:00
2023-11-07 22:05:14 +00:00
if ( $uploadInfo !== null ) {
2023-11-09 18:56:48 +00:00
if ( $uploadInfo -> isCopyrightTakedown ())
2022-07-06 20:42:26 +00:00
return 451 ;
2022-07-05 19:07:24 +00:00
2023-11-07 22:05:14 +00:00
if ( $uploadInfo -> getUserId () !== $userInfo -> getId ()
2023-11-09 18:56:48 +00:00
|| $uploadInfo -> getAppId () !== $appInfo -> getId ())
2023-11-07 22:05:14 +00:00
unset ( $uploadInfo );
2022-07-06 20:42:26 +00:00
}
2022-07-05 19:07:24 +00:00
2023-11-09 18:56:48 +00:00
if ( empty ( $uploadInfo )) {
$uploadInfo = $uploadsData -> createUpload (
$appInfo , $userInfo , $_SERVER [ 'REMOTE_ADDR' ],
$file -> getSuggestedFileName (), mime_content_type ( $localFile ),
$fileSize , $hash , $appInfo -> getBumpAmount (), true
2023-11-07 21:33:46 +00:00
);
2023-11-09 18:56:48 +00:00
$filePath = $uploadsCtx -> getFileDataPath ( $uploadInfo );
$file -> moveTo ( $filePath );
} else {
$filePath = $uploadsCtx -> getFileDataPath ( $uploadInfo );
if ( $uploadInfo -> isDeleted ())
$uploadsData -> restoreUpload ( $uploadInfo );
2023-11-07 21:33:46 +00:00
2023-11-09 18:56:48 +00:00
$uploadsData -> bumpUploadExpires ( $uploadInfo );
2022-07-06 20:42:26 +00:00
}
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' );
2023-11-07 22:05:14 +00:00
return eepromUploadInfo ( $uploadInfo );
2022-07-06 20:42:26 +00:00
});
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 ) {
2023-11-09 18:56:48 +00:00
global $userInfo , $eeprom ;
if ( $userInfo === null )
return 401 ;
$uploadsData = $eeprom -> getUploadsContext () -> getUploadsData ();
$uploadInfo = $uploadsData -> getUpload ( uploadId : $fileId );
if ( $uploadInfo === null ) {
2022-07-06 20:42:26 +00:00
$response -> setContent ( 'File not found.' );
return 404 ;
}
2022-07-05 19:07:24 +00:00
2023-11-09 18:56:48 +00:00
if ( $uploadInfo -> isCopyrightTakedown ()) {
2022-07-06 20:42:26 +00:00
$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
2023-11-09 18:56:48 +00:00
if ( $userInfo -> isRestricted () || $userInfo -> getId () !== $uploadInfo -> getUserId ())
2022-07-06 20:42:26 +00:00
return 403 ;
2020-05-12 15:19:18 +00:00
2023-11-09 18:56:48 +00:00
$uploadsData -> deleteUpload ( $uploadInfo );
2022-07-06 20:42:26 +00:00
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 ) {
2023-11-09 18:56:48 +00:00
global $eeprom ;
2022-07-06 20:42:26 +00:00
$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
2023-11-09 18:56:48 +00:00
$uploadsCtx = $eeprom -> getUploadsContext ();
$uploadsData = $uploadsCtx -> getUploadsData ();
$uploadInfo = $uploadsData -> getUpload ( uploadId : $fileId );
if ( $uploadInfo === null ) {
2022-07-06 20:42:26 +00:00
$response -> setContent ( 'File not found.' );
return 404 ;
}
2020-05-08 22:53:21 +00:00
2023-11-09 18:56:48 +00:00
if ( $uploadInfo -> isCopyrightTakedown ()) {
2022-07-06 20:42:26 +00:00
$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
2023-11-09 18:56:48 +00:00
if ( $isJson )
return eepromUploadInfo ( $uploadInfo );
$filePath = $uploadsCtx -> getFileDataPath ( $uploadInfo );
if ( ! is_file ( $filePath )) {
2022-07-06 20:42:26 +00:00
$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 ) {
2023-11-09 18:56:48 +00:00
$uploadsData -> bumpUploadAccess ( $uploadInfo );
$uploadsData -> bumpUploadExpires ( $uploadInfo );
2022-07-06 20:42:26 +00:00
}
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
$fileName = $uploadInfo -> getName ();
2023-11-09 18:56:48 +00:00
$contentType = $uploadInfo -> getMediaTypeString ();
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
2023-11-09 18:56:48 +00:00
if ( $isThumbnail ) {
if ( ! $uploadsCtx -> supportsThumbnailing ( $uploadInfo ))
return 404 ;
2020-05-08 22:53:21 +00:00
2022-07-06 20:42:26 +00:00
$contentType = 'image/jpeg' ;
2023-11-09 18:56:48 +00:00
$accelRedirectPath = $uploadsCtx -> getThumbnailDataRedirectPathOrCreate ( $uploadInfo );
2022-07-06 20:42:26 +00:00
$fileName = pathinfo ( $fileName , PATHINFO_FILENAME ) . '-thumb.jpg' ;
2023-11-09 18:56:48 +00:00
} else {
$accelRedirectPath = $uploadsCtx -> getFileDataRedirectPath ( $uploadInfo );
2022-07-06 20:42:26 +00:00
}
2020-05-08 22:53:21 +00:00
2023-11-09 18:56:48 +00:00
$response -> accelRedirect ( $accelRedirectPath );
2022-07-06 20:42:26 +00:00
$response -> setContentType ( $contentType );
$response -> setFileName ( addslashes ( $fileName ));
});
} 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 ) {
2023-11-09 18:56:48 +00:00
global $eeprom ;
2022-07-06 20:42:26 +00:00
$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
2023-11-09 18:56:48 +00:00
$uploadsCtx = $eeprom -> getUploadsContext ();
$uploadsData = $uploadsCtx -> getUploadsData ();
$uploadInfo = $uploadsData -> getUpload ( uploadId : $fileId );
if ( $uploadInfo === null ) {
2022-07-06 20:42:26 +00:00
$response -> setContent ( 'File not found.' );
return 404 ;
}
2020-05-08 22:53:21 +00:00
2023-11-09 18:56:48 +00:00
if ( $uploadInfo -> isCopyrightTakedown ()) {
2022-07-06 20:42:26 +00:00
$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
}
2023-11-09 18:56:48 +00:00
$filePath = $uploadsCtx -> getFileDataPath ( $uploadInfo );
if ( ! is_file ( $filePath )) {
2022-07-06 20:42:26 +00:00
$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 ) {
2023-11-09 18:56:48 +00:00
$uploadsData -> bumpUploadAccess ( $uploadInfo );
$uploadsData -> bumpUploadExpires ( $uploadInfo );
2020-05-08 22:53:21 +00:00
}
2022-07-06 20:42:26 +00:00
$fileName = $uploadInfo -> getName ();
2023-11-09 18:56:48 +00:00
$contentType = $uploadInfo -> getMediaTypeString ();
2022-07-06 20:42:26 +00:00
if ( $contentType === 'application/octet-stream' || str_starts_with ( $contentType , 'text/' ))
$contentType = 'text/plain' ;
2023-11-09 18:56:48 +00:00
if ( $isThumbnail ) {
if ( ! $uploadsCtx -> supportsThumbnailing ( $uploadInfo ))
return 404 ;
2022-07-06 20:42:26 +00:00
$contentType = 'image/jpeg' ;
2023-11-09 18:56:48 +00:00
$accelRedirectPath = $uploadsCtx -> getThumbnailDataRedirectPathOrCreate ( $uploadInfo );
2022-07-06 20:42:26 +00:00
$fileName = pathinfo ( $fileName , PATHINFO_FILENAME ) . '-thumb.jpg' ;
2023-11-09 18:56:48 +00:00
} else {
$accelRedirectPath = $uploadsCtx -> getFileDataRedirectPath ( $uploadInfo );
2020-05-08 22:53:21 +00:00
}
2023-11-09 18:56:48 +00:00
$response -> accelRedirect ( $accelRedirectPath );
2022-07-06 20:42:26 +00:00
$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 ();