Invision Power Services, Inc. * @copyright (c) Invision Power Services, Inc. * @license https://www.invisioncommunity.com/legal/standards/ * @package Invision Community * @since 31 Jul 2017 */ // This file deliberately exists outside of the framework // and MUST NOT call init.php @ini_set('display_errors', 'off'); @require( "../../system/3rd_party/pclzip/pclzip.lib.php" ); if ( file_exists( "../../constants.php" ) ) { require "../../constants.php"; } foreach ( array( 'FOLDER_PERMISSION_NO_WRITE' => 0755, 'FILE_PERMISSION_NO_WRITE' => 0644, 'TEMP_DIRECTORY' => sys_get_temp_dir() ) as $k => $v ) { if ( !defined( $k ) ) { define( $k, $v ); } } /** * Extractor Class */ class Extractor { const NUMBER_PER_GO = 20; /** * @brief File */ private $file; /** * @brief Root Directory */ private $container; /** * @brief PclZip Object */ private $zip; /** * @brief Zip Contents */ private $contents; /** * @brief FTP Connection */ private $ftp; /** * @brief SSH Connection */ private $ssh; /** * @brief SFTP Connection */ private $sftp; /** * @brief SFTP Directory */ private $sftpDir; /** * Constructor * * @param string $file Zip File * @param string $container Root directory * @return void */ public function __construct( $file, $container ) { if ( !$file or !$container ) { throw new Exception; } $this->file = $file; $this->container = $container . '/'; } /** * Security Check * * @param string $key Security key * @return void */ public function securityCheck( $key ) { require "../../conf_global.php"; return $key === md5( $INFO['board_start'] . $_GET['file'] . $INFO['sql_pass'] ); } /** * Establish FTP/SSH connection * * @param array $value FTP/SSH Credentials * @return void */ public function connectToFtp( $value ) { if ( $value['protocol'] == 'sftp' ) { $this->ssh = ssh2_connect( $value['server'], $value['port'] ); if ( $this->ssh === FALSE ) { throw new Exception; } if ( !@ssh2_auth_password( $this->ssh, $value['un'], $value['pw'] ) ) { throw new Exception; } $this->sftp = @ssh2_sftp( $this->ssh ); if ( $this->sftp === FALSE ) { throw new Exception; } if ( $value['path'] and !@ssh2_sftp_stat( $this->sftp, $value['path'] ) ) { throw new Exception; } $this->sftpDir = ssh2_sftp_realpath( $this->sftp, $value['path'] ) . '/'; } else { if ( $value['protocol'] == 'ssl_ftp' ) { $this->ftp = @ftp_ssl_connect( $value['server'], $value['port'], 3 ); } else { $this->ftp = @ftp_connect( $value['server'], $value['port'], 3 ); } if ( $this->ftp === FALSE ) { throw new Exception; } if ( !@ftp_login( $this->ftp, $value['un'], $value['pw'] ) ) { throw new Exception; } if( ftp_nlist( $this->ftp, '.' ) === FALSE ) { @ftp_pasv( $this->ftp, true ); } if ( !@ftp_chdir( $this->ftp, $value['path'] ) ) { throw new Exception; } } } /** * Number of files * * @return int */ public function numberOfFiles() { $properties = $this->zip->properties(); return $properties['nb']; } /** * Extract the files * * @param int $offset Offset * @return int */ public function extract( $offset ) { $this->zip = new PclZip( $this->file ); $this->contents = $this->zip->listContent(); if ( !$this->contents ) { throw new Exception; } $done = 0; for ( $i = 0; $i < self::NUMBER_PER_GO; $i++ ) { $index = $offset + $i; if ( isset( $this->contents[ $index ] ) ) { $this->_extractFile( $index ); $done++; } else { return $done; } } return $done; } /** * Extract a particular file * * @param int $index File index in zip * @return void */ private function _extractFile( $index ) { /* Get the file */ $path = mb_substr( $this->contents[ $index ]['filename'], 0, mb_strlen( $this->container ) ) === $this->container ? mb_substr( $this->contents[ $index ]['filename'], mb_strlen( $this->container ) ) : $this->contents[ $index ]['filename']; if ( !$path or mb_substr( $path, -1 ) === '/' or mb_substr( $path, 0, 1 ) === '.' or mb_substr( $path, 0, 9 ) === '__MACOSX/' ) { return; } /* Create a directory if needed */ $dir = dirname( $path ); $directories = array( $dir ); while ( $dir != '.' ) { $dir = dirname( $dir ); if ( $dir != '.' ) { $directories[] = $dir; } } foreach ( array_reverse( $directories ) as $dir ) { if ( !is_dir( "../../{$dir}" ) ) { if ( $this->sftp ) { @ssh2_sftp_mkdir( $this->sftp, $this->sftpDir . $dir ); } if ( $this->ftp ) { @ftp_mkdir( $this->ftp, $dir ); } else { @mkdir( "../../{$dir}", FOLDER_PERMISSION_NO_WRITE ); } } } /* Write contents */ $contents = @$this->zip->extractByIndex( $index, \PCLZIP_OPT_EXTRACT_AS_STRING ); $contents = $contents[0]['content']; if ( $this->sftp or $this->ftp ) { $tmpFile = tempnam( TEMP_DIRECTORY, 'IPS' ); \file_put_contents( $tmpFile, $contents ); if ( $this->sftp ) { if ( @ssh2_scp_send( $this->ssh, $tmpFile, $this->sftpDir . $path ) === FALSE ) { throw new \Exception; } } else { if ( @ftp_put( $this->ftp, $path, $tmpFile, FTP_BINARY ) === FALSE ) { throw new \Exception; } } @unlink( $tmpFile ); } else { /* Determine if the file exists (we'll want to know this later) */ $fileExists = file_exists( "../../{$path}" ); $fh = @\fopen( "../../{$path}", 'w+' ); if ( $fh === FALSE ) { $lastError = error_get_last(); throw new Exception( $lastError['message'] ); } if ( @\fwrite( $fh, $contents ) === FALSE ) { $lastError = error_get_last(); throw new Exception( $lastError['message'] ); } else { /* If the file existed before we started, we should clear it from opcache if opcache is enabled */ if( $fileExists ) { if ( function_exists( 'opcache_invalidate' ) ) { @opcache_invalidate( "../../{$path}" ); } } /* Otherwise, we should set file permissions on the file if it's brand new in case server defaults to something odd */ else { @chmod( "../../{$path}", FILE_PERMISSION_NO_WRITE ); } } @\fclose( $fh ); } } } /* ! Controller */ try { $extractor = new Extractor( isset( $_GET['file'] ) ? $_GET['file'] : NULL, isset( $_GET['container'] ) ? $_GET['container'] : '' ); /* Check this request came from the ACP */ if ( !$extractor->securityCheck( $_GET['key'] ) ) { throw new Exception; } /* Establish an FTP connection if necessary */ if ( $_GET['ftp'] ) { $extractor->connectToFtp( $_GET['ftp'] ); } /* Extract a batch of files */ $i = isset( $_GET['i'] ) ? $_GET['i'] : 0; $done = $extractor->extract( $i ); /* If we didn't extract anything, are we done? */ if ( !$done and $i >= $extractor->numberOfFiles() ) { @unlink( $_GET['file'] ); if ( function_exists( 'opcache_reset' ) ) { @opcache_reset(); } echo <<
HTML; } /* Nope, redirect to the next batch */ else { $newI = $i + $extractor::NUMBER_PER_GO; $percentComplete = round( 100 / $extractor->numberOfFiles() * $newI ); $properties = array( 'file' => $_GET['file'], 'container' => $_GET['container'], 'key' => $_GET['key'], 'ftp' => $_GET['ftp'], 'i' => $newI, ); $url = "extract.php?" . http_build_query( $properties, '', '&' ); echo <<
HTML; } } catch ( Exception $e ) { echo ""; exit; } ?>