<!DOCTYPE html>
<html lang="en">
<head>
<title>Invision Community Update Extractor</title>
<style type='text/css'>@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-moz-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-ms-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:0 0}to{background-position:40px 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.ipsProgressBar{width:50%;margin:auto;height:20px;overflow:hidden;background:#9c9c9c;background:-moz-linear-gradient(top,rgba(156,156,156,1) 0,rgba(180,180,180,1) 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,rgba(156,156,156,1)),color-stop(100%,rgba(180,180,180,1)));background:-webkit-linear-gradient(top,rgba(156,156,156,1) 0,rgba(180,180,180,1) 100%);background:-o-linear-gradient(top,rgba(156,156,156,1) 0,rgba(180,180,180,1) 100%);background:-ms-linear-gradient(top,rgba(156,156,156,1) 0,rgba(180,180,180,1) 100%);background:linear-gradient(to bottom,rgba(156,156,156,1) 0,rgba(180,180,180,1) 100%);border-radius:4px;box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.ipsProgressBar_animated .ipsProgressBar_progress{background-color:#5490c0;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(.25,rgba(255,255,255,.15)),color-stop(.25,transparent),color-stop(.5,transparent),color-stop(.5,rgba(255,255,255,.15)),color-stop(.75,rgba(255,255,255,.15)),color-stop(.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:40px 40px;-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.ipsProgressBar_progress{float:left;width:0;height:100%;font-size:12px;color:#fff;text-align:center;text-shadow:0 -1px 0 rgba(0,0,0,.25);background:#5490c0;position:relative;padding-left:6px}.ipsProgressBar_progress[data-progress]:after{position:absolute;right:5px;top:0;line-height:32px;color:#fff;content:attr(data-progress);display:block;font-weight:700}</style>
</head>
<body style="margin:0">
<?php
/**
* @brief Update Extractor
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @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
<div class="ipsProgressBar ipsProgressBar_animated">
<div class="ipsProgressBar_progress" style="width: 100%;"></div>
</div>
<script type='text/javascript'>parent.location = parent.location + '&check=1';</script><noscript><a href='index.php' target='_parent'>Click here to continue</a></noscript>
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
<div class="ipsProgressBar ipsProgressBar_animated">
<div class="ipsProgressBar_progress" style="width: {$percentComplete}%;"></div>
</div>
<noscript><a href='{$url}' target='_parent'>Click here to continue</a></noscript>
<script type='text/javascript'>window.onload = function(){setTimeout(function(){window.location = '{$url}';}, 50);};</script>
HTML;
}
}
catch ( Exception $e )
{
echo "<script type='text/javascript'>parent.location = parent.location + '&fail=1';</script><noscript>An error occurred. Please visit the <a href='https://remoteservices.invisionpower.com/docs/client_area' target='_blank'>client area</a> to manually download the latest version. After uploading the files, <a href='index.php' target='_parent'>continue to the upgrader</a>.</noscript>";
exit;
}
?>
</body>
</html>