Version 4.1.2

This commit is contained in:
Neo committed 2025-12-19 05:32:19 -08:00
1 parent 3ba79295ee
commit 28bd025b35
1131 files changed
+63658 -20579

No files matched your search

+127 -12
View File
@@ -23,6 +23,13 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _Amazon extends \IPS\File
{
/**
* An array of ( configuration_id => array( ext, etx ) ) extensions that require gzip versions storing
* Looks up $storageExtensions of extension classes
*/
protected static $gzipExtensions = array();
/* !ACP Configuration */
/**
@@ -137,7 +144,7 @@ class _Amazon extends \IPS\File
{
return \IPS\Member::loggedIn()->language()->addToStack( 'filehandler_display_name', FALSE, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->addToStack('filehandler__Amazon'), $settings['bucket'] ) ) );
}
/* !File Handling */
/**
@@ -152,6 +159,42 @@ class _Amazon extends \IPS\File
parent::__construct( $configuration );
}
/**
* Fetch the gzip extensions specific for $this->configurationId
*
* @return array
*/
public function getGzipExtensions()
{
if ( $this->storageExtension and ! array_key_exists( $this->storageExtension, static::$gzipExtensions ) )
{
$bits = explode( '_', $this->storageExtension );
$class = '\IPS\\' . $bits[0] . '\extensions\core\FileStorage\\' . $bits[1];
if ( isset( $class::$storeGzipExtensions ) and is_array( $class::$storeGzipExtensions ) and count( $class::$storeGzipExtensions ) )
{
static::$gzipExtensions[ $this->storageExtension ] = $class::$storeGzipExtensions;
}
else
{
static::$gzipExtensions[ $this->storageExtension ] = array();
}
}
return $this->storageExtension ? static::$gzipExtensions[ $this->storageExtension ] : array();
}
/**
* AWS does not gzip content when serving it, so if we want gzip compressed JS and CSS, we need to store a copy ourselves.
*
* @return boolean
*/
public function needsGzipVersion()
{
/* Use filename and not originalFilename so only true .js and .css files are checked, and not renamed uploads */
return in_array( mb_substr( $this->filename, mb_strrpos( $this->filename, '.' ) + 1 ), $this->getGzipExtensions() );
}
/**
* Return the base URL
*
@@ -162,6 +205,22 @@ class _Amazon extends \IPS\File
return preg_replace( '#^http(s)?://#', '//', ( empty( $this->configuration['custom_url'] ) ) ? rtrim( static::buildBaseUrl( $this->configuration ), '/' ) : $this->configuration['custom_url'] );
}
/**
* Load File Data
*
* @return void
*/
public function load()
{
parent::load();
/* Change the public URL to the gzipped version if the browser supports it */
if ( $this->needsGzipVersion() and ( isset( $_SERVER['HTTP_ACCEPT_ENCODING'] ) and \strpos( $_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip' ) !== false ) )
{
$this->url = new \IPS\Http\Url( (string) $this->url . '.gz' );
}
}
/**
* Save File
*
@@ -169,20 +228,25 @@ class _Amazon extends \IPS\File
*/
public function save()
{
/* @link http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingMetadata.html regarding cleaning filenames */
if ( preg_match( '#[^a-zA-Z0-9!\-_\.\*\(\)]#', $this->filename ) )
{
$this->filename = uniqid() . '_' . preg_replace( '#[^a-zA-Z0-9!\-_\.\*\(\)]#', '', $this->filename );
}
$this->container = trim( $this->container, '/' );
$this->url = static::buildBaseUrl( $this->configuration ) . "{$this->container}/{$this->filename}";
$this->url = $this->baseUrl() . "/{$this->container}/{$this->filename}";
$response = static::makeRequest( "{$this->container}/{$this->filename}", 'PUT', $this->configuration, $this->configurationId, (string) $this->contents() );
if ( $response->httpResponseCode != 200 )
{
throw new \RuntimeException('COULD_NOT_SAVE_FILE');
}
/* Write the gzip version */
if ( $this->needsGzipVersion() )
{
$response = static::makeRequest( "{$this->container}/{$this->filename}.gz", 'PUT', $this->configuration, $this->configurationId, gzencode( (string) $this->contents() ) );
if ( $response->httpResponseCode != 200 )
{
throw new \RuntimeException('COULD_NOT_SAVE_FILE');
}
}
}
/**
@@ -213,6 +277,12 @@ class _Amazon extends \IPS\File
if ( $response->httpResponseCode == 204 )
{
/* Got a gzip version? */
if ( $this->needsGzipVersion() )
{
static::makeRequest( "{$this->container}/{$this->filename}.gz", 'DELETE', $this->configuration, $this->configurationId );
}
/* Ok */
return;
}
@@ -256,6 +326,25 @@ class _Amazon extends \IPS\File
}
}
/**
* Get filesize (in bytes)
*
* @return string
*/
public function filesize()
{
$this->container = trim( $this->container, '/' );
$response = static::makeRequest( "{$this->container}/{$this->filename}", 'HEAD', $this->configuration, $this->configurationId );
if ( $response->httpResponseCode != 200 OR !isset( $response->httpHeaders['Content-Length'] ) )
{
return parent::filesize();
}
return $response->httpHeaders['Content-Length'];
}
/* !Amazon Utility Methods */
/**
@@ -273,19 +362,45 @@ class _Amazon extends \IPS\File
{
/* Build a request */
$request = \IPS\Http\Url::external( static::buildBaseUrl( $configuration ) . $uri )->request();
/* When using virtual hosted–style buckets with SSL, the SSL wild card certificate only matches buckets that do not contain periods. To work around this, use HTTP or write your own certificate verification logic. @link http://docs.aws.amazon.com/AmazonS3/latest/dev/BucketRestrictions.html */
if ( \IPS\Request::i()->isSecure() and mb_strstr( $configuration['bucket'], '.' ) )
{
$request->sslCheck( FALSE );
}
$date = date('r');
$headers = array( 'Date' => $date, 'Content-Type' => \IPS\File::getMimeType( $uri ), 'Content-MD5' => base64_encode( md5( $content, TRUE ) ), 'x-amz-acl' => 'public-read' );
/* Make sure the file has the correct mime type, even if it is gzipped */
$mimeUri = ( mb_substr( $uri, -3 ) === '.gz' ) ? mb_substr( $uri, 0, -3 ) : $uri;
$headers = array( 'Date' => $date, 'Content-Type' => \IPS\File::getMimeType( $mimeUri ), 'Content-MD5' => base64_encode( md5( $content, TRUE ) ), 'x-amz-acl' => 'public-read' );
if ( $mimeUri !== $uri )
{
$headers['Content-Encoding'] = 'gzip';
}
if( mb_strtoupper( $verb ) === 'PUT' )
{
$headers['Content-Length'] = \strlen( $content );
}
$cacheSeconds = 3600;
$ext = mb_substr( $mimeUri, ( mb_strrpos( $mimeUri, '.' ) + 1 ) );
if ( preg_match( "/\." . preg_quote( $ext ) . "\.([a-zA-Z0-9]{32})\." . preg_quote( $ext ) . "$/", $mimeUri ) )
{
/* This is an obfuscated file, so it will change each time the file is updated so it is safe to cache for longer */
$cacheSeconds = 3600 * 24 * 365;
}
$headers['Cache-Control'] = 'max-age=' . $cacheSeconds;
$headers['Expires'] = date( 'r', time() + $cacheSeconds );
/* We need to strip query string parameters for the signature, but not always (e.g. a subresource such as ?acl needs to be included and multi-
object delete requests must include the query string params). Let the callee decide to do this or not. */
if( isset( $configuration['_strip_querystring'] ) AND $configuration['_strip_querystring'] === TRUE )
{
$uri = preg_replace( "/^(.*?)\?.*$/", "$1", $uri );
$uri = preg_replace( "/^(.*?)\?.*$/", "$1", $uri );
}
$string = implode( "\n", array(
+18 -6
View File
@@ -101,6 +101,24 @@ class _Database extends \IPS\File
$this->filename = urldecode( $this->filename );
$this->container = urldecode( $this->container );
$this->originalFilename = urldecode( $this->originalFilename );
/* The URL may have been stored as //site.com/applications/core/interface/file/index.php?id=1&salt=1111 in which case the previous load()
call wouldn't have loaded the data correctly, so we need to do it here now */
if( isset( $this->url->queryString['id'] ) AND isset( $this->url->queryString['salt'] ) )
{
try
{
$record = \IPS\Db::i()->select( '*', 'core_files', array( 'id=? AND salt=?', $this->url->queryString['id'], $this->url->queryString['salt'] ) )->first();
$this->contents = $record['contents'];
$this->filename = $record['filename'];
$this->originalFilename = $this->unObscureFilename( $this->filename );
$this->container = $record['container'];
}
catch( \UnderflowException $ex )
{
throw new \RuntimeException( 'FILE_DOES_NOT_EXIST' );
}
}
}
/**
@@ -135,12 +153,6 @@ class _Database extends \IPS\File
{
$salt = md5( uniqid() );
/* Make sure name doesn't have anything that may break URLs */
if ( preg_match( '#[^a-zA-Z0-9!\-_\.\*\(\)]#', $this->filename ) )
{
$this->filename = uniqid() . '_' . preg_replace( '#[^a-zA-Z0-9!\-_\.\*\(\)]#', '', $this->filename );
}
$id = \IPS\Db::i()->insert( 'core_files', array(
'filename' => $this->filename,
'salt' => $salt,
+200 -25
View File
@@ -179,26 +179,47 @@ abstract class _File
{
$class->container = $container;
}
/* Make sure images don't have HTML in the comments, which can cause be an XSS in older versions of IE */
$class->storageExtension = $storageExtension;
/* Image-specific stuff */
$ext = mb_substr( $filename, ( mb_strrpos( $filename, '.' ) + 1 ) );
if( $isSafe === FALSE and in_array( $ext, \IPS\Image::$imageExtensions ) )
if( in_array( $ext, \IPS\Image::$imageExtensions ) and ( !$isSafe or \IPS\Image::exifSupported() ) )
{
$contentToCheck = $data;
/* Get contents */
if( $data === NULL AND $filePath !== NULL )
{
$handle = fopen( $filePath, 'r');
$contentToCheck = fread( $handle, 2048 );
fclose( $handle );
$data = \file_get_contents( $filePath );
$filePath = NULL;
}
if( static::checkXssInFile( $contentToCheck ) )
/* Make sure images don't have HTML in the comments, which can cause be an XSS in older versions of IE */
$image = NULL;
if( !$isSafe and static::checkXssInFile( $data ) )
{
throw new \DomainException( "SECURITY_EXCEPTION_RAISED", 99 );
/* Try to just strip the EXIF */
$image = \IPS\Image::create( $data );
$image->resize( 100, 100 );
$data = (string) $image;
/* And if it still fails, throw an error */
if ( static::checkXssInFile( $data ) )
{
throw new \DomainException( "SECURITY_EXCEPTION_RAISED", 99 );
}
}
/* Correct orientation */
if ( \IPS\Image::exifSupported() )
{
$image = $image ?: \IPS\Image::create( $data );
if ( $image->hasBeenRotated )
{
$data = (string) \IPS\Image::create( $data );
}
}
}
/* Set the name */
$class->setFilename( $filename, $obscure );
@@ -448,6 +469,7 @@ abstract class _File
public static function get( $storageExtension, $url )
{
$class = static::getClass( $storageExtension, $url );
$class->storageExtension = $storageExtension;
$class->url = $url;
$class->load();
return $class;
@@ -464,7 +486,7 @@ abstract class _File
{
return static::getClass( $configurationId )->removeOrphanedFiles( $fileIndex, \IPS\Application::allExtensions( 'core', 'FileStorage', FALSE ) );
}
/**
* Determine if the change in configuration warrants a move process
*
@@ -510,9 +532,9 @@ abstract class _File
/**
* This is primarily a utility function to convert old style full URLs (http://site.com/uploads/monthly_04_2015/file.txt) into the new style (monthly_04_2015/file.txt)
* @deprecated 4.1.0
*
* return string|boolean URL (string) if URL needed repairing, or FALSE if it did not.
* @deprecated 4.1.0
* @return string|boolean URL (string) if URL needed repairing, or FALSE if it did not.
*/
public static function repairUrl( $url )
{
@@ -550,6 +572,11 @@ abstract class _File
*/
public $configurationId;
/**
* @brief Storage Extension (core_Theme, etc)
*/
public $storageExtension;
/**
* @brief Original Filename
*/
@@ -638,6 +665,77 @@ abstract class _File
}
}
/**
* Send file with byte-offset supported. Requires a path to a locally stored file. This method can be more efficient than
* getting the file contents and printing as the file contents do not need to be stored in memory, however files will need to
* be written to disk and removed, and the method is only useful if there is a way to retrieve a file without storing the
* contents in memory already.
*
* @param string $file Path to file
* @param int|null $start Start point to print from (for ranges)
* @param int|null $length Length to print to (for ranges)
* @param int|null $throttle Throttle speed
* @return void
*/
protected function sendFile( $file, $start=NULL, $length=NULL, $throttle=NULL )
{
/* Turn off output buffering if it is on */
while( ob_get_level() > 0 )
{
ob_end_clean();
}
if( $throttle === NULL AND !($start AND $length) AND function_exists('readfile') )
{
readfile( $file );
}
else
{
if( $fh = fopen( $file, 'rb' ) )
{
$read = ( $throttle !== NULL ) ? $throttle : 4096;
if( $start AND $length )
{
fseek( $fh, $start );
while( $length AND !feof( $fh ) )
{
if( $read > $length )
{
$read = $length;
}
echo fread( $fh, $read );
flush();
$length -= $read;
if( $throttle )
{
sleep( 1 );
}
}
}
else
{
while( ! feof( $fh ) )
{
echo fread( $fh, $read );
flush();
if( $throttle )
{
sleep( 1 );
}
}
}
fclose( $fh );
}
}
}
/**
* Get Contents
*
@@ -691,15 +789,30 @@ abstract class _File
public function setFilename( $filename, $obscure=TRUE )
{
$this->originalFilename = $filename;
/* Make sure name doesn't have anything that may break URLs */
if ( preg_match( '#[^a-zA-Z0-9!\-_\.\*\(\)\@]#', $filename ) )
{
$filename = uniqid() . '_' . preg_replace( '#[^a-zA-Z0-9!\-_\.\*\(\)\@]#', '', $filename );
}
if ( $obscure )
{
$this->filename = static::obscureFilename( $filename );
$filename = static::obscureFilename( $filename );
}
else
{
$this->filename = $filename;
$filename = $filename;
}
/* Most operating systems allow a max filename length of 255 bytes, so we should make sure we don't go over that */
if( \strlen( $filename ) > 200 )
{
/* If the filename is over 200 chars, grab the first 100 and the last 100 and concatenate with a dash - this should help ensure we retain the most useful info */
$filename = mb_substr( $filename, 0, 100 ) . '-' . mb_substr( $filename, -100 );
}
$this->filename = $filename;
}
/**
@@ -763,7 +876,6 @@ abstract class _File
if ( static::isFullyQualifiedUrl( $url ) )
{
$url = mb_substr( $url, mb_strlen( $this->baseUrl() ) + 1 );
}
$exploded = explode( '/', $url );
@@ -824,6 +936,36 @@ abstract class _File
return $this->container . '/' . $this->filename;
}
/**
* The configuration settings have been updated
*
* @return void
*/
public function settingsUpdated()
{
$settings = json_decode( \IPS\Settings::i()->upload_settings, TRUE );
foreach( $settings as $method => $id )
{
if ( $id == $this->configurationId )
{
$exploded = explode( '_', str_replace( 'filestorage__', '', $method ) );
$classname = "IPS\\{$exploded[0]}\\extensions\\core\\FileStorage\\{$exploded[1]}";
if ( method_exists( $classname, 'settingsUpdated' ) )
{
$classname::settingsUpdated( $this->configuration );
}
}
}
/* Clear guest page caches */
\IPS\Data\Cache::i()->clearAll();
/* Clear datastore */
\IPS\Data\Store::i()->clearAll();
}
/**
* Return the fully qualified URL
*
@@ -988,14 +1130,15 @@ abstract class _File
$data['attach_is_image'] = TRUE;
$data['attach_img_width'] = $dimensions[0];
$data['attach_img_height'] = $dimensions[1];
unset( $image );
$data['attach_thumb_location'] = (string) $this->thumbnail( 'core_Attachment', $thumbDims[0], $thumbDims[1] );
$data['attach_thumb_width'] = static::$thumbnailDimensions[0];
$data['attach_thumb_height'] = static::$thumbnailDimensions[1];
if ( $dimensions[0] > $thumbDims[0] or $dimensions[1] > $thumbDims[1] )
{
$data['attach_thumb_location'] = (string) $this->thumbnail( 'core_Attachment', $thumbDims[0], $thumbDims[1] );
$data['attach_thumb_width'] = static::$thumbnailDimensions[0];
$data['attach_thumb_height'] = static::$thumbnailDimensions[1];
$this->attachmentThumbnailUrl = $data['attach_thumb_location'];
$this->attachmentThumbnailUrl = $data['attach_thumb_location'];
}
}
catch ( \InvalidArgumentException $e ) { }
@@ -1259,6 +1402,38 @@ abstract class _File
return 'application/x-unknown'; // This, slightly unusual, type is needed to stop some browsers adding random extensions
}
/**
* Return a value pulled from php.ini in bytes
*
* @note This function is intended to normalize values for things like post_max_size which could be -1, 0, 8383900, 8M, 1G, etc.
* @return float
*/
public static function returnBytes( $size )
{
$size = trim( $size );
if( !$size OR $size == -1 )
{
return 0;
}
/* Get the last character, which may be 'm' or may be a number */
$last = mb_strtolower( $size[ \strlen( $size ) - 1 ] );
/* Adjust value as necessary - note that we do not break intentionally */
switch( $last )
{
case 'g':
$size *= 1024;
case 'm':
$size *= 1024;
case 'k':
$size *= 1024;
}
return (float) $size;
}
/* !Mime-Type Map */
+52 -69
View File
@@ -61,7 +61,11 @@ class _FileSystem extends \IPS\File
{
$values['custom_url'] = NULL;
}
if ( !$testDir )
{
throw new \DomainException( \IPS\Member::loggedIn()->language()->addToStack( 'dir_not_provided', FALSE ) );
}
if ( !is_dir( $testDir ) )
{
throw new \DomainException( \IPS\Member::loggedIn()->language()->addToStack( 'dir_does_not_exist', FALSE, array( 'sprintf' => array( $testDir ) ) ) );
@@ -229,61 +233,7 @@ class _FileSystem extends \IPS\File
{
$file = $this->configuration['dir'] . '/' . $this->container . '/' . $this->filename;
/* Turn off output buffering if it is on */
while( ob_get_level() > 0 )
{
ob_end_clean();
}
if( $throttle === NULL AND !($start AND $length) AND function_exists('readfile') )
{
readfile( $file );
}
else
{
if( $fh = fopen( $file, 'rb' ) )
{
$read = ( $throttle !== NULL ) ? $throttle : 4096;
if( $start AND $length )
{
fseek( $fh, $start );
while( $length AND !feof( $fh ) )
{
if( $read > $length )
{
$read = $length;
}
echo fread( $fh, $read );
flush();
$length -= $read;
if( $throttle )
{
sleep( 1 );
}
}
}
else
{
while( ! feof( $fh ) )
{
echo fread( $fh, $read );
flush();
if( $throttle )
{
sleep( 1 );
}
}
}
fclose( $fh );
}
}
$this->sendFile( $file, $start, $length, $throttle );
}
/**
@@ -318,6 +268,7 @@ class _FileSystem extends \IPS\File
*
* @return array
* @throws \DomainException
* @throws \RuntimeException
* @throws \InvalidArgumentException
*/
public function getImageDimensions()
@@ -329,6 +280,11 @@ class _FileSystem extends \IPS\File
$file = $this->configuration['dir'] . '/' . $this->container . '/' . $this->filename;
if ( ! file_exists( $file ) )
{
throw new \RuntimeException;
}
if( ( $image = getimagesize( $file ) ) === FALSE )
{
return parent::getImageDimensions();
@@ -345,12 +301,6 @@ class _FileSystem extends \IPS\File
*/
public function save()
{
/* Make sure name doesn't have anything that may break URLs */
if ( preg_match( '#[^a-zA-Z0-9!\-_\.\*\(\)]#', $this->filename ) )
{
$this->filename = uniqid() . '_' . preg_replace( '#[^a-zA-Z0-9!\-_\.\*\(\)]#', '', $this->filename );
}
/* Make the folder */
$folder = $this->configuration['dir'] . '/' . $this->getFolder();
@@ -380,6 +330,12 @@ class _FileSystem extends \IPS\File
/* Set the URL */
$this->url = new \IPS\Http\Url( $this->fullyQualifiedUrl( "{$this->container}/{$this->filename}" ) );
/* Clear zend opcache if enabled */
if( function_exists('opcache_reset') )
{
@opcache_reset();
}
}
/**
@@ -391,7 +347,15 @@ class _FileSystem extends \IPS\File
{
if( file_exists( "{$this->configuration['dir']}/{$this->container}/{$this->filename}" ) )
{
return @unlink( "{$this->configuration['dir']}/{$this->container}/{$this->filename}" );
$result = @unlink( "{$this->configuration['dir']}/{$this->container}/{$this->filename}" );
/* Clear zend opcache if enabled */
if( function_exists('opcache_reset') )
{
@opcache_reset();
}
return $result;
}
else
{
@@ -403,25 +367,38 @@ class _FileSystem extends \IPS\File
* Delete Container
*
* @param string $container Key
* @param bool $skipClear Skip clearing the opcache, used for recursive calls since the parent call will do it
* @return void
*/
public function deleteContainer( $container )
public function deleteContainer( $container, $skipClear = FALSE )
{
$dir = $this->configuration['dir'] . '/' . $container;
if ( is_dir( $dir ) )
{
foreach ( new \DirectoryIterator( $dir ) as $f )
{
if ( !$f->isDot() )
{
unlink( $f->getPathname() );
if( $f->isDir() )
{
$this->deleteContainer( $container . '/' . $f->getFilename(), TRUE );
}
else
{
unlink( $f->getPathname() );
}
}
}
$handle = opendir( $dir );
closedir ( $handle );
rmdir( $dir );
}
/* Clear zend opcache if enabled */
if( function_exists('opcache_reset') AND $skipClear === FALSE )
{
@opcache_reset();
}
}
/**
@@ -446,14 +423,20 @@ class _FileSystem extends \IPS\File
}
fclose( $file );
@unlink( $chunk );
/* Clear zend opcache if enabled */
if( function_exists('opcache_reset') )
{
@opcache_reset();
}
if ( $rename )
{
$newFileName = static::obscureFilename( $rename );
rename( "{$this->configuration['dir']}/{$folder}/{$filename}", "{$this->configuration['dir']}/{$folder}/{$newFileName}" );
return "{$folder}/{$newFileName}";
}
return "{$folder}/{$filename}";
}
+28 -12
View File
@@ -149,12 +149,6 @@ class _Ftp extends \IPS\File
*/
public function save()
{
/* Make sure name doesn't have anything that may break URLs */
if ( preg_match( '#[^a-zA-Z0-9!\-_\.\*\(\)]#', $this->filename ) )
{
$this->filename = uniqid() . '_' . preg_replace( '#[^a-zA-Z0-9!\-_\.\*\(\)]#', '', $this->filename );
}
/* Get contents */
$contents = $this->contents();
@@ -218,7 +212,7 @@ class _Ftp extends \IPS\File
/**
* @brief FTP Connection
*/
protected static $ftp = NULL;
protected $ftp = NULL;
/**
* Get FTP Connection
@@ -228,20 +222,20 @@ class _Ftp extends \IPS\File
*/
protected function ftp()
{
if ( static::$ftp === NULL )
if ( $this->ftp === NULL )
{
if ( $this->configuration['ftp_details']['protocol'] == 'sftp' )
{
static::$ftp = new \IPS\Ftp\Sftp( $this->configuration['ftp_details']['server'], $this->configuration['ftp_details']['un'], $this->configuration['ftp_details']['pw'], $this->configuration['ftp_details']['port'] );
$this->ftp = new \IPS\Ftp\Sftp( $this->configuration['ftp_details']['server'], $this->configuration['ftp_details']['un'], $this->configuration['ftp_details']['pw'], $this->configuration['ftp_details']['port'] );
}
else
{
static::$ftp = new \IPS\Ftp( $this->configuration['ftp_details']['server'], $this->configuration['ftp_details']['un'], $this->configuration['ftp_details']['pw'], $this->configuration['ftp_details']['port'], ( $this->configuration['ftp_details']['protocol'] == 'ssl_ftp' ) );
$this->ftp = new \IPS\Ftp( $this->configuration['ftp_details']['server'], $this->configuration['ftp_details']['un'], $this->configuration['ftp_details']['pw'], $this->configuration['ftp_details']['port'], ( $this->configuration['ftp_details']['protocol'] == 'ssl_ftp' ) );
}
static::$ftp->chdir( $this->configuration['ftp_details']['path'] );
$this->ftp->chdir( $this->configuration['ftp_details']['path'] );
}
return static::$ftp;
return $this->ftp;
}
/**
@@ -336,6 +330,28 @@ class _Ftp extends \IPS\File
$size = $this->ftp()->size( $this->filename );
$this->ftp()->chdir( $this->configuration['ftp_details']['path'] );
return $size ?: parent::filesize();
}
/**
* Print the contents of the file
*
* @param int|null $start Start point to print from (for ranges)
* @param int|null $length Length to print to (for ranges)
* @param int|null $throttle Throttle speed
* @return void
*/
public function printFile( $start=NULL, $length=NULL, $throttle=NULL )
{
/* Download the file locally */
$file = $this->ftp()->download( $this->configuration['ftp_details']['path'] . ( $this->container ? '/' . $this->container : '' ) . '/' . $this->filename, NULL, TRUE );
/* Send the file */
$this->sendFile( $file, $start, $length, $throttle );
/* Remove the temporary file */
@unlink( $file );
}
}