2019-11-20 17:46:47 +00:00
< ? php
namespace App\Controllers ;
2020-03-03 16:18:52 +00:00
use App\Database\Queries\UserQuery ;
2019-11-20 17:46:47 +00:00
use GuzzleHttp\Psr7\Stream ;
use Intervention\Image\Constraint ;
use Intervention\Image\ImageManagerStatic as Image ;
use League\Flysystem\FileNotFoundException ;
use League\Flysystem\Filesystem ;
use Psr\Http\Message\ResponseInterface as Response ;
use Psr\Http\Message\ServerRequestInterface as Request ;
use Slim\Exception\HttpBadRequestException ;
use Slim\Exception\HttpNotFoundException ;
use Slim\Exception\HttpUnauthorizedException ;
class MediaController extends Controller
{
/**
2019-11-21 17:00:47 +00:00
* @ param Request $request
* @ param Response $response
* @ param string $userCode
* @ param string $mediaCode
* @ param string | null $token
2019-11-20 17:49:31 +00:00
*
2019-11-21 17:00:47 +00:00
* @ return Response
2019-11-20 17:46:47 +00:00
* @ throws HttpNotFoundException
* @ throws \Twig\Error\LoaderError
* @ throws \Twig\Error\RuntimeError
* @ throws \Twig\Error\SyntaxError
* @ throws FileNotFoundException
2019-11-20 17:49:31 +00:00
*
2019-11-20 17:46:47 +00:00
*/
public function show ( Request $request , Response $response , string $userCode , string $mediaCode , string $token = null ) : Response
{
2020-04-07 16:43:34 +00:00
$media = $this -> getMedia ( $userCode , $mediaCode , true );
2019-11-20 17:46:47 +00:00
if ( ! $media || ( ! $media -> published && $this -> session -> get ( 'user_id' ) !== $media -> user_id && ! $this -> session -> get ( 'admin' , false ))) {
throw new HttpNotFoundException ( $request );
}
$filesystem = $this -> storage ;
if ( isBot ( $request -> getHeaderLine ( 'User-Agent' ))) {
return $this -> streamMedia ( $request , $response , $filesystem , $media );
2019-11-21 14:23:02 +00:00
}
try {
$media -> mimetype = $filesystem -> getMimetype ( $media -> storage_path );
$size = $filesystem -> getSize ( $media -> storage_path );
2019-11-20 17:46:47 +00:00
2019-11-21 14:23:02 +00:00
$type = explode ( '/' , $media -> mimetype )[ 0 ];
if ( $type === 'image' && ! isDisplayableImage ( $media -> mimetype )) {
$type = 'application' ;
$media -> mimetype = 'application/octet-stream' ;
}
if ( $type === 'text' ) {
if ( $size <= ( 200 * 1024 )) { // less than 200 KB
$media -> text = $filesystem -> read ( $media -> storage_path );
} else {
2019-11-20 17:46:47 +00:00
$type = 'application' ;
$media -> mimetype = 'application/octet-stream' ;
}
}
2019-11-21 14:23:02 +00:00
$media -> size = humanFileSize ( $size );
} catch ( FileNotFoundException $e ) {
throw new HttpNotFoundException ( $request );
2019-11-20 17:46:47 +00:00
}
2019-11-21 14:23:02 +00:00
2019-11-21 17:00:47 +00:00
return view () -> render ( $response , 'upload/public.twig' , [
2019-11-21 14:23:02 +00:00
'delete_token' => $token ,
2019-11-21 17:00:47 +00:00
'media' => $media ,
'type' => $type ,
'url' => urlFor ( " / { $userCode } / { $mediaCode } " ),
2020-03-01 16:03:07 +00:00
'copy_url_behavior' => $this -> getSetting ( 'copy_url_behavior' , 'off' ),
2019-11-21 17:00:47 +00:00
]);
2019-11-20 17:46:47 +00:00
}
/**
2019-11-21 17:00:47 +00:00
* @ param Request $request
* @ param Response $response
* @ param int $id
2019-11-20 17:49:31 +00:00
*
2019-11-21 17:00:47 +00:00
* @ return Response
2019-11-20 17:46:47 +00:00
* @ throws HttpNotFoundException
2019-11-21 14:23:33 +00:00
*
2019-11-21 17:00:47 +00:00
* @ throws FileNotFoundException
2019-11-20 17:46:47 +00:00
*/
public function getRawById ( Request $request , Response $response , int $id ) : Response
{
$media = $this -> database -> query ( 'SELECT * FROM `uploads` WHERE `id` = ? LIMIT 1' , $id ) -> fetch ();
if ( ! $media ) {
throw new HttpNotFoundException ( $request );
}
return $this -> streamMedia ( $request , $response , $this -> storage , $media );
}
/**
2019-11-21 17:00:47 +00:00
* @ param Request $request
* @ param Response $response
* @ param string $userCode
* @ param string $mediaCode
* @ param string | null $ext
2019-11-20 17:49:31 +00:00
*
2019-11-21 17:00:47 +00:00
* @ return Response
2019-11-20 17:46:47 +00:00
* @ throws HttpBadRequestException
* @ throws HttpNotFoundException
2019-11-21 14:23:33 +00:00
*
2019-11-21 17:00:47 +00:00
* @ throws FileNotFoundException
2019-11-20 17:46:47 +00:00
*/
public function getRaw ( Request $request , Response $response , string $userCode , string $mediaCode , ? string $ext = null ) : Response
{
2020-04-07 16:43:34 +00:00
$media = $this -> getMedia ( $userCode , $mediaCode , false );
2019-11-20 17:46:47 +00:00
if ( ! $media || ! $media -> published && $this -> session -> get ( 'user_id' ) !== $media -> user_id && ! $this -> session -> get ( 'admin' , false )) {
throw new HttpNotFoundException ( $request );
}
if ( $ext !== null && pathinfo ( $media -> filename , PATHINFO_EXTENSION ) !== $ext ) {
throw new HttpBadRequestException ( $request );
}
2020-02-25 16:09:36 +00:00
// If contains html, return it as text/plain
if ( strpos ( $this -> storage -> getMimetype ( $media -> storage_path ), 'text/htm' ) !== false ) {
$response = $this -> streamMedia ( $request , $response , $this -> storage , $media );
return $response -> withHeader ( 'Content-Type' , 'text/plain' );
}
2019-11-20 17:46:47 +00:00
return $this -> streamMedia ( $request , $response , $this -> storage , $media );
}
/**
2019-11-21 17:00:47 +00:00
* @ param Request $request
* @ param Response $response
* @ param string $userCode
* @ param string $mediaCode
2019-11-20 17:49:31 +00:00
*
2019-11-21 17:00:47 +00:00
* @ return Response
2019-11-20 17:46:47 +00:00
* @ throws HttpNotFoundException
2019-11-21 14:23:33 +00:00
*
2019-11-21 17:00:47 +00:00
* @ throws FileNotFoundException
2019-11-20 17:46:47 +00:00
*/
public function download ( Request $request , Response $response , string $userCode , string $mediaCode ) : Response
{
2020-04-07 16:43:34 +00:00
$media = $this -> getMedia ( $userCode , $mediaCode , false );
2019-11-20 17:46:47 +00:00
if ( ! $media || ! $media -> published && $this -> session -> get ( 'user_id' ) !== $media -> user_id && ! $this -> session -> get ( 'admin' , false )) {
throw new HttpNotFoundException ( $request );
}
2019-11-20 17:49:31 +00:00
2019-11-20 17:46:47 +00:00
return $this -> streamMedia ( $request , $response , $this -> storage , $media , 'attachment' );
}
/**
2019-11-21 17:00:47 +00:00
* @ param Request $request
* @ param Response $response
* @ param int $id
2019-11-20 17:49:31 +00:00
*
2019-11-21 17:00:47 +00:00
* @ return Response
2019-11-20 17:46:47 +00:00
* @ throws HttpNotFoundException
2019-11-20 17:49:31 +00:00
*
2019-11-20 17:46:47 +00:00
*/
public function togglePublish ( Request $request , Response $response , int $id ) : Response
{
if ( $this -> session -> get ( 'admin' )) {
$media = $this -> database -> query ( 'SELECT * FROM `uploads` WHERE `id` = ? LIMIT 1' , $id ) -> fetch ();
} else {
2019-11-21 17:00:47 +00:00
$media = $this -> database -> query ( 'SELECT * FROM `uploads` WHERE `id` = ? AND `user_id` = ? LIMIT 1' , [ $id , $this -> session -> get ( 'user_id' )]) -> fetch ();
2019-11-20 17:46:47 +00:00
}
if ( ! $media ) {
throw new HttpNotFoundException ( $request );
}
2019-11-21 17:00:47 +00:00
$this -> database -> query ( 'UPDATE `uploads` SET `published`=? WHERE `id`=?' , [ $media -> published ? 0 : 1 , $media -> id ]);
2019-11-20 17:46:47 +00:00
return $response ;
}
/**
2019-11-21 17:00:47 +00:00
* @ param Request $request
* @ param Response $response
* @ param int $id
2019-11-20 17:49:31 +00:00
*
2019-11-21 17:00:47 +00:00
* @ return Response
* @ throws HttpNotFoundException
2020-03-04 14:25:45 +00:00
* @ throws HttpUnauthorizedException
2019-11-20 17:46:47 +00:00
*/
public function delete ( Request $request , Response $response , int $id ) : Response
{
$media = $this -> database -> query ( 'SELECT * FROM `uploads` WHERE `id` = ? LIMIT 1' , $id ) -> fetch ();
if ( ! $media ) {
throw new HttpNotFoundException ( $request );
}
if ( $this -> session -> get ( 'admin' , false ) || $media -> user_id === $this -> session -> get ( 'user_id' )) {
2020-03-04 14:25:45 +00:00
$this -> deleteMedia ( $request , $media -> storage_path , $id , $media -> user_id );
2019-11-21 17:00:47 +00:00
$this -> logger -> info ( 'User ' . $this -> session -> get ( 'username' ) . ' deleted a media.' , [ $id ]);
2020-03-04 14:25:45 +00:00
2020-03-01 16:03:07 +00:00
if ( $media -> user_id === $this -> session -> get ( 'user_id' )) {
2020-03-04 14:25:45 +00:00
$user = make ( UserQuery :: class ) -> get ( $request , $media -> user_id , true );
2020-03-01 16:03:07 +00:00
$this -> setSessionQuotaInfo ( $user -> current_disk_quota , $user -> max_disk_quota );
}
2019-11-20 17:46:47 +00:00
} else {
throw new HttpUnauthorizedException ( $request );
}
2019-11-21 14:23:02 +00:00
if ( $request -> getMethod () === 'GET' ) {
return redirect ( $response , route ( 'home' ));
}
2019-11-20 17:46:47 +00:00
return $response ;
}
/**
2019-11-21 17:00:47 +00:00
* @ param Request $request
* @ param Response $response
* @ param string $userCode
* @ param string $mediaCode
* @ param string $token
2019-11-20 17:49:31 +00:00
*
2019-11-21 17:00:47 +00:00
* @ return Response
2019-11-20 17:46:47 +00:00
* @ throws HttpUnauthorizedException
2019-11-21 14:23:33 +00:00
*
2019-11-21 17:00:47 +00:00
* @ throws HttpNotFoundException
2019-11-20 17:46:47 +00:00
*/
public function deleteByToken ( Request $request , Response $response , string $userCode , string $mediaCode , string $token ) : Response
{
2020-04-07 16:43:34 +00:00
$media = $this -> getMedia ( $userCode , $mediaCode , false );
2019-11-20 17:46:47 +00:00
if ( ! $media ) {
throw new HttpNotFoundException ( $request );
}
$user = $this -> database -> query ( 'SELECT `id`, `active` FROM `users` WHERE `token` = ? LIMIT 1' , $token ) -> fetch ();
if ( ! $user ) {
$this -> session -> alert ( lang ( 'token_not_found' ), 'danger' );
2019-11-20 17:49:31 +00:00
2019-11-20 17:46:47 +00:00
return redirect ( $response , $request -> getHeaderLine ( 'Referer' ));
}
if ( ! $user -> active ) {
$this -> session -> alert ( lang ( 'account_disabled' ), 'danger' );
2019-11-20 17:49:31 +00:00
2019-11-20 17:46:47 +00:00
return redirect ( $response , $request -> getHeaderLine ( 'Referer' ));
}
if ( $this -> session -> get ( 'admin' , false ) || $user -> id === $media -> user_id ) {
2020-03-04 14:25:45 +00:00
$this -> deleteMedia ( $request , $media -> storage_path , $media -> mediaId , $user -> id );
2019-11-21 17:00:47 +00:00
$this -> logger -> info ( 'User ' . $user -> username . ' deleted a media via token.' , [ $media -> mediaId ]);
2019-11-20 17:46:47 +00:00
} else {
throw new HttpUnauthorizedException ( $request );
}
return redirect ( $response , route ( 'home' ));
}
/**
2019-11-21 17:00:47 +00:00
* @ param Request $request
* @ param string $storagePath
* @ param int $id
2019-11-20 17:49:31 +00:00
*
2020-03-04 14:25:45 +00:00
* @ param int $userId
* @ return void
2019-11-20 17:46:47 +00:00
* @ throws HttpNotFoundException
*/
2020-03-04 14:25:45 +00:00
protected function deleteMedia ( Request $request , string $storagePath , int $id , int $userId )
2019-11-20 17:46:47 +00:00
{
try {
2020-02-28 13:29:29 +00:00
$size = $this -> storage -> getSize ( $storagePath );
2019-11-20 17:46:47 +00:00
$this -> storage -> delete ( $storagePath );
2020-03-04 14:25:45 +00:00
$this -> updateUserQuota ( $request , $userId , $size , true );
2019-11-20 17:46:47 +00:00
} catch ( FileNotFoundException $e ) {
throw new HttpNotFoundException ( $request );
} finally {
$this -> database -> query ( 'DELETE FROM `uploads` WHERE `id` = ?' , $id );
}
}
/**
* @ param $userCode
* @ param $mediaCode
2019-11-20 17:49:31 +00:00
*
2020-04-07 16:43:34 +00:00
* @ param bool $withTags
2019-11-20 17:46:47 +00:00
* @ return mixed
*/
2020-04-07 16:43:34 +00:00
protected function getMedia ( $userCode , $mediaCode , $withTags = false )
2019-11-20 17:46:47 +00:00
{
$mediaCode = pathinfo ( $mediaCode )[ 'filename' ];
2020-04-07 16:43:34 +00:00
$media = $this -> database -> query ( 'SELECT `uploads`.*, `users`.*, `users`.`id` AS `userId`, `uploads`.`id` AS `mediaId` FROM `uploads` INNER JOIN `users` ON `uploads`.`user_id` = `users`.`id` WHERE `user_code` = ? AND `uploads`.`code` = ? LIMIT 1' , [
2019-11-20 17:46:47 +00:00
$userCode ,
$mediaCode ,
2019-11-21 17:00:47 +00:00
]) -> fetch ();
2020-04-07 16:43:34 +00:00
if ( ! $withTags || ! $media ) {
return $media ;
}
$media -> tags = [];
foreach ( $this -> database -> query ( 'SELECT `tags`.`id`, `tags`.`name` FROM `uploads_tags` INNER JOIN `tags` ON `uploads_tags`.`tag_id` = `tags`.`id` WHERE `uploads_tags`.`upload_id` = ?' , $media -> mediaId ) as $tag ) {
$media -> tags [ $tag -> id ] = $tag -> name ;
}
return $media ;
2019-11-20 17:46:47 +00:00
}
/**
2019-11-21 17:00:47 +00:00
* @ param Request $request
* @ param Response $response
* @ param Filesystem $storage
2019-11-20 17:46:47 +00:00
* @ param $media
2019-11-21 17:00:47 +00:00
* @ param string $disposition
2019-11-20 17:49:31 +00:00
*
2019-11-21 17:00:47 +00:00
* @ return Response
2019-11-20 17:46:47 +00:00
* @ throws FileNotFoundException
2019-11-20 17:49:31 +00:00
*
2019-11-20 17:46:47 +00:00
*/
protected function streamMedia ( Request $request , Response $response , Filesystem $storage , $media , string $disposition = 'inline' ) : Response
{
set_time_limit ( 0 );
$mime = $storage -> getMimetype ( $media -> storage_path );
if ( param ( $request , 'width' ) !== null && explode ( '/' , $mime )[ 0 ] === 'image' ) {
return $this -> makeThumbnail ( $storage , $media , param ( $request , 'width' ), param ( $request , 'height' ), $disposition );
} else {
$stream = new Stream ( $storage -> readStream ( $media -> storage_path ));
2019-11-21 17:00:47 +00:00
if ( ! in_array ( explode ( '/' , $mime )[ 0 ], [ 'image' , 'video' , 'audio' ]) || $disposition === 'attachment' ) {
2019-11-20 17:46:47 +00:00
return $response -> withHeader ( 'Content-Type' , $mime )
-> withHeader ( 'Content-Disposition' , $disposition . '; filename="' . $media -> filename . '"' )
-> withHeader ( 'Content-Length' , $stream -> getSize ())
-> withBody ( $stream );
}
if ( isset ( $request -> getServerParams ()[ 'HTTP_RANGE' ])) {
return $this -> handlePartialRequest ( $response , $stream , $request -> getServerParams ()[ 'HTTP_RANGE' ], $disposition , $media , $mime );
}
return $response -> withHeader ( 'Content-Type' , $mime )
-> withHeader ( 'Content-Length' , $stream -> getSize ())
-> withHeader ( 'Accept-Ranges' , 'bytes' )
-> withBody ( $stream );
}
}
/**
2019-11-21 17:00:47 +00:00
* @ param Filesystem $storage
2019-11-20 17:46:47 +00:00
* @ param $media
2019-11-21 17:00:47 +00:00
* @ param null $width
* @ param null $height
* @ param string $disposition
2019-11-20 17:49:31 +00:00
*
2019-11-21 17:00:47 +00:00
* @ return Response
2019-11-20 17:46:47 +00:00
* @ throws FileNotFoundException
2019-11-20 17:49:31 +00:00
*
2019-11-20 17:46:47 +00:00
*/
protected function makeThumbnail ( Filesystem $storage , $media , $width = null , $height = null , string $disposition = 'inline' )
{
return Image :: make ( $storage -> readStream ( $media -> storage_path ))
-> resize ( $width , $height , function ( Constraint $constraint ) {
$constraint -> aspectRatio ();
})
-> resizeCanvas ( $width , $height , 'center' )
-> psrResponse ( 'png' )
-> withHeader ( 'Content-Disposition' , $disposition . ';filename="scaled-' . pathinfo ( $media -> filename , PATHINFO_FILENAME ) . '.png"' );
}
/**
2019-11-21 17:00:47 +00:00
* @ param Response $response
* @ param Stream $stream
* @ param string $range
* @ param string $disposition
2019-11-20 17:46:47 +00:00
* @ param $media
* @ param $mime
2019-11-20 17:49:31 +00:00
*
2019-11-20 17:46:47 +00:00
* @ return Response
*/
protected function handlePartialRequest ( Response $response , Stream $stream , string $range , string $disposition , $media , $mime )
{
$end = $stream -> getSize () - 1 ;
2020-02-25 16:09:36 +00:00
[, $range ] = explode ( '=' , $range , 2 );
2019-11-20 17:46:47 +00:00
if ( strpos ( $range , ',' ) !== false ) {
return $response -> withHeader ( 'Content-Type' , $mime )
-> withHeader ( 'Content-Disposition' , $disposition . '; filename="' . $media -> filename . '"' )
-> withHeader ( 'Content-Length' , $stream -> getSize ())
-> withHeader ( 'Accept-Ranges' , 'bytes' )
-> withHeader ( 'Content-Range' , " 0, { $stream -> getSize () } " )
-> withStatus ( 416 )
-> withBody ( $stream );
}
if ( $range === '-' ) {
2019-11-21 14:23:33 +00:00
$start = $stream -> getSize () - ( int ) substr ( $range , 1 );
2019-11-20 17:46:47 +00:00
} else {
$range = explode ( '-' , $range );
2019-11-21 14:23:33 +00:00
$start = ( int ) $range [ 0 ];
$end = ( isset ( $range [ 1 ]) && is_numeric ( $range [ 1 ])) ? ( int ) $range [ 1 ] : $stream -> getSize ();
2019-11-20 17:46:47 +00:00
}
$end = ( $end > $stream -> getSize () - 1 ) ? $stream -> getSize () - 1 : $end ;
$stream -> seek ( $start );
header ( " Content-Type: $mime " );
header ( 'Content-Length: ' . ( $end - $start + 1 ));
header ( 'Accept-Ranges: bytes' );
header ( " Content-Range: bytes $start - $end / { $stream -> getSize () } " );
http_response_code ( 206 );
ob_end_clean ();
$buffer = 16348 ;
$readed = $start ;
while ( $readed < $end ) {
if ( $readed + $buffer > $end ) {
$buffer = $end - $readed + 1 ;
}
echo $stream -> read ( $buffer );
$readed += $buffer ;
}
exit ( 0 );
}
2019-11-20 17:49:31 +00:00
}