";
DupArchiveMiniExpander::init("DupArchive_Installer_Extractor::log");
try {
$candidates = glob('*_archive.daf');
$numCandidates = count($candidates);
if($numCandidates == 0) {
die('Error. No .daf files found in this directory. Put one in the directory then browse to the extractor again.');
} else if($numCandidates > 1) {
die('Error. More than one .daf file found in this directory. Put exactly one in the directory then browse to the extractor again.');
}
$archivePath = dirname(__FILE__)."/{$candidates[0]}";
$archiveName = basename($archivePath);
$installerName = 'installer-backup.php';
$installerPath = dirname(__FILE__)."/{$installerName}";
DupArchiveMiniExpander::expandItems($archivePath, $installerName, dirname(__FILE__));
if(!file_exists($installerPath)) {
// Backup installer isn't there so look for full name
$installerPath = str_replace('_archive.daf', '_installer.php', $archivePath);
$installerName = basename($installerPath);
DupArchiveMiniExpander::expandItems($archivePath, $installerName, dirname(__FILE__));
if(!file_exists($installerPath)) {
die("Error. Installer was not found within {$archivePath}");
}
}
echo "SUCCCESSFUL EXTRACT.
";
echo "ARCHIVE: {$archiveName}
";
echo "INSTALLER: {$installerName}
";
$installerUrl = '//'.$_SERVER['HTTP_HOST'].dirname($_SERVER['SCRIPT_NAME'])."/{$installerName}";
die("Launch Installer");
} catch (Exception $ex) {
self::log("Error expanding installer subdirectory:".$ex->getMessage());
throw $ex;
}
}
/**
* Attempts to set the 'dup-installer' directory permissions
*
* @return null
*/
private function fixInstallerPerms()
{
$file_perms = substr(sprintf('%o', fileperms(__FILE__)), -4);
$file_perms = octdec($file_perms);
//$dir_perms = substr(sprintf('%o', fileperms(dirname(__FILE__))), -4);
// No longer using existing directory permissions since that can cause problems. Just set it to 755
$dir_perms = '755';
$dir_perms = octdec($dir_perms);
$installer_dir_path = $this->installerContentsPath;
$this->setPerms($installer_dir_path, $dir_perms, false);
$this->setPerms($installer_dir_path, $file_perms, true);
}
/**
* Set the permissions of a given directory and optionally all files
*
* @param string $directory The full path to the directory where perms will be set
* @param string $perms The given permission sets to use such as '0755'
* @param string $do_files Also set the permissions of all the files in the directory
*
* @return null
*/
private function setPerms($directory, $perms, $do_files)
{
if (!$do_files) {
// If setting a directory hiearchy be sure to include the base directory
$this->setPermsOnItem($directory, $perms);
}
$item_names = array_diff(scandir($directory), array('.', '..'));
foreach ($item_names as $item_name) {
$path = "$directory/$item_name";
if (($do_files && is_file($path)) || (!$do_files && !is_file($path))) {
$this->setPermsOnItem($path, $perms);
}
}
}
/**
* Set the permissions of a single directory or file
*
* @param string $path The full path to the directory or file where perms will be set
* @param string $perms The given permission sets to use such as '0755'
*
* @return bool Returns true if the permission was properly set
*/
private function setPermsOnItem($path, $perms)
{
$result = @chmod($path, $perms);
$perms_display = decoct($perms);
if ($result === false) {
self::log("Couldn't set permissions of $path to {$perms_display}
");
} else {
self::log("Set permissions of $path to {$perms_display}
");
}
return $result;
}
/**
* Logs a string to the dup-installer-bootlog__[HASH].txt file
*
* @param string $s The string to log to the log file
*
* @return null
*/
public static function log($s)
{
$timestamp = date('M j H:i:s');
//file_put_contents(self::BOOTSTRAP_LOG, "$timestamp $s\n", FILE_APPEND);
echo "{$s}
";
}
/**
* Attempts to get the archive file path
*
* @return string The full path to the archive file
*/
private function getArchiveFilePath()
{
$archive_filename = self::ARCHIVE_FILENAME;
if (isset($_GET['archive'])) {
$archive_filename = $_GET['archive'];
}
$archive_filepath = str_replace("\\", '/', dirname(__FILE__) . '/' . $archive_filename);
return $archive_filepath;
}
/**
* Checks to see if a string starts with specific characters
*
* @return bool Returns true if the string starts with a specific format
*/
private function startsWith($haystack, $needle)
{
return $needle === "" || strrpos($haystack, $needle, - strlen($haystack)) !== false;
}
/**
* Checks to see if the server supports issuing commands to shell_exex
*
* @return bool Returns true shell_exec can be ran on this server
*/
public function hasShellExec()
{
$cmds = array('shell_exec', 'escapeshellarg', 'escapeshellcmd', 'extension_loaded');
//Function disabled at server level
if (array_intersect($cmds, array_map('trim', explode(',', @ini_get('disable_functions'))))) return false;
//Suhosin: http://www.hardened-php.net/suhosin/
//Will cause PHP to silently fail
if (extension_loaded('suhosin')) {
$suhosin_ini = @ini_get("suhosin.executor.func.blacklist");
if (array_intersect($cmds, array_map('trim', explode(',', $suhosin_ini)))) return false;
}
// Can we issue a simple echo command?
if (!@shell_exec('echo duplicator')) return false;
return true;
}
}
$installerExtractor = new DupArchive_Installer_Extractor();
$installerExtractor->run();
//---------- DUPARCHIVE MINI EXPANDER ------------------------
class DupArchiveHeaderMiniU
{
const MaxStandardHeaderFieldLength = 128;
public static function readStandardHeaderField($archiveHandle, $ename)
{
$expectedStart = "<{$ename}>";
$expectedEnd = "{$ename}>";
$startingElement = fread($archiveHandle, strlen($expectedStart));
if($startingElement !== $expectedStart) {
throw new Exception("Invalid starting element. Was expecting {$expectedStart} but got {$startingElement}");
}
return stream_get_line($archiveHandle, self::MaxStandardHeaderFieldLength, $expectedEnd);
}
}
class DupArchiveMiniItemHeaderType
{
const None = 0;
const File = 1;
const Directory = 2;
const Glob = 3;
}
class DupArchiveMiniFileHeader
{
public $fileSize;
public $mtime;
public $permissions;
public $hash;
public $relativePathLength;
public $relativePath;
static function readFromArchive($archiveHandle)
{
$instance = new DupArchiveMiniFileHeader();
$instance->fileSize = DupArchiveHeaderMiniU::readStandardHeaderField($archiveHandle, 'FS');
$instance->mtime = DupArchiveHeaderMiniU::readStandardHeaderField($archiveHandle, 'MT');
$instance->permissions = DupArchiveHeaderMiniU::readStandardHeaderField($archiveHandle, 'P');
$instance->hash = DupArchiveHeaderMiniU::readStandardHeaderField($archiveHandle, 'HA');
$instance->relativePathLength = DupArchiveHeaderMiniU::readStandardHeaderField($archiveHandle, 'RPL');
// Skip
fread($archiveHandle, 5);
// Skip the #F!
//fread($archiveHandle, 3);
// Skip the
fread($archiveHandle, 4);
return $instance;
}
}
class DupArchiveMiniDirectoryHeader
{
public $mtime;
public $permissions;
public $relativePathLength;
public $relativePath;
// const MaxHeaderSize = 8192;
// const MaxStandardHeaderFieldLength = 128;
static function readFromArchive($archiveHandle)
{
$instance = new DupArchiveMiniDirectoryHeader();
$instance->mtime = DupArchiveHeaderMiniU::readStandardHeaderField($archiveHandle, 'MT');
$instance->permissions = DupArchiveHeaderMiniU::readStandardHeaderField($archiveHandle, 'P');
$instance->relativePathLength = DupArchiveHeaderMiniU::readStandardHeaderField($archiveHandle, 'RPL');
// Skip the
fread($archiveHandle, 5);
// Skip the
fread($archiveHandle, 4);
return $instance;
}
}
class DupArchiveMiniGlobHeader //extends HeaderBase
{
public $originalSize;
public $storedSize;
public $hash;
// const MaxHeaderSize = 255;
public static function readFromArchive($archiveHandle, $skipGlob)
{
$instance = new DupArchiveMiniGlobHeader();
// DupArchiveUtil::log('Reading glob starting at ' . ftell($archiveHandle));
$startElement = fread($archiveHandle, 3);
//if ($marker != '?G#') {
if ($startElement != '') {
throw new Exception("Invalid glob header marker found {$startElement}. location:" . ftell($archiveHandle));
}
$instance->originalSize = DupArchiveHeaderMiniU::readStandardHeaderField($archiveHandle, 'OS');
$instance->storedSize = DupArchiveHeaderMiniU::readStandardHeaderField($archiveHandle, 'SS');
$instance->hash = DupArchiveHeaderMiniU::readStandardHeaderField($archiveHandle, 'HA');
// Skip the
fread($archiveHandle, 4);
if ($skipGlob) {
// DupProSnapLibIOU::fseek($archiveHandle, $instance->storedSize, SEEK_CUR);
if(fseek($archiveHandle, $instance->storedSize, SEEK_CUR) === -1)
{
throw new Exception("Can't fseek when skipping glob at location:".ftell($archiveHandle));
}
}
return $instance;
}
}
class DupArchiveMiniHeader
{
public $version;
public $isCompressed;
// const MaxHeaderSize = 50;
private function __construct()
{
// Prevent instantiation
}
public static function readFromArchive($archiveHandle)
{
$instance = new DupArchiveMiniHeader();
$startElement = fgets($archiveHandle, 4);
if ($startElement != '') {
throw new Exception("Invalid archive header marker found {$startElement}");
}
$instance->version = DupArchiveHeaderMiniU::readStandardHeaderField($archiveHandle, 'V');
$instance->isCompressed = DupArchiveHeaderMiniU::readStandardHeaderField($archiveHandle, 'C') == 'true' ? true : false;
// Skip the
fgets($archiveHandle, 5);
return $instance;
}
}
class DupArchiveMiniWriteInfo
{
public $archiveHandle = null;
public $currentFileHeader = null;
public $destDirectory = null;
public $directoryWriteCount = 0;
public $fileWriteCount = 0;
public $isCompressed = false;
public $enableWrite = false;
public function getCurrentDestFilePath()
{
if($this->destDirectory != null)
{
return "{$this->destDirectory}/{$this->currentFileHeader->relativePath}";
}
else
{
return null;
}
}
}
class DupArchiveMiniExpander
{
public static $loggingFunction = null;
public static function init($loggingFunction)
{
self::$loggingFunction = $loggingFunction;
}
public static function log($s, $flush=false)
{
if(self::$loggingFunction != null) {
call_user_func(self::$loggingFunction, "MINI EXPAND:$s", $flush);
}
}
public static function expandDirectory($archivePath, $relativePath, $destPath)
{
self::expandItems($archivePath, $relativePath, $destPath);
}
public static function expandItems($archivePath, $inclusionFilter, $destDirectory, $ignoreErrors = false)
{
$archiveHandle = fopen($archivePath, 'rb');
if ($archiveHandle === false) {
throw new Exception("Can’t open archive at $archivePath!");
}
$archiveHeader = DupArchiveMiniHeader::readFromArchive($archiveHandle);
$writeInfo = new DupArchiveMiniWriteInfo();
$writeInfo->destDirectory = $destDirectory;
$writeInfo->isCompressed = $archiveHeader->isCompressed;
$moreToRead = true;
while ($moreToRead) {
if ($writeInfo->currentFileHeader != null) {
try {
if (self::passesInclusionFilter($inclusionFilter, $writeInfo->currentFileHeader->relativePath)) {
self::writeToFile($archiveHandle, $writeInfo);
$writeInfo->fileWriteCount++;
}
else if($writeInfo->currentFileHeader->fileSize > 0) {
// self::log("skipping {$writeInfo->currentFileHeader->relativePath} since it doesn’t match the filter");
// Skip the contents since the it isn't a match
$dataSize = 0;
do {
$globHeader = DupArchiveMiniGlobHeader::readFromArchive($archiveHandle, true);
$dataSize += $globHeader->originalSize;
$moreGlobs = ($dataSize < $writeInfo->currentFileHeader->fileSize);
} while ($moreGlobs);
}
$writeInfo->currentFileHeader = null;
// Expand state taken care of within the write to file to ensure consistency
} catch (Exception $ex) {
if (!$ignoreErrors) {
throw $ex;
}
}
} else {
$headerType = self::getNextHeaderType($archiveHandle);
switch ($headerType) {
case DupArchiveMiniItemHeaderType::File:
//$writeInfo->currentFileHeader = DupArchiveMiniFileHeader::readFromArchive($archiveHandle, $inclusionFilter);
$writeInfo->currentFileHeader = DupArchiveMiniFileHeader::readFromArchive($archiveHandle);
break;
case DupArchiveMiniItemHeaderType::Directory:
$directoryHeader = DupArchiveMiniDirectoryHeader::readFromArchive($archiveHandle);
// self::log("considering $inclusionFilter and {$directoryHeader->relativePath}");
if (self::passesInclusionFilter($inclusionFilter, $directoryHeader->relativePath)) {
// self::log("passed");
$directory = "{$writeInfo->destDirectory}/{$directoryHeader->relativePath}";
// $mode = $directoryHeader->permissions;
// rodo handle this more elegantly @mkdir($directory, $directoryHeader->permissions, true);
@mkdir($directory, 0755, true);
$writeInfo->directoryWriteCount++;
}
else {
// self::log("didnt pass");
}
break;
case DupArchiveMiniItemHeaderType::None:
$moreToRead = false;
}
}
}
fclose($archiveHandle);
}
private static function getNextHeaderType($archiveHandle)
{
$retVal = DupArchiveMiniItemHeaderType::None;
$marker = fgets($archiveHandle, 4);
if (feof($archiveHandle) === false) {
switch ($marker) {
case '':
$retVal = DupArchiveMiniItemHeaderType::Directory;
break;
case '':
$retVal = DupArchiveMiniItemHeaderType::File;
break;
case '':
$retVal = DupArchiveMiniItemHeaderType::Glob;
break;
default:
throw new Exception("Invalid header marker {$marker}. Location:".ftell($archiveHandle));
}
}
return $retVal;
}
private static function writeToFile($archiveHandle, $writeInfo)
{
$destFilePath = $writeInfo->getCurrentDestFilePath();
if($writeInfo->currentFileHeader->fileSize > 0)
{
/* @var $writeInfo DupArchiveMiniWriteInfo */
$parentDir = dirname($destFilePath);
if (!file_exists($parentDir)) {
$r = @mkdir($parentDir, 0755, true);
if(!$r)
{
throw new Exception("Couldn't create {$parentDir}");
}
}
$destFileHandle = fopen($destFilePath, 'wb+');
if ($destFileHandle === false) {
throw new Exception("Couldn't open {$destFilePath} for writing.");
}
do {
self::appendGlobToFile($archiveHandle, $destFileHandle, $writeInfo);
$currentFileOffset = ftell($destFileHandle);
$moreGlobstoProcess = $currentFileOffset < $writeInfo->currentFileHeader->fileSize;
} while ($moreGlobstoProcess);
fclose($destFileHandle);
@chmod($destFilePath, 0644);
self::validateExpandedFile($writeInfo);
} else {
if(touch($destFilePath) === false) {
throw new Exception("Couldn't create $destFilePath");
}
@chmod($destFilePath, 0644);
}
}
private static function validateExpandedFile($writeInfo)
{
/* @var $writeInfo DupArchiveMiniWriteInfo */
if ($writeInfo->currentFileHeader->hash !== '00000000000000000000000000000000') {
$hash = hash_file('crc32b', $writeInfo->getCurrentDestFilePath());
if ($hash !== $writeInfo->currentFileHeader->hash) {
throw new Exception("MD5 validation fails for {$writeInfo->getCurrentDestFilePath()}");
}
}
}
// Assumption is that archive handle points to a glob header on this call
private static function appendGlobToFile($archiveHandle, $destFileHandle, $writeInfo)
{
/* @var $writeInfo DupArchiveMiniWriteInfo */
$globHeader = DupArchiveMiniGlobHeader::readFromArchive($archiveHandle, false);
$globContents = fread($archiveHandle, $globHeader->storedSize);
if ($globContents === false) {
throw new Exception("Error reading glob from {$writeInfo->getDestFilePath()}");
}
if ($writeInfo->isCompressed) {
$globContents = gzinflate($globContents);
}
DupProSnapLibIOU::fwrite($destFileHandle, $globContents);
}
private static function passesInclusionFilter($filter, $candidate)
{
return (substr($candidate, 0, strlen($filter)) == $filter);
}
}
?>