Version 5.0.0 beta 1

This commit is contained in:
Neo committed 2025-12-19 16:27:35 -08:00
1 parent 25ddeb65d6
commit 15c7beabc5
6736 files changed
+627902 -497943

No files matched your search

+75 -67
View File
@@ -12,39 +12,47 @@
namespace IPS\Http;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use IPS\File;
use IPS\Log;
use IPS\Output;
use function count;
use function defined;
use function strlen;
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* HTTP Ranges Request/Response Class
*/
class _Ranges extends Response
class Ranges extends Response
{
/**
* @brief File object
*/
public $file = NULL;
public ?File $file = NULL;
/**
* @brief Is this a range request?
*/
public $ranged = FALSE;
public bool $ranged = FALSE;
/**
* Constructor
*
* @param \IPS\File $file File object we are sending
* @param int $throttle Throttle speed (kb/sec)
* @param File $file File object we are sending
* @param int $throttle Throttle speed (kb/sec)
* @return void
*/
public function __construct( $file, $throttle=0 )
public function __construct( File $file, int $throttle=0 )
{
/* Send some headers */
\IPS\Output::i()->sendHeader( 'Accept-Ranges: bytes' );
\IPS\Output::i()->sendHeader( "Content-Transfer-Encoding: binary" );
Output::i()->sendHeader( 'Accept-Ranges: bytes' );
Output::i()->sendHeader( "Content-Transfer-Encoding: binary" );
/* We will need the file size */
$filesize = $file->filesize();
@@ -52,12 +60,12 @@ class _Ranges extends Response
$range = 0;
$start_range = 0;
\IPS\Log::debug( "Range response initiated", 'ranges' );
Log::debug( "Range response initiated", 'ranges' );
/* Was this a ranged request? */
if( isset( $_SERVER['HTTP_RANGE'] ) )
{
\IPS\Log::debug( "This is a range request: " . $_SERVER['HTTP_RANGE'], 'ranges' );
Log::debug( "This is a range request: " . $_SERVER['HTTP_RANGE'], 'ranges' );
/* Get the range */
list( $a, $range ) = explode( "=", $_SERVER['HTTP_RANGE'] );
@@ -66,39 +74,39 @@ class _Ranges extends Response
$ranges = explode( ",", $range );
/* Did we only find one range? */
if( \count($ranges) == 1 )
if( count($ranges) == 1 )
{
\IPS\Log::debug( "Only one range detected", 'ranges' );
Log::debug( "Only one range detected", 'ranges' );
/* Get the actual range start and end */
list( $start_range, $end_range ) = explode( "-", $range );
/* If there is no start, we want the last x bytes */
if( \strlen( $start_range ) == 0 )
if( strlen( $start_range ) == 0 )
{
$size = $filesize - 1;
$length = $end_range + 1;
$length = (int) $end_range + 1;
}
/* Or did we want the first x bytes? */
else if( \strlen( $end_range ) == 0 )
else if( strlen( $end_range ) == 0 )
{
$size = $filesize - 1;
$length = $size - $start_range + 1;
$length = $size - (int) $start_range + 1;
}
/* Or a specific range with start and end specified */
else
{
$size = $end_range;
$length = $end_range - $start_range + 1;
$length = (int) $end_range - (int) $start_range + 1;
}
/* If the range is invalid we need to send a 416 header and let the client know the valid range */
if( $start_range > $size OR $end_range > $size )
{
\IPS\Log::debug( "Invalid range, responding with 416 header. Requested {$start_range} - {$end_range} out of {$size}", 'ranges' );
Log::debug( "Invalid range, responding with 416 header. Requested {$start_range} - {$end_range} out of {$size}", 'ranges' );
\IPS\Output::i()->sendStatusCodeHeader( 416 );
\IPS\Output::i()->sendHeader( "Content-Range: */{$filesize}" );
Output::i()->sendStatusCodeHeader( 416 );
Output::i()->sendHeader( "Content-Range: */{$filesize}" );
return;
}
/* Otherwise send the 206 header */
@@ -110,16 +118,16 @@ class _Ranges extends Response
$size = $filesize - 1;
}
\IPS\Log::debug( "Sending requested range with 206 header: {$start_range} - {$size} out of {$filesize}", 'ranges' );
Log::debug( "Sending requested range with 206 header: {$start_range} - {$size} out of {$filesize}", 'ranges' );
\IPS\Output::i()->sendStatusCodeHeader( 206 );
\IPS\Output::i()->sendHeader( "Content-Range: bytes {$start_range}-{$size}/{$filesize}" );
Output::i()->sendStatusCodeHeader( 206 );
Output::i()->sendHeader( "Content-Range: bytes {$start_range}-{$size}/{$filesize}" );
}
/* A couple more headers for good measure */
if( !ini_get('zlib.output_compression') OR ini_get('zlib.output_compression') == 'off' )
{
\IPS\Output::i()->sendHeader( "Content-Length: " . $filesize );
Output::i()->sendHeader( "Content-Length: " . $filesize );
}
}
/* Or did they get froggy and request multiple ranges? */
@@ -130,37 +138,37 @@ class _Ranges extends Response
foreach( $ranges as $arange )
{
\IPS\Log::debug( "Multiple ranges: {$start_range} - {$end_range}", 'ranges' );
/* Get the actual range start and end */
/* Get the actual range start and end */
list( $start_range, $end_range ) = explode( "-", $arange );
Log::debug( "Multiple ranges: {$start_range} - {$end_range}", 'ranges' );
/* If there is no start, we want the last x bytes */
if( \strlen( $start_range ) == 0 )
if( strlen( $start_range ) == 0 )
{
$size = $filesize - 1;
$length = $end_range + 1;
$length = (int) $end_range + 1;
}
/* Or did we want the first x bytes? */
else if( \strlen( $end_range ) == 0 )
else if( strlen( $end_range ) == 0 )
{
$size = $filesize - 1;
$length = $size - $start_range + 1;
$length = $size -(int) $start_range + 1;
}
/* Or a specific range with start and end specified */
else
{
$size = $end_range;
$length = $end_range - $start_range + 1;
$length = (int) $end_range - (int) $start_range + 1;
}
/* If the range is invalid we need to send a 416 header and let the client know the valid range */
if( $start_range > $size OR $end_range > $size )
{
\IPS\Log::debug( "This range out of multiple ranges was invalid", 'ranges' );
Log::debug( "This range out of multiple ranges was invalid", 'ranges' );
\IPS\Output::i()->sendStatusCodeHeader( 416 );
\IPS\Output::i()->sendHeader( "Content-Range: */{$filesize}" );
Output::i()->sendStatusCodeHeader( 416 );
Output::i()->sendHeader( "Content-Range: */{$filesize}" );
return;
}
/* Otherwise store this range */
@@ -171,7 +179,7 @@ class _Ranges extends Response
}
/* Did we still only have one range? */
if( \count($the_responses) == 1 )
if( count($the_responses) == 1 )
{
$length = $the_responses[0][2];
$start_range = $the_responses[0][0];
@@ -179,40 +187,40 @@ class _Ranges extends Response
$the_responses = array();
\IPS\Log::debug( "One range out of multiple ranges to respond: {$start_range} - {$size} out of {$filesize}", 'ranges' );
Log::debug( "One range out of multiple ranges to respond: {$start_range} - {$size} out of {$filesize}", 'ranges' );
\IPS\Output::i()->sendStatusCodeHeader( 206 );
\IPS\Output::i()->sendHeader( "Content-Range: bytes {$start_range}-{$size}/{$filesize}" );
Output::i()->sendStatusCodeHeader( 206 );
Output::i()->sendHeader( "Content-Range: bytes {$start_range}-{$size}/{$filesize}" );
/* A couple more headers for good measure */
if( !ini_get('zlib.output_compression') OR ini_get('zlib.output_compression') == 'off' )
{
\IPS\Output::i()->sendHeader( "Content-Length: " . $filesize );
Output::i()->sendHeader( "Content-Length: " . $filesize );
}
}
/* Now we're working with something exciting */
else if( \count($the_responses) > 1 )
else if( count($the_responses) > 1 )
{
$content_length = 0;
\IPS\Log::debug( "Multiple range responses, using boundary", 'ranges' );
Log::debug( "Multiple range responses, using boundary", 'ranges' );
foreach( $the_responses as $part )
{
$content_length += \strlen( "\r\n--IPDOWNLOADSBOUNDARYMARKER\r\n" );
$content_length += \strlen( "Content-Type: " . \IPS\File::getMimeType( $file->originalFilename ) . "\r\n" );
$content_length += \strlen( "Content-Range: bytes {$part[0]}-{$part[1]}/{$filesize}\r\n\r\n" );
$content_length += strlen( "\r\n--IPDOWNLOADSBOUNDARYMARKER\r\n" );
$content_length += strlen( "Content-Type: " . File::getMimeType( $file->originalFilename ) . "\r\n" );
$content_length += strlen( "Content-Range: bytes {$part[0]}-{$part[1]}/{$filesize}\r\n\r\n" );
$content_length += $part[2];
}
$content_length += \strlen( "\r\n--IPDOWNLOADSBOUNDARYMARKER--\r\n" );
$content_length += strlen( "\r\n--IPDOWNLOADSBOUNDARYMARKER--\r\n" );
\IPS\Output::i()->sendStatusCodeHeader( 206 );
\IPS\Output::i()->sendHeader( "Content-Type: multipart/x-byteranges; boundary=IPDOWNLOADSBOUNDARYMARKER" );
Output::i()->sendStatusCodeHeader( 206 );
Output::i()->sendHeader( "Content-Type: multipart/x-byteranges; boundary=IPDOWNLOADSBOUNDARYMARKER" );
if( !ini_get('zlib.output_compression') OR ini_get('zlib.output_compression') == 'off' )
{
\IPS\Output::i()->sendHeader( "Content-Length: " . $content_length );
Output::i()->sendHeader( "Content-Length: " . $content_length );
}
}
}
@@ -220,25 +228,25 @@ class _Ranges extends Response
/* No range */
else
{
\IPS\Log::debug( "No range requested, sending entire file", 'ranges' );
Log::debug( "No range requested, sending entire file", 'ranges' );
$size = $filesize - 1;
\IPS\Output::i()->sendStatusCodeHeader( 200 );
Output::i()->sendStatusCodeHeader( 200 );
if( !ini_get('zlib.output_compression') OR ini_get('zlib.output_compression') == 'off' )
{
\IPS\Output::i()->sendHeader( "Content-Length: " . $filesize );
Output::i()->sendHeader( "Content-Length: " . $filesize );
}
\IPS\Output::i()->sendHeader( "Content-Range: bytes 0-{$size}/{$filesize}" );
Output::i()->sendHeader( "Content-Range: bytes 0-{$size}/{$filesize}" );
}
/* Not sending multiple ranges */
if( !isset( $the_responses ) OR !\count( $the_responses ) )
if( !isset( $the_responses ) OR !count( $the_responses ) )
{
\IPS\Output::i()->sendHeader( 'Content-Type: ' . \IPS\File::getMimeType( $file->originalFilename ) );
\IPS\Output::i()->sendHeader( 'Content-Disposition: ' . \IPS\Output::getContentDisposition( 'attachment', $file->originalFilename ) );
Output::i()->sendHeader( 'Content-Type: ' . File::getMimeType( $file->originalFilename ) );
Output::i()->sendHeader( 'Content-Disposition: ' . Output::getContentDisposition( 'attachment', $file->originalFilename ) );
}
/* Turn off output buffering if it is on */
@@ -251,7 +259,7 @@ class _Ranges extends Response
$throttle = ( $throttle > 0 ) ? ( $throttle * 1024 ) : NULL;
/* Do we have multiple ranges? */
if( isset( $the_responses ) AND \count($the_responses) )
if( isset( $the_responses ) AND count($the_responses) )
{
/* Loop over each range */
foreach( $the_responses as $part )
@@ -259,7 +267,7 @@ class _Ranges extends Response
$length = $part[2];
echo "\r\n--IPDOWNLOADSBOUNDARYMARKER\r\n";
echo "Content-Type: " . \IPS\File::getMimeType( $file->originalFilename ) . "\r\n";
echo "Content-Type: " . File::getMimeType( $file->originalFilename ) . "\r\n";
echo "Content-Range: bytes {$part[0]}-{$part[1]}/{$filesize}\r\n\r\n";
$file->printFile( $part[0], $part[2], $throttle );
@@ -279,7 +287,7 @@ class _Ranges extends Response
*
* @return bool
*/
public static function isStartOfFile()
public static function isStartOfFile(): bool
{
/* Was this a ranged request? */
if( isset( $_SERVER['HTTP_RANGE'] ) )
@@ -291,22 +299,22 @@ class _Ranges extends Response
$ranges = explode( ",", $range );
/* Did we only find one range? */
if( \count($ranges) == 1 )
if( count($ranges) == 1 )
{
/* Get the actual range start and end */
list( $start_range, $end_range ) = explode( "-", $range );
if( \strlen( $start_range ) == 0 )
if( strlen( $start_range ) == 0 )
{
return TRUE;
}
else if( \strlen( $end_range ) == 0 )
else if( strlen( $end_range ) == 0 )
{
return FALSE;
}
else
{
return ( $start_range == 0 ) ? TRUE : FALSE;
return ( $start_range == 0 );
}
}
/* Or did they get froggy and request multiple ranges? */
@@ -317,11 +325,11 @@ class _Ranges extends Response
/* Get the actual range start and end */
list( $start_range, $end_range ) = explode( "-", $arange );
if( \strlen( $start_range ) == 0 )
if( strlen( $start_range ) == 0 )
{
return TRUE;
}
else if( \strlen( $end_range ) == 0 )
else if( strlen( $end_range ) == 0 )
{
continue;
}