archiveFileName = $archiveFileName; $this->archiveFileSize = $archiveFileSize; $this->installerDirName = $installerDirName; $this->packageHash = $packageHash; $this->secondaryHash = $secondaryHash; $this->version = $version; $this->setHTTPHeaders(); $this->targetRoot = BootstrapUtils::setSafePath(dirname(__FILE__)); $this->log('', true); $archive_filepath = $this->getArchiveFilePath(); $this->origDupInstFolder = $this->installerDirName; $this->targetDupInstFolder = filter_input(INPUT_GET, 'dup_folder', FILTER_SANITIZE_SPECIAL_CHARS, array( "options" => array( "default" => $this->installerDirName, ), 'flags' => FILTER_FLAG_STRIP_HIGH)); $this->isCustomDupFolder = $this->origDupInstFolder !== $this->targetDupInstFolder; $this->targetDupInst = $this->targetRoot . '/' . $this->targetDupInstFolder; $this->manualExtractFileName = 'dup-manual-extract__' . $this->packageHash; if ($this->isCustomDupFolder) { $this->extractionTmpFolder = $this->getTempDir($this->targetRoot); } else { $this->extractionTmpFolder = $this->targetRoot; } SecureCsrf::init($this->targetDupInst, $this->packageHash); $archiveActualSize = @file_exists($archive_filepath) ? @filesize($archive_filepath) : false; $archiveActualSize = ($archiveActualSize !== false) ? $archiveActualSize : 0; $this->hasZipArchive = class_exists('ZipArchive'); $this->archiveExpectedSize = (is_numeric($this->archiveFileSize) ? (int) $this->archiveFileSize : 0); $this->archiveActualSize = $archiveActualSize; if ($this->archiveExpectedSize > 0) { $this->archiveRatio = (((1.0) * $this->archiveActualSize) / $this->archiveExpectedSize) * 100; } else { $this->archiveRatio = 100; } } public static function initSetValues() { define('KB_IN_BYTES', 1024); define('MB_IN_BYTES', 1024 * KB_IN_BYTES); define('GB_IN_BYTES', 1024 * MB_IN_BYTES); define('DUPLICATOR_PRO_PHP_MAX_MEMORY', 4096 * MB_IN_BYTES); date_default_timezone_set('UTC'); // Some machines don’t have this set so just do it here. @ignore_user_abort(true); @set_time_limit(3600); if (BootstrapUtils::isIniValChangeable('memory_limit')) { @ini_set('memory_limit', DUPLICATOR_PRO_PHP_MAX_MEMORY); } if (BootstrapUtils::isIniValChangeable('max_input_time')) { @ini_set('max_input_time', '-1'); } if (BootstrapUtils::isIniValChangeable('pcre.backtrack_limit')) { @ini_set('pcre.backtrack_limit', PHP_INT_MAX); } if (BootstrapUtils::isIniValChangeable('default_socket_timeout')) { @ini_set('default_socket_timeout', 3600); } } public static function shellUnzipEnabled() { return (BootstrapUtils::getUnzipFilePath() !== false); } private function setHTTPHeaders() { header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0"); header("Cache-Control: post-check=0, pre-check=0", false); header("Pragma: no-cache"); } private function getTempDir($path) { $tempfile = tempnam($path, 'dup-installer_tmp_'); if (file_exists($tempfile)) { unlink($tempfile); mkdir($tempfile); if (is_dir($tempfile)) { return $tempfile; } } return false; } public function run() { date_default_timezone_set('UTC'); // Some machines don't have this set so just do it here $this->log('==DUPLICATOR INSTALLER BOOTSTRAP v' . $this->version . '=='); $this->log('----------------------------------------------------'); $this->log('Installer bootstrap start'); $archive_filepath = $this->getArchiveFilePath(); $this->errorMessage = ''; $is_installer_file_valid = true; if (preg_match('/_([a-z0-9]{7})[a-z0-9]+_[0-9]{6}([0-9]{8})_archive.(?:zip|daf)$/', $this->archiveFileName, $matches)) { $expected_package_hash = $matches[1] . '-' . $matches[2]; if ($this->packageHash != $expected_package_hash) { $is_installer_file_valid = false; $this->log("[ERROR] Installer and archive mismatch detected."); } } else { $this->log("[ERROR] Invalid archive file name."); $is_installer_file_valid = false; } if (false === $is_installer_file_valid) { $this->errorMessage = "Installer and archive mismatch detected. Ensure uncorrupted installer and matching archive are present."; return BootstrapView::VIEW_ERROR; } $extract_installer = true; $installerDirFound = ( file_exists($this->targetDupInst) && file_exists($this->targetDupInst . "/main.installer.php") && file_exists($this->targetDupInst . "/dup-archive__" . $this->packageHash . ".txt") ); $manualExtractFound = ( $installerDirFound && file_exists($this->targetDupInst . "/" . $this->manualExtractFileName) ); if (!$manualExtractFound && $this->archiveCheck() == false) { return BootstrapView::VIEW_ERROR; } if ($this->engineCheck() == false) { return BootstrapView::VIEW_ERROR; } if (!$this->passwordCheck()) { return BootstrapView::VIEW_PASSWORD; } if ($installerDirFound) { if (($extract_installer = filter_input(INPUT_GET, 'force-extract-installer', FILTER_VALIDATE_BOOLEAN))) { $this->log("Manual extract found with force extract installer get parametr"); } else { $this->log("Manual extract found so not going to extract " . $this->targetDupInstFolder . " dir"); } } else { $extract_installer = true; } if (file_exists($this->targetDupInst)) { $this->log("EXTRACT " . $this->targetDupInstFolder . " dir"); $hash_pattern = '[a-z0-9][a-z0-9][a-z0-9][a-z0-9][a-z0-9][a-z0-9][a-z0-9]-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]'; $file_patterns_with_hash_file = array( 'dup-archive__' . $hash_pattern . '.txt' => 'dup-archive__' . $this->packageHash . '.txt', 'dup-database__' . $hash_pattern . '.sql' => 'dup-database__' . $this->packageHash . '.sql', 'dup-installer-data__' . $hash_pattern . '.sql' => 'dup-installer-data__' . $this->packageHash . '.sql', 'dup-installer-log__' . $hash_pattern . '.txt' => 'dup-installer-log__' . $this->packageHash . '.txt', 'dup-scan__' . $hash_pattern . '.json' => 'dup-scan__' . $this->packageHash . '.json', 'dup-scanned-dirs__' . $hash_pattern . '.txt' => 'dup-scanned-dirs__' . $this->packageHash . '.txt', 'dup-scanned-files__' . $hash_pattern . '.txt' => 'dup-scanned-files__' . $this->packageHash . '.txt', ); foreach ($file_patterns_with_hash_file as $file_pattern => $hash_file) { $globs = glob($this->targetDupInst . '/' . $file_pattern); if (!empty($globs)) { foreach ($globs as $glob) { $file = basename($glob); if ($file != $hash_file) { if (unlink($glob)) { $this->log('Successfully deleted the file ' . $glob); } else { $this->errorMessage .= '[ERROR] Error deleting the file ' . $glob . ' Please manually delete it and try again.'; $this->log($this->errorMessage); } } } } } } if ($extract_installer) { try { $this->extractInstaller(); } catch (Exception $e) { $this->log("Extraction exception msg: " . $e->getMessage() . "\n" . $e->getTraceAsString()); return BootstrapView::VIEW_ERROR; } } else { $this->log("NOTICE: Didn't need to extract the installer."); } if ($this->isCustomDupFolder && file_exists($this->extractionTmpFolder)) { rmdir($this->extractionTmpFolder); } $config_files = glob($this->targetDupInst . '/dup-archive__*.txt'); $config_file_absolute_path = array_pop($config_files); if (!file_exists($config_file_absolute_path)) { $this->errorMessage = 'Archive config file not found in ' . $this->targetDupInstFolder . ' folder.

'; return BootstrapView::VIEW_ERROR; } $uri_start = BootstrapUtils::getCurrentUrl(false, false, 1); if (!file_exists($this->targetDupInst)) { $this->errorMessage = 'Can\'t extract installer directory. ' . 'See this FAQ item' . ' for details on how to resolve.'; return BootstrapView::VIEW_ERROR; } $bootloader_name = basename(__FILE__); $this->mainInstallerURL = $uri_start . '/' . $this->targetDupInstFolder . '/main.installer.php'; $this->archive = $archive_filepath; $this->bootloader = $bootloader_name; $this->fixInstallerPerms($this->mainInstallerURL); $this->setCsrfData(); $this->log("DONE: No detected errors so redirecting to the main installer. Main Installer URI = {$this->mainInstallerURL}"); return BootstrapView::VIEW_REDIRECT; } public function getErrorMessage() { return $this->errorMessage; } public function appendErrorMessage($message) { $this->errorMessage .= (strlen($message) ? " \n" : '') . $message; } public function isZip() { return (strcasecmp(pathinfo($this->archiveFileName, PATHINFO_EXTENSION), 'zip') === 0); } protected function isArchiveEncrypted() { static $isEncrypted = null; if ($isEncrypted === null) { if ($this->isZip()) { $isEncrypted = BootstrapUtils::isZipArchiveEncrypted($this->getArchiveFilePath(), 'main.installer.php'); } else { $isEncrypted = DupArchive::isEncrypted($this->getArchiveFilePath()); } } return $isEncrypted; } protected function passwordCheck() { if (!$this->isArchiveEncrypted()) { return true; } $this->log('ARCHIVE ENCRYPTED, PASSWORD CHECK'); $result = false; $password = (isset($_REQUEST[self::NAME_PWD]) ? BootstrapUtils::sanitizeNSCharsNewline($_REQUEST[self::NAME_PWD]) : ''); $passwordSend = (strlen($password) > 0 || (isset($_REQUEST[self::NAME_PWD_BUTTON]) && $_REQUEST[self::NAME_PWD_BUTTON] === 'secure')); if ($this->isZip()) { $zip = new ZipArchive(); if (($zipOpenRes = $zip->open($this->getArchiveFilePath())) !== true) { $message = "[ERROR] Couldn't open archive archive file with ZipArchive CODE[" . $zipOpenRes . "]"; $this->log($message); throw new Exception($message); } if (($stats = $zip->statName('main.installer.php', ZipArchive::FL_NODIR)) == false) { throw new Exception('Formatting archive error, cannot find the file main.installer.php'); } $zip->setPassword($password); if ($result = $zip->getFromIndex($stats['index'])) { $this->log('ZIP ARCHIVE PASSWORD OK '); } else { $this->log('ZIP ARCHIVE PASSWORD FAIL '); } $zip->close(); } else { if (($result = DupArchive::checkPassword($this->getArchiveFilePath(), $password))) { $this->log('DUP ARCHIVE PASSWORD OK '); } else { $this->log('DUP ARCHIVE PASSWORD FAIL '); } } if ($result) { $this->archivePwd = $password; } else { if ($passwordSend) { $this->appendErrorMessage('Invalid password'); } sleep(1); } return $result; } protected function archiveCheck() { $archive_filepath = $this->getArchiveFilePath(); $archiveExpectedEasy = BootstrapUtils::readableByteSize($this->archiveExpectedSize); $archiveActualEasy = BootstrapUtils::readableByteSize($this->archiveActualSize); if (!file_exists($archive_filepath)) { $this->log("[ERROR] Archive file not found!"); $this->errorMessage = "" . "Archive not found! The required archive file must be present in the 'Extraction Path' below. " . "When the archive file name was created " . "it was given a secure hashed file name. This file name must be the exact same " . "name as when it was created character for character. " . "Each archive file has a unique installer associated with it and must be used together. See the list below for more options:
" . "
" . "Extraction Path: {$this->targetRoot}/
"; return false; } if (strlen($this->archiveFileSize) > 0 && !self::checkInputValidInt($this->archiveFileSize)) { $no_of_bits = PHP_INT_SIZE * 8; $this->errorMessage = 'Current is a ' . $no_of_bits . '-bit SO. This archive is too large for ' . $no_of_bits . '-bit PHP.' . '
'; $this->log('[ERROR] ' . $this->errorMessage); $this->errorMessage .= 'Possibibles solutions:
'; $this->errorMessage .= '- Use the file filters to get your package lower to support this server or try the package on a Linux server.' . '
'; $this->errorMessage .= '- Perform a ' . 'Manual Extract Install' . '
'; switch ($no_of_bits == 32) { case 32: $this->errorMessage .= '- Ask your host to upgrade the server to 64-bit PHP or install on another system has 64-bit PHP' . '
'; break; case 64: $this->errorMessage .= '- Ask your host to upgrade the server to 128-bit PHP or install on another system has 128-bit PHP' . '
'; break; } if (self::isWindows()) { $this->errorMessage .= '- ' . 'Windows DupArchive extractor to extract all files from the archive.' . '
'; } return false; } if (($this->archiveRatio < 90) && ($this->archiveActualSize > 0) && ($this->archiveExpectedSize > 0)) { $this->log( "ERROR: The expected archive size should be around [{$archiveExpectedEasy}]. " . "The actual size is currently [{$archiveActualEasy}]." ); $this->log("ERROR: The archive file may not have fully been downloaded to the server"); $this->errorMessage = "Archive file size warning.
The expected archive size is [{$archiveExpectedEasy}]. " . "Currently the archive size is [{$archiveActualEasy}].
" . "The archive file may have not fully been uploaded to the server." . ""; return false; } return true; } protected function engineCheck() { if (!$this->isArchiveEncrypted()) { return true; } try { if ($this->isZip()) { if (version_compare(BootstrapUtils::getLibzipVersion(), '1.2.0', '<')) { ob_start(); ?> ZipArchive Error
This server is unable to decrypt the archive file (ZipArchive), Libzip version 1.2.0+ is required.
Current Libzip version: Please contact your host or server admin to update Libzip versuion. DupArchive Error
This server is unable to decrypt the archive file (DupArchive) without the PHP OpenSSL module.
Please contact your host or server admin to enable the OpenSSL module. errorMessage = $e->getMessage(); return false; } return true; } protected function extractInstaller() { $this->log("Ready to extract the installer"); $archive_filepath = $this->getArchiveFilePath(); $this->log("Checking permission of destination folder"); $destination = $this->targetRoot; if (!is_writable($destination)) { $this->log("destination folder for extraction is not writable"); if (BootstrapUtils::chmod($destination, 'u+rwx')) { $this->log("Permission of destination folder changed to u+rwx"); } else { $this->log("[ERROR] Permission of destination folder failed to change to u+rwx"); } } if (!is_writable($destination)) { $this->log("WARNING: The {$destination} directory is not writable."); $this->errorMessage = "NOTICE: The {$destination} directory is not writable on this server please talk to your host or server admin about making "; $this->errorMessage .= "" . "writable {$destination} directory on this server.
"; throw new Exception('Destination folter isn\'t writeable'); } if ($this->isZip()) { if ($this->extractInstallerZip() == false) { throw new Exception('Fail zip extraction'); } } else { try { DupArchiveExpandBasicEngine::setCallbacks( array($this, 'log'), array($this, 'chmod'), array($this, 'mkdir') ); $offset = DupArchiveExpandBasicEngine::getExtraOffset($archive_filepath, $this->archivePwd); $this->log('Expand directory from offset ' . $offset); DupArchiveExpandBasicEngine::expandDirectory( $archive_filepath, $this->origDupInstFolder, $this->extractionTmpFolder, $this->archivePwd, false, $offset ); @unlink($this->extractionTmpFolder . "/" . $this->origDupInstFolder . "/" . $this->manualExtractFileName); } catch (Exception $ex) { $this->log("[ERROR] Error expanding installer subdirectory:" . $ex->getMessage()); throw $ex; } } if ($this->isCustomDupFolder) { $this->log("Move dup-installer folder to custom folder:" . $this->targetDupInst); if (file_exists($this->targetDupInst)) { $this->log('Custom folder already exists so delete it'); if (BootstrapUtils::rrmdir($this->targetDupInst) == false) { throw new Exception('Can\'t remove custom target folder'); } } if (rename($this->extractionTmpFolder . '/' . $this->origDupInstFolder, $this->targetDupInst) === false) { throw new Exception('Can\'t rename the tmp dup-installer folder'); } } $htaccessToRemove = $this->targetDupInst . '/.htaccess'; if (is_file($htaccessToRemove) && is_writable($htaccessToRemove)) { $this->log("Remove Htaccess in dup-installer folder"); @unlink($htaccessToRemove); } $is_apache = (strpos($_SERVER['SERVER_SOFTWARE'], 'Apache') !== false || strpos($_SERVER['SERVER_SOFTWARE'], 'LiteSpeed') !== false); $is_nginx = (strpos($_SERVER['SERVER_SOFTWARE'], 'nginx') !== false); $sapi_type = php_sapi_name(); $php_ini_data = array( 'max_execution_time' => 3600, 'max_input_time' => -1, 'ignore_user_abort' => 'On', 'post_max_size' => '4096M', 'upload_max_filesize' => '4096M', 'memory_limit' => DUPLICATOR_PRO_PHP_MAX_MEMORY, 'default_socket_timeout' => 3600, 'pcre.backtrack_limit' => 99999999999, ); $sapi_type_first_three_chars = substr($sapi_type, 0, 3); if ('fpm' === $sapi_type_first_three_chars) { $this->log("SAPI: FPM"); if ($is_apache) { $this->log('Server: Apache'); } elseif ($is_nginx) { $this->log('Server: Nginx'); } if (($is_apache && function_exists('apache_get_modules') && in_array('mod_rewrite', apache_get_modules())) || $is_nginx) { $htaccess_data = array(); foreach ($php_ini_data as $php_ini_key => $php_ini_val) { if ($is_apache) { $htaccess_data[] = 'SetEnv PHP_VALUE "' . $php_ini_key . ' = ' . $php_ini_val . '"'; } elseif ($is_nginx) { if ('On' == $php_ini_val || 'Off' == $php_ini_val) { $htaccess_data[] = 'php_flag ' . $php_ini_key . ' ' . $php_ini_val; } else { $htaccess_data[] = 'php_value ' . $php_ini_key . ' ' . $php_ini_val; } } } $htaccess_text = implode("\n", $htaccess_data); $htaccess_file_path = $this->targetDupInst . '/.htaccess'; $this->log("creating {$htaccess_file_path} with the content:"); $this->log($htaccess_text); @file_put_contents($htaccess_file_path, $htaccess_text); } } elseif ('cgi' === $sapi_type_first_three_chars || 'litespeed' === $sapi_type) { if ('cgi' === $sapi_type_first_three_chars) { $this->log("SAPI: CGI"); } else { $this->log("SAPI: litespeed"); } if (version_compare(phpversion(), 5.5) >= 0 && (!$is_apache || 'litespeed' === $sapi_type)) { $ini_data = array(); foreach ($php_ini_data as $php_ini_key => $php_ini_val) { $ini_data[] = $php_ini_key . ' = ' . $php_ini_val; } $ini_text = implode("\n", $ini_data); $ini_file_path = $this->targetDupInst . '/.user.ini'; $this->log("creating {$ini_file_path} with the content:"); $this->log($ini_text); @file_put_contents($ini_file_path, $ini_text); } else { $this->log("No need to create " . $this->targetDupInstFolder . "/.htaccess or " . $this->targetDupInstFolder . "/.user.ini"); } } elseif ("apache2handler" === $sapi_type) { $this->log("No need to create " . $this->targetDupInstFolder . "/.htaccess or " . $this->targetDupInstFolder . "/.user.ini"); $this->log("SAPI: apache2handler"); } else { $this->log("No need to create " . $this->targetDupInstFolder . "/.htaccess or " . $this->targetDupInstFolder . "/.user.ini"); $this->log("ERROR: SAPI: Unrecognized"); } } protected function extractInstallerZip() { $success = false; switch ($this->getZipMode()) { case self::ZIP_MODE_ARCHIVE: $this->log("ZipArchive exists so using that"); $success = $this->extractInstallerZipArchive($this->getArchiveFilePath(), $this->origDupInstFolder, $this->extractionTmpFolder); if ($success) { $this->log('Successfully extracted with ZipArchive'); } else { if (0 == $this->installerFilesFound) { $this->errorMessage = "[ERROR] This archive is not properly formatted and does not contain a " . $this->origDupInstFolder . " directory. Please make sure you are attempting to install " . "the original archive and not one that has been reconstructed."; $this->log($this->errorMessage); } else { $this->errorMessage = '[ERROR] Error extracting with ZipArchive. '; $this->log($this->errorMessage); } } break; case self::ZIP_MODE_SHELL: $success = $this->extractInstallerShellexec($this->getArchiveFilePath(), $this->origDupInstFolder, $this->extractionTmpFolder); $this->log("Resetting perms of items in folder {$this->targetDupInstFolder}"); self::setPermsToDefaultR($this->targetDupInstFolder); if ($success) { $this->log('Successfully extracted with Shell Exec'); $this->errorMessage = ''; } else { $this->errorMessage .= '[ERROR] Error extracting with Shell Exec. ' . 'Please manually extract archive then choose Advanced > Manual Extract in installer.'; $this->log($this->errorMessage); } break; case self::ZIP_MODE_NONE: if (!class_exists('ZipArchive') && BootstrapUtils::getUnzipFilePath() === false) { $this->log("WARNING: ZipArchive and Shell Exec are not enabled on this server."); $this->errorMessage = "NOTICE: ZipArchive and Shell Exec are not enabled on this server please " . "talk to your host or server admin about enabling "; $this->errorMessage .= "ZipArchive " . "or Shell Exec " . "on this server or manually extract archive then choose Advanced > Manual Extract in installer."; } break; } return $success; } protected function setCsrfData() { SecureCsrf::setKeyVal('installerOrigCall', BootstrapUtils::getCurrentUrl()); SecureCsrf::setKeyVal('installerOrigPath', __FILE__); SecureCsrf::setKeyVal('archive', $this->archive); SecureCsrf::setKeyVal('bootloader', $this->bootloader); SecureCsrf::setKeyVal(self::CSRF_KEY_ARCHIVE_PASSWORD, $this->archivePwd); SecureCsrf::setKeyVal('booturl', '//' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']); SecureCsrf::setKeyVal('bootLogFile', $this->getBootLogFilePath()); SecureCsrf::setKeyVal('package_hash', $this->packageHash); SecureCsrf::setKeyVal('secondaryHash', $this->secondaryHash); } public function getRedirectForm() { $secure_csrf_token = SecureCsrf::generate('secure'); $ctrl_csrf_token = SecureCsrf::generate('ctrl-step1'); $id = uniqid(); $html = "
\n"; $data = array( 'ctrl_action' => 'ctrl-step1', 'ctrl_csrf_token' => $ctrl_csrf_token, 'step_action' => 'init' ); foreach ($data as $name => $value) { if ('csrf_token' != $name) { $_SESSION[$name] = $value; } $html .= "\n"; } $html .= "
\n"; $html .= ""; return $html; } private function fixInstallerPerms() { $file_perms = 'u+rw'; $dir_perms = 'u+rwx'; $installer_dir_path = $this->targetDupInstFolder; $this->setPerms($installer_dir_path, $dir_perms, false); $this->setPerms($installer_dir_path, $file_perms, true); } private function setPerms($directory, $perms, $do_files) { if (!$do_files) { $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); } } } private function setPermsOnItem($path, $perms) { if (($result = BootstrapUtils::chmod($path, $perms)) === false) { $this->log("ERROR: Couldn't set permissions of $path
"); } else { $this->log("Set permissions of $path
"); } return $result; } private function compareStrings($oldString, $newString) { $ret = ''; for ($i = 0; isset($oldString[$i]) || isset($newString[$i]); $i++) { if (!isset($oldString[$i])) { $ret .= '' . $newString[$i] . ''; continue; } for ($char = 0; isset($oldString[$i][$char]) || isset($newString[$i][$char]); $char++) { if (!isset($oldString[$i][$char])) { $ret .= '' . substr($newString[$i], $char) . ''; break; } elseif (!isset($newString[$i][$char])) { break; } if (ord($oldString[$i][$char]) != ord($newString[$i][$char])) { $ret .= '' . $newString[$i][$char] . ''; } else { $ret .= $newString[$i][$char]; } } } return $ret; } public function log($s, $deleteOld = false) { static $logfile = null; if (is_null($logfile)) { $logfile = $this->getBootLogFilePath(); } if ($deleteOld && file_exists($logfile)) { @unlink($logfile); } $timestamp = date('M j H:i:s'); return @file_put_contents($logfile, '[' . $timestamp . '] ' . $this->postprocessLog($s) . "\n", FILE_APPEND); } public function getBootLogFilePath() { return $this->targetRoot . '/dup-installer-bootlog__' . $this->secondaryHash . '.txt'; } protected function postprocessLog($str) { return str_replace(array( $this->getArchiveFileHash(), $this->packageHash, $this->secondaryHash ), '[HASH]', $str); } public function getArchiveFileHash() { static $fileHash = null; if (is_null($fileHash)) { $fileHash = preg_replace('/^.+_([a-z0-9]+)_[0-9]{14}_archive\.(?:daf|zip)$/', '$1', $this->archiveFileName); } return $fileHash; } private function extractInstallerZipArchive($archive_filepath, $origDupInstFolder, $destination, $checkSubFolder = false) { $success = true; $zipArchive = new ZipArchive(); $subFolderArchiveList = array(); if (($zipOpenRes = $zipArchive->open($archive_filepath)) !== true) { $this->log("[ERROR] Couldn't open archive archive file with ZipArchive CODE[" . $zipOpenRes . "]"); return false; } if (strlen($this->archivePwd)) { $zipArchive->setPassword($this->archivePwd); } $this->log("Successfully opened archive file."); $folder_prefix = $origDupInstFolder . '/'; $this->log("Extracting all files from archive within " . $origDupInstFolder); $this->installerFilesFound = 0; for ($i = 0; $i < $zipArchive->numFiles; $i++) { $stat = $zipArchive->statIndex($i); if ($checkSubFolder == false) { $filenameCheck = $stat['name']; $filename = $stat['name']; $tmpSubFolder = null; } else { $safePath = rtrim(BootstrapUtils::setSafePath($stat['name']), '/'); $tmpArray = explode('/', $safePath); if (count($tmpArray) < 2) { continue; } $tmpSubFolder = $tmpArray[0]; array_shift($tmpArray); $filenameCheck = implode('/', $tmpArray); $filename = $stat['name']; } if (!BootstrapUtils::startsWith($filenameCheck, $folder_prefix)) { continue; } $this->installerFilesFound++; if (!empty($tmpSubFolder) && !in_array($tmpSubFolder, $subFolderArchiveList)) { $subFolderArchiveList[] = $tmpSubFolder; } if (basename($filename) === $this->manualExtractFileName) { $this->log("Skipping manual extract file: {$filename}"); continue; } if ($zipArchive->extractTo($destination, $filename) === true) { $this->log("Success: {$filename} >>> {$destination}"); } else { $this->log("[ERROR] Error extracting {$filename} >>> {$destination}"); $success = false; break; } } if ($checkSubFolder && count($subFolderArchiveList) !== 1) { $this->log("Error: Multiple dup subfolder archive"); $success = false; } else { if ($checkSubFolder) { $this->moveUpfromSubFolder($destination . '/' . $subFolderArchiveList[0], true); } $lib_directory = $destination . '/' . $origDupInstFolder . '/lib'; $snaplib_directory = $lib_directory . '/snaplib'; if (!file_exists($snaplib_directory)) { $folder_prefix = 'snaplib/'; $destination = $lib_directory; for ($i = 0; $i < $zipArchive->numFiles; $i++) { $stat = $zipArchive->statIndex($i); $filename = $stat['name']; if (BootstrapUtils::startsWith($filename, $folder_prefix)) { $this->installerFilesFound++; if ($zipArchive->extractTo($destination, $filename) === true) { $this->log("Success: {$filename} >>> {$destination}"); } else { $this->log("[ERROR] Error extracting {$filename} from archive archive file"); $success = false; break; } } } } } if ($zipArchive->close() === true) { $this->log("Successfully closed archive file"); } else { $this->log("[ERROR] Problem closing archive file"); $success = false; } if ($success != false && $this->installerFilesFound < 10) { if ($checkSubFolder) { $this->log("[ERROR] Couldn't find the installer directory in the archive!"); $success = false; } else { $this->log("[ERROR] Couldn't find the installer directory in archive root! Check subfolder"); return $this->extractInstallerZipArchive($archive_filepath, $origDupInstFolder, $destination, true); } } return $success; } public static function isWindows() { static $isWindows = null; if (is_null($isWindows)) { $isWindows = (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'); } return $isWindows; } public static function setPermsToDefaultR($directory) { $dir = new \RecursiveDirectoryIterator($directory, \FilesystemIterator::SKIP_DOTS); $iterator = new \RecursiveIteratorIterator($dir, \RecursiveIteratorIterator::SELF_FIRST); $defaultFilePermission = 0666 & ~umask(); $defaultDirPermission = 0777 & ~umask(); foreach ($iterator as $item) { if ($item->isFile()) { BootstrapUtils::chmod($item->getPathname(), $defaultFilePermission); } if ($item->isDir()) { BootstrapUtils::chmod($item->getPathname(), $defaultDirPermission); } } } public static function checkInputValidInt($input) { return (filter_var($input, FILTER_VALIDATE_INT) === 0 || filter_var($input, FILTER_VALIDATE_INT)); } private function moveUpfromSubFolder($subFolderName, $deleteSubFolder = false) { if (!is_dir($subFolderName)) { return false; } $parentFolder = dirname($subFolderName); if (!is_writable($parentFolder)) { return false; } $success = true; if (($subList = glob(rtrim($subFolderName, '/') . '/*', GLOB_NOSORT)) === false) { $this->log("[ERROR] Problem glob folder " . $subFolderName); return false; } else { foreach ($subList as $cName) { $destination = $parentFolder . '/' . basename($cName); if (file_exists($destination)) { $success = BootstrapUtils::rrmdir($destination); } if ($success) { $success = rename($cName, $destination); } else { break; } } if ($success && $deleteSubFolder) { $success = BootstrapUtils::rrmdir($subFolderName, true); } } if (!$success) { $this->log("[ERROR] Problem om moveUpfromSubFolder subFolder:" . $subFolderName); } return $success; } private function extractInstallerShellexec($archive_filepath, $origDupInstFolder, $destination) { $success = false; $this->log("Attempting to use Shell Exec"); $unzip_filepath = BootstrapUtils::getUnzipFilePath(); if ($unzip_filepath == null) { return false; } $params = "-q"; if (strlen($this->archivePwd)) { $params .= ' -P ' . escapeshellarg($this->archivePwd); } $unzip_command = escapeshellcmd($unzip_filepath) . ' ' . $params . ' ' . escapeshellarg($archive_filepath) . ' ' . escapeshellarg($origDupInstFolder . '/*') . ' -d ' . $destination . ' -x ' . escapeshellarg($origDupInstFolder . '/' . $this->manualExtractFileName) . ' 2>&1'; escapeshellarg($this->root_path) . " 2>&1"; $this->log("Executing $unzip_command"); $stderr = shell_exec($unzip_command); if ($stderr == '') { $this->log("Shell exec unzip succeeded"); $success = true; } else { $this->log("[ERROR] Shell exec unzip failed. Output={$stderr}"); } return $success; } private function getArchiveFilePath() { if (($archive_filepath = filter_input(INPUT_GET, 'archive', FILTER_SANITIZE_SPECIAL_CHARS)) != false) { if (is_dir($archive_filepath) && file_exists($archive_filepath . '/' . $this->archiveFileName)) { $archive_filepath = $archive_filepath . '/' . $this->archiveFileName; } else { $archive_filepath = $archive_filepath; } } else { $archive_filepath = $this->targetRoot . '/' . $this->archiveFileName; } if (($realPath = realpath($archive_filepath)) !== false) { return $realPath; } else { return $archive_filepath; } } private function getZipMode() { $zip_mode = self::ZIP_MODE_AUTO; if (isset($_GET['zipmode'])) { $zipmode_string = $_GET['zipmode']; $this->log("Unzip mode specified in querystring: $zipmode_string"); switch ($zipmode_string) { case 'autounzip': $zip_mode = self::ZIP_MODE_AUTO; break; case 'ziparchive': $zip_mode = self::ZIP_MODE_ARCHIVE; break; case 'shellexec': $zip_mode = self::ZIP_MODE_SHELL; break; } } switch ($zip_mode) { case self::ZIP_MODE_AUTO: case self::ZIP_MODE_ARCHIVE: if (class_exists('ZipArchive')) { return self::ZIP_MODE_ARCHIVE; } elseif (function_exists('shell_exec')) { return self::ZIP_MODE_SHELL; } else { return self::ZIP_MODE_NONE; } break; case self::ZIP_MODE_SHELL: if (function_exists('shell_exec')) { return self::ZIP_MODE_SHELL; } elseif (class_exists('ZipArchive')) { return self::ZIP_MODE_ARCHIVE; } else { return self::ZIP_MODE_NONE; } break; default: return self::ZIP_MODE_NONE; } return $zip_mode; } public function getFilesWithExtension($extension) { $files = array(); foreach (glob("*.{$extension}") as $name) { if (file_exists($name)) { $files[] = $name; } } if (count($files) > 0) { return $files; } if (($dh = opendir($this->targetRoot))) { while (false !== ($name = readdir($dh))) { $ext = substr($name, strrpos($name, '.') + 1); if (in_array($ext, array($extension))) { $files[] = $name; } } closedir($dh); } return $files; } public function getVersion() { return $this->version; } public function getSecondaryHash() { return $this->secondaryHash; } } } namespace Duplicator\Installer\Bootstrap { use Duplicator\Core\Bootstrap; use Exception; use ZipArchive; class BootstrapUtils { public static function isIniValChangeable($setting) { static $ini_all; if (!isset($ini_all)) { $ini_all = false; if (function_exists('ini_get_all')) { $ini_all = ini_get_all(); } } if (isset($ini_all[$setting]['access']) && ( INI_ALL === ( $ini_all[$setting]['access'] & 7 ) || INI_USER === ( $ini_all[$setting]['access'] & 7 ) )) { return true; } if (!is_array($ini_all)) { return true; } return false; } public static function phpVersionCheck($minPhpVer) { if (version_compare(PHP_VERSION, $minPhpVer, '>=')) { return true; } $match = null; if (preg_match("#^\d+(\.\d+)*#", PHP_VERSION, $match)) { $phpVersion = $match[0]; } else { $phpVersion = PHP_VERSION; } ?> Duplicator Professional - issue

DUPLICATOR PRO ISSUE: PHP REQUIRED

This server is running PHP: . A minimum of PHP is required.

Contact your hosting provider or server administrator and let them know you would like to upgrade your PHP version.

\s*(libzip.*\sver.+?)\s*<\/td>\s*\s*(.+?)\s*<\/td>/i', $info, $matches) !== 1) { $zlibVersion = "0"; } else { $zlibVersion = $matches[2]; } } return $zlibVersion; } public static function isZipArchiveEncrypted($path, $fileToCheck) { $zip = new ZipArchive(); if (($zipOpenRes = $zip->open($path)) !== true) { $message = "[ERROR] Couldn't open archive archive file with ZipArchive CODE[" . $zipOpenRes . "]"; throw new Exception($message); } if (($stats = $zip->statName($fileToCheck, ZipArchive::FL_NODIR)) == false) { throw new Exception('Formatting archive error, cannot find file ' . $fileToCheck); } if (isset($stats['encryption_method'])) { $isEncrypt = ($stats['encryption_method'] > 0); } else { $isEncrypt = ($zip->getFromIndex($stats['index']) === false); } $zip->close(); return $isEncrypt; } public static function getCurrentUrl($queryString = true, $requestUri = false, $getParentDirLevel = 0) { if (isset($_SERVER['HTTP_X_ORIGINAL_HOST'])) { $host = $_SERVER['HTTP_X_ORIGINAL_HOST']; } else { $host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : $_SERVER['SERVER_NAME']; //WAS SERVER_NAME and caused problems on some boxes } if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') { $_SERVER ['HTTPS'] = 'on'; } if (isset($_SERVER['HTTP_X_FORWARDED_SSL']) && $_SERVER['HTTP_X_FORWARDED_SSL'] === 'https') { $_SERVER ['HTTPS'] = 'on'; } if (isset($_SERVER['HTTP_CF_VISITOR'])) { $visitor = json_decode($_SERVER['HTTP_CF_VISITOR']); if ($visitor->scheme == 'https') { $_SERVER ['HTTPS'] = 'on'; } } $protocol = 'http' . ((isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) === 'on') ? 's' : ''); if ($requestUri) { $serverUrlSelf = preg_replace('/\?.*$/', '', $_SERVER['REQUEST_URI']); } else { $serverUrlSelf = $_SERVER['SCRIPT_NAME']; for ($i = 0; $i < $getParentDirLevel; $i++) { $serverUrlSelf = preg_match('/^[\\\\\/]?$/', dirname($serverUrlSelf)) ? '' : dirname($serverUrlSelf); } } $query = ($queryString && isset($_SERVER['QUERY_STRING']) && strlen($_SERVER['QUERY_STRING']) > 0 ) ? '?' . $_SERVER['QUERY_STRING'] : ''; return $protocol . '://' . $host . $serverUrlSelf . $query; } public static function chmod($file, $mode) { if (!file_exists($file)) { return false; } $octalMode = 0; if (is_int($mode)) { $octalMode = $mode; } elseif (is_string($mode)) { $mode = trim($mode); if (preg_match('/([0-7]{1,3})/', $mode)) { $octalMode = intval(('0' . $mode), 8); } elseif (preg_match_all('/(a|[ugo]{1,3})([-=+])([rwx]{1,3})/', $mode, $gMatch, PREG_SET_ORDER)) { if (!function_exists('fileperms')) { return false; } $octalMode = (fileperms($file) & 0777); foreach ($gMatch as $matches) { $group = $matches[1]; if ($group === 'a') { $group = 'ugo'; } $action = $matches[2]; $gPerms = $matches[3]; $octalGroupMode = 0; $subPerm = 0; $subPerm += strpos($gPerms, 'x') !== false ? 1 : 0; // mask 001 $subPerm += strpos($gPerms, 'w') !== false ? 2 : 0; // mask 010 $subPerm += strpos($gPerms, 'r') !== false ? 4 : 0; // mask 100 $ugoLen = strlen($group); if ($action === '=') { $ugoMaskInvert = 0777; for ($i = 0; $i < $ugoLen; $i++) { switch ($group[$i]) { case 'u': $octalGroupMode = $octalGroupMode | $subPerm << 6; // mask xxx000000 $ugoMaskInvert = $ugoMaskInvert & 077; break; case 'g': $octalGroupMode = $octalGroupMode | $subPerm << 3; // mask 000xxx000 $ugoMaskInvert = $ugoMaskInvert & 0707; break; case 'o': $octalGroupMode = $octalGroupMode | $subPerm; // mask 000000xxx $ugoMaskInvert = $ugoMaskInvert & 0770; break; } } $octalMode = $octalMode & ($ugoMaskInvert | $octalGroupMode); } else { for ($i = 0; $i < $ugoLen; $i++) { switch ($group[$i]) { case 'u': $octalGroupMode = $octalGroupMode | $subPerm << 6; // mask xxx000000 break; case 'g': $octalGroupMode = $octalGroupMode | $subPerm << 3; // mask 000xxx000 break; case 'o': $octalGroupMode = $octalGroupMode | $subPerm; // mask 000000xxx break; } } switch ($action) { case '+': $octalMode = $octalMode | $octalGroupMode; break; case '-': $octalMode = $octalMode & ~$octalGroupMode; break; } } } } } if (function_exists('fileperms') && $octalMode === (fileperms($file) & 0777)) { return true; } if (!function_exists('chmod')) { return false; } return @chmod($file, $octalMode); } public static function mkdir($path, $mode = 0777, $recursive = false, $context = null) { if (strlen($path) > PHP_MAXPATHLEN) { throw new Exception('Skipping a file that exceeds allowed max path length [' . PHP_MAXPATHLEN . ']. File: ' . $path); } if (!file_exists($path)) { if (!function_exists('mkdir')) { return false; } if (!@mkdir($path, 0777, $recursive)) { return false; } } return self::chmod($path, $mode); } public static function startsWith($haystack, $needle) { return $needle === "" || strrpos($haystack, $needle, - strlen($haystack)) !== false; } public static function hasShellExec() { $cmds = array('shell_exec', 'escapeshellarg', 'escapeshellcmd', 'extension_loaded'); if (array_intersect($cmds, array_map('trim', explode(',', @ini_get('disable_functions'))))) { return false; } if (extension_loaded('suhosin')) { $suhosin_ini = @ini_get("suhosin.executor.func.blacklist"); if (array_intersect($cmds, array_map('trim', explode(',', $suhosin_ini)))) { return false; } } if (! function_exists('shell_exec')) { return false; } if (!@shell_exec('echo duplicator')) { return false; } return true; } public static function getUnzipFilePath() { static $filepath = null; if ($filepath === null) { if (!self::hasShellExec()) { $filepath = false; } elseif (shell_exec('hash unzip 2>&1') == null) { $filepath = 'unzip'; } else { $filepath = false; $possible_paths = array( '/usr/bin/unzip', '/opt/local/bin/unzip', '/bin/unzip', '/usr/local/bin/unzip', '/usr/sfw/bin/unzip', '/usr/xdg4/bin/unzip', '/opt/bin/unzip', ); foreach ($possible_paths as $path) { if (file_exists($path)) { $filepath = $path; break; } } } } return $filepath; } public static function readableByteSize($size) { try { $units = array('B', 'KB', 'MB', 'GB', 'TB'); for ($i = 0; $size >= 1024 && $i < 4; $i++) { $size /= 1024; } return round($size, 2) . $units[$i]; } catch (Exception $e) { return "n/a"; } } public static function rrmdir($path) { if (is_dir($path)) { if (($dh = opendir($path)) === false) { return false; } while (($object = readdir($dh)) !== false) { if ($object == "." || $object == "..") { continue; } if (!self::rrmdir($path . "/" . $object)) { closedir($dh); return false; } } closedir($dh); return @rmdir($path); } else { if (is_writable($path)) { return @unlink($path); } else { return false; } } } public static function setSafePath($path) { return str_replace("\\", "/", $path); } public static function sanitizeNSCharsNewline($string) { return preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F\r\n]/u', '', (string) $string); } } } namespace Duplicator\Installer\Bootstrap { use Duplicator\Libs\DupArchive\DupArchive; class BootstrapView { const VIEW_ERROR = 'error'; const VIEW_REDIRECT = 'redirect'; const VIEW_PASSWORD = 'pwd'; protected $boot = null; public function __construct(BootstrapRunner $boot) { $this->boot = $boot; } public function redirectToInsaller() { $this->renderHeader(); ?>
Initializing Installer. Please wait...
boot->getRedirectForm(); $this->renderFooter(); } public function renderPassword() { $this->renderHeader(); $errorMsg = $this->boot->getErrorMessage(); if (strlen($errorMsg) > 0) { $this->renderTopMessages($errorMsg); } ?>

Password Required
This archive was created with an encryption enabled password. Please provide the password to extract the archive file.
Lost passwords for encrypted archives cannot be recovered by support. If the password was lost then a new archive will need to be created.
renderFooter(); } protected function renderTopMessages($message) { ?>
renderExclamationCircle(); ?>
renderHeader(); ?>

Setup Notice:

An error has occurred. In order to load the full installer please resolve the issue below.
boot->getErrorMessage(); ?>


Server Settings:

boot->isZip()) { ?>
ZipArchive: boot->hasZipArchive ? 'Enabled' : 'Disabled'; ?>
ShellExec Unzip: boot->shellUnzipEnabled() ? 'Enabled' : 'Disabled'; ?>
PHP OpenSSL Module: Enabled' : 'Disabled'; ?>
Extraction Path: boot->targetRoot; ?>
Installer Path: boot->targetDupInstFolder; ?>
Archive Size: Expected Size: boot->archiveExpectedSize); ?>   Actual Size: boot->archiveActualSize); ?>
Boot Log dup-installer-bootlog__[HASH].txt


Please Note: Either ZipArchive or Shell Exec will need to be enabled for the installer to run automatically otherwise a manual extraction will need to be performed. In order to run the installer manually follow the instructions to manually extract before running the installer.
renderFooter(); } protected function renderHeader() { ?> Duplicator Pro Installer renderCSS(); ?>
renderBoltFont(); ?> Duplicator Pro
version: boot->getVersion(); ?>
' . htmlspecialchars($log_message) . ''); break; case E_NOTICE: case E_WARNING: default: $log_message = self::getMessage($errno, $errstr, $errfile, $errline); call_user_func(self::$logCallback, $log_message); break; } } private static function getMessage($errno, $errstr, $errfile, $errline) { $result = '[PHP ERR]'; switch ($errno) { case E_ERROR: $result .= '[FATAL]'; break; case E_WARNING: $result .= '[WARN]'; break; case E_NOTICE: $result .= '[NOTICE]'; break; default: $result .= '[ISSUE]'; break; } $result .= ' MSG:'; $result .= $errstr; $result .= ' [CODE:' . $errno . '|FILE:' . $errfile . '|LINE:' . $errline . ']'; return $result; } public static function shutdown() { if (($error = error_get_last())) { LogHandler::error($error['type'], $error['message'], $error['file'], $error['line']); } } } } namespace Duplicator\Installer\Utils { use Exception; class SecureCsrf { const PREFIX = '_DUPX_CSRF'; private static $packagHash = null; private static $mainFolder = null; private static $CSRFVars = null; public static function init($mainFolderm, $packageHash) { self::$mainFolder = $mainFolderm; self::$packagHash = $packageHash; self::$CSRFVars = null; } public static function setKeyVal($key, $val) { self::getCSRFVars(); self::$CSRFVars[$key] = $val; self::saveCSRFVars(); } public static function removeKeyVal($key, $val) { self::getCSRFVars(); if (isset(self::$CSRFVars[$key])) { unset(self::$CSRFVars[$key]); self::saveCSRFVars(); } } public static function getVal($key) { self::getCSRFVars(); if (isset(self::$CSRFVars[$key])) { return self::$CSRFVars[$key]; } else { return false; } } public static function generate($form = null) { $keyName = self::getKeyName($form); $existingToken = self::getVal($keyName); if (false !== $existingToken) { $token = $existingToken; } else { $token = self::token() . self::fingerprint(); } self::setKeyVal($keyName, $token); return $token; } public static function check($token, $form = null) { if (empty($form)) { return false; } $keyName = self::getKeyName($form); self::getCSRFVars(); return (isset(self::$CSRFVars[$keyName]) && self::$CSRFVars[$keyName] == $token); } protected static function token() { $microtime = (int) (microtime(true) * 10000); mt_srand($microtime); $charid = strtoupper(md5(uniqid(rand(), true))); return substr($charid, 0, 8) . substr($charid, 8, 4) . substr($charid, 12, 4) . substr($charid, 16, 4) . substr($charid, 20, 12); } protected static function fingerprint() { return strtoupper(md5(implode('|', array($_SERVER['REMOTE_ADDR'], $_SERVER['HTTP_USER_AGENT'])))); } private static function getKeyName($form) { return self::PREFIX . '_' . $form; } private static function getPackageHash() { if (is_null(self::$packagHash)) { throw new Exception('Not init CSFR CLASS'); } return self::$packagHash; } public static function getFilePath() { if (is_null(self::$mainFolder)) { throw new Exception('Not init CSFR CLASS'); } $dupInstallerfolderPath = self::$mainFolder; $packageHash = self::getPackageHash(); $fileName = 'dup-installer-csrf__' . $packageHash . '.txt'; $filePath = $dupInstallerfolderPath . '/' . $fileName; return $filePath; } private static function getCSRFVars() { if (is_null(self::$CSRFVars)) { $filePath = self::getFilePath(); if (file_exists($filePath)) { $contents = file_get_contents($filePath); if (empty($contents)) { self::$CSRFVars = array(); } else { $CSRFobjs = json_decode($contents); foreach ($CSRFobjs as $key => $value) { self::$CSRFVars[$key] = $value; } } } else { self::$CSRFVars = array(); } } return self::$CSRFVars; } private static function saveCSRFVars() { $contents = json_encode(self::$CSRFVars); $filePath = self::getFilePath(); file_put_contents($filePath, $contents); } } } namespace Duplicator\Libs\DupArchive { use Duplicator\Libs\DupArchive\Headers\DupArchiveDirectoryHeader; use Duplicator\Libs\DupArchive\Headers\DupArchiveFileHeader; use Duplicator\Libs\DupArchive\Headers\DupArchiveGlobHeader; use Duplicator\Libs\DupArchive\Headers\DupArchiveHeader; use Error; use Exception; class DupArchive { const EXCEPTION_CODE_FILE_DONT_EXISTS = 10; const EXCEPTION_CODE_OPEN_ERROR = 11; const EXCEPTION_CODE_INVALID_PASSWORD = 12; const EXCEPTION_CODE_INVALID_MARKER = 13; const EXCEPTION_CODE_INVALID_PARAM = 14; const EXCEPTION_CODE_ADD_ERROR = 15; const EXCEPTION_CODE_EXTRACT_ERROR = 16; const EXCEPTION_CODE_VALIDATION_ERROR = 17; const DUPARCHIVE_VERSION = '5.0.0'; const INDEX_FILE_NAME = '__dup__archive__index.json'; const INDEX_FILE_SIZE = 2000; // reserver 2K const EXTRA_FILES_POS_KEY = 'extraPos'; const HEADER_TYPE_NONE = 0; const HEADER_TYPE_FILE = 1; const HEADER_TYPE_DIR = 2; const HEADER_TYPE_GLOB = 3; const FLAG_COMPRESS = 1; //bitmask const FLAG_CRYPT = 2; //bitmask const HASH_ALGO = 'crc32b'; const PWD_ALGO = '$6$rounds=50000$'; // SHA-512 50000 times with salt const PWD_SALT_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#%^-_%^&*()[]{}<>~`+=,.;:/?|'; const CRYPT_ALGO = 'AES-256-CBC'; protected static function getNextHeaderType($archiveHandle) { $retVal = self::HEADER_TYPE_NONE; $marker = fgets($archiveHandle, 4); if (feof($archiveHandle) === false) { switch ($marker) { case '': $retVal = self::HEADER_TYPE_DIR; break; case '': $retVal = self::HEADER_TYPE_FILE; break; case '': $retVal = self::HEADER_TYPE_GLOB; break; default: throw new Exception("Invalid header marker {$marker}. Location:" . ftell($archiveHandle), self::EXCEPTION_CODE_INVALID_MARKER); } } return $retVal; } public static function isEncrypted($path) { return !self::checkPassword($path, ''); } public static function checkPassword($path, $password) { try { $header = self::getArchiveHeader($path, $password); } catch (Exception $e) { if ($e->getCode() == self::EXCEPTION_CODE_INVALID_PASSWORD) { return false; } else { throw $e; } } return true; } public static function getArchiveHeader($path, $password) { try { if (!file_exists($path)) { throw new Exception('Archive file don\'t exists', self::EXCEPTION_CODE_FILE_DONT_EXISTS); } if (($archiveHandle = fopen($path, 'r')) == false) { throw new Exception('Can\'t open archive file', self::EXCEPTION_CODE_OPEN_ERROR); } $result = (new DupArchiveHeader())->readFromArchive($archiveHandle, $password); } finally { if (is_resource($archiveHandle)) { fclose($archiveHandle); } } return $result; } public static function isEncryptionAvaliable() { static $isAvaliable = null; if ($isAvaliable === null) { $isAvaliable = ( function_exists('openssl_cipher_iv_length') && function_exists('openssl_encrypt') && function_exists('openssl_decrypt') ); } return $isAvaliable; } public static function getIndexData($archivePath, $password) { try { $indexContent = self::getSrcFile($archivePath, self::INDEX_FILE_NAME, $password, 0, 3000); if ($indexContent === false) { return false; } $indexData = json_decode(rtrim($indexContent, "\0"), true); if (!is_array($indexData)) { return false; } } catch (Exception $e) { return false; } catch (Error $e) { return false; } return $indexData; } public static function getExtraOffset($archivePath, $password) { if (($indexData = self::getIndexData($archivePath, $password)) === false) { return 0; } return (isset($indexData[self::EXTRA_FILES_POS_KEY]) ? $indexData[self::EXTRA_FILES_POS_KEY] : 0); } public static function seachPathInArchive($archivePath, $relativePath, $password, $offset = 0, $sizeToSearch = 0) { try { if (($archiveHandle = fopen($archivePath, 'rb')) === false) { throw new Exception("Can’t open archive at $archivePath!", self::EXCEPTION_CODE_OPEN_ERROR); } $archiveHeader = (new DupArchiveHeader())->readFromArchive($archiveHandle, $password); $result = self::searchPath($archivePath, $archiveHeader, $relativePath, $offset, $sizeToSearch); } finally { if (is_resource($archiveHandle)) { fclose($archiveHandle); } } return $result; } public static function searchPath($archiveHandle, DupArchiveHeader $archiveHeader, $relativePath, $offset = 0, $sizeToSearch = 0) { if (!is_resource($archiveHandle)) { throw new Exception('Archive handle must be a resource', self::EXCEPTION_CODE_INVALID_PARAM); } if (fseek($archiveHandle, $offset, SEEK_SET) < 0) { return false; } if ($offset == 0) { $hd = (new DupArchiveHeader())->readFromArchive($archiveHandle, $archiveHeader->getPassword()); } $result = false; $position = ftell($archiveHandle); $continue = true; do { switch (($type = self::getNextHeaderType($archiveHandle))) { case self::HEADER_TYPE_FILE: $currentFileHeader = (new DupArchiveFileHeader($archiveHeader))->readFromArchive($archiveHandle, true, true); if ($currentFileHeader->relativePath == $relativePath) { $continue = false; $result = $position; } break; case self::HEADER_TYPE_DIR: $directoryHeader = (new DupArchiveDirectoryHeader($archiveHeader))->readFromArchive($archiveHandle, true); if ($directoryHeader->relativePath == $relativePath) { $continue = false; $result = $position; } break; case self::HEADER_TYPE_NONE: $continue = false; break; default: throw new Exception('Invali header type "' . $type . '"', self::EXCEPTION_CODE_INVALID_MARKER); } $position = ftell($archiveHandle); if ($sizeToSearch > 0 && ($position - $offset) >= $sizeToSearch) { break; } } while ($continue); if ($result !== false) { if (fseek($archiveHandle, $result, SEEK_SET) < 0) { return false; } } return $result; } public static function getSrcFile($archivePath, $relativePath, $password, $offset = 0, $sizeToSearch = 0) { try { if (($archiveHandle = fopen($archivePath, 'rb')) === false) { throw new Exception("Can’t open archive at $archivePath!", self::EXCEPTION_CODE_OPEN_ERROR); } $archiveHeader = (new DupArchiveHeader())->readFromArchive($archiveHandle, $password); if (self::searchPath($archiveHandle, $archiveHeader, $relativePath, $offset, $sizeToSearch) === false) { return false; } if (self::getNextHeaderType($archiveHandle) != self::HEADER_TYPE_FILE) { return false; } $header = (new DupArchiveFileHeader($archiveHeader))->readFromArchive($archiveHandle, false, true); $result = self::getSrcFromHeader($archiveHandle, $header); } finally { if (is_resource($archiveHandle)) { fclose($archiveHandle); } } return $result; } protected static function getSrcFromHeader($archiveHandle, DupArchiveFileHeader $fileHeader) { if ($fileHeader->fileSize == 0) { return ''; } $dataSize = 0; $result = ''; $globHeader = new DupArchiveGlobHeader($fileHeader); do { $globHeader->readFromArchive($archiveHandle); $result .= $globHeader->readContent($archiveHandle, $globHeader); $dataSize += $globHeader->originalSize; } while ($dataSize < $fileHeader->fileSize); return $result; } protected static function skipFileInArchive($archiveHandle, DupArchiveFileHeader $fileHeader) { if ($fileHeader->fileSize == 0) { return; } $dataSize = 0; $globHeader = new DupArchiveGlobHeader($fileHeader); do { $globHeader->readFromArchive($archiveHandle, true); $dataSize += $globHeader->originalSize; } while ($dataSize < $fileHeader->fileSize); } protected static function skipToNextHeader($archiveHandle, DupArchiveHeader $archiveHeader) { $headerType = self::getNextHeaderType($archiveHandle); switch ($headerType) { case self::HEADER_TYPE_FILE: $fileHeader = (new DupArchiveFileHeader($archiveHeader))->readFromArchive($archiveHandle, false, true); self::skipFileInArchive($archiveHandle, $fileHeader); break; case self::HEADER_TYPE_DIR: $directoryHeader = (new DupArchiveDirectoryHeader($archiveHeader))->readFromArchive($archiveHandle, true); break; case self::HEADER_TYPE_NONE: false; } } public static function generateSalt($length) { $maxRand = (strlen(self::PWD_SALT_CHARS) - 1); $password = ''; for ($i = 0; $i < $length; $i++) { if (function_exists('random_int')) { $cIndex = random_int(0, $maxRand); } else { mt_srand(time()); $cIndex = mt_rand(0, $maxRand); } $password .= substr(self::PWD_SALT_CHARS, $cIndex, 1); } return $password; } public static function encrypt($content, $key) { static $ivLen = null; if ($ivLen === null) { if (!self::isEncryptionAvaliable()) { throw new Exception('Encryption is unavaiable', self::EXCEPTION_CODE_OPEN_ERROR); } $ivLen = openssl_cipher_iv_length(DupArchive::CRYPT_ALGO); } $iv = openssl_random_pseudo_bytes($ivLen); if (($result = openssl_encrypt($content, DupArchive::CRYPT_ALGO, $key, OPENSSL_RAW_DATA, $iv)) === false) { return false; } else { return $iv . $result; } } public static function decrypt($content, $key) { static $ivLen = null; if ($ivLen === null) { if (!self::isEncryptionAvaliable()) { throw new Exception('Encryption is unavaiable', self::EXCEPTION_CODE_OPEN_ERROR); } $ivLen = openssl_cipher_iv_length(DupArchive::CRYPT_ALGO); } $iv = substr($content, 0, $ivLen); return openssl_decrypt(substr($content, $ivLen), DupArchive::CRYPT_ALGO, $key, OPENSSL_RAW_DATA, $iv); } } } namespace Duplicator\Libs\DupArchive { use Duplicator\Libs\DupArchive\Headers\DupArchiveDirectoryHeader; use Duplicator\Libs\DupArchive\Headers\DupArchiveFileHeader; use Duplicator\Libs\DupArchive\Headers\DupArchiveGlobHeader; use Duplicator\Libs\DupArchive\Headers\DupArchiveHeader; use Duplicator\Libs\DupArchive\Info\DupArchiveExpanderInfo; use Exception; class DupArchiveExpandBasicEngine extends DupArchive { protected static $logCallback = null; protected static $chmodCallback = null; protected static $mkdirCallback = null; public static function setCallbacks($log, $chmod, $mkdir) { self::$logCallback = (is_callable($log) ? $log : null); self::$chmodCallback = (is_callable($chmod) ? $chmod : null); self::$mkdirCallback = (is_callable($mkdir) ? $mkdir : null); } public static function log($s, $flush = false) { if (self::$logCallback == null) { return; } call_user_func(self::$logCallback, "MINI EXPAND:$s", $flush); } public static function expandDirectory($archivePath, $relativePath, $destPath, $password, $ignoreErrors = false, $offset = 0) { self::expandItems($archivePath, $relativePath, $destPath, $password, $ignoreErrors, $offset); } private static function expandItems($archivePath, $inclusionFilter, $destDirectory, $password, $ignoreErrors = false, $offset = 0) { $archiveHandle = fopen($archivePath, 'rb'); if ($archiveHandle === false) { throw new Exception("Can’t open archive at $archivePath!", self::EXCEPTION_CODE_OPEN_ERROR); } $archiveHeader = (new DupArchiveHeader())->readFromArchive($archiveHandle, $password); $writeInfo = new DupArchiveExpanderInfo(); $writeInfo->destDirectory = $destDirectory; if ($offset > 0) { fseek($archiveHandle, $offset); } $moreToRead = true; while ($moreToRead) { if ($writeInfo->currentFileHeader != null) { try { if (self::passesInclusionFilter($inclusionFilter, $writeInfo->currentFileHeader->relativePath)) { self::writeToFile($archiveHandle, $writeInfo); $writeInfo->fileWriteCount++; } elseif ($writeInfo->currentFileHeader->fileSize > 0) { self::skipFileInArchive($archiveHandle, $writeInfo->currentFileHeader); } $writeInfo->currentFileHeader = null; } catch (Exception $ex) { if (!$ignoreErrors) { throw $ex; } } } else { $headerType = self::getNextHeaderType($archiveHandle); switch ($headerType) { case self::HEADER_TYPE_FILE: $writeInfo->currentFileHeader = (new DupArchiveFileHeader($archiveHeader))->readFromArchive($archiveHandle, false, true); break; case self::HEADER_TYPE_DIR: $directoryHeader = (new DupArchiveDirectoryHeader($archiveHeader))->readFromArchive($archiveHandle, true); if (self::passesInclusionFilter($inclusionFilter, $directoryHeader->relativePath)) { $directory = "{$writeInfo->destDirectory}/{$directoryHeader->relativePath}"; if (is_callable(self::$mkdirCallback)) { call_user_func(self::$mkdirCallback, $directory, 'u+rwx', true); } else { mkdir($directory, 0755, true); } $writeInfo->directoryWriteCount++; } else { } break; case self::HEADER_TYPE_NONE: $moreToRead = false; } } } fclose($archiveHandle); } private static function writeToFile($archiveHandle, DupArchiveExpanderInfo $writeInfo) { $destFilePath = $writeInfo->getCurrentDestFilePath(); if ($writeInfo->currentFileHeader->fileSize > 0) { $parentDir = dirname($destFilePath); if (!file_exists($parentDir)) { if (is_callable(self::$mkdirCallback)) { $res = call_user_func(self::$mkdirCallback, $parentDir, 'u+rwx', true); } else { $res = mkdir($parentDir, 0755, true); } if (!$res) { throw new Exception("Couldn't create {$parentDir}", self::EXCEPTION_CODE_EXTRACT_ERROR); } } $destFileHandle = fopen($destFilePath, 'wb+'); if ($destFileHandle === false) { throw new Exception("Couldn't open {$destFilePath} for writing.", self::EXCEPTION_CODE_OPEN_ERROR); } do { self::appendGlobToFile($archiveHandle, $destFileHandle, $writeInfo); $currentFileOffset = ftell($destFileHandle); $moreGlobstoProcess = $currentFileOffset < $writeInfo->currentFileHeader->fileSize; } while ($moreGlobstoProcess); fclose($destFileHandle); if (is_callable(self::$chmodCallback)) { call_user_func(self::$chmodCallback, $destFilePath, 'u+rw'); } else { chmod($destFilePath, 0644); } if ($writeInfo->currentFileHeader->validateFile($destFilePath) == false) { throw new Exception("HASH Validation fails for {$destFilePath}", self::EXCEPTION_CODE_VALIDATION_ERROR); } } else { if (touch($destFilePath) === false) { throw new Exception("Couldn't create $destFilePath", self::EXCEPTION_CODE_EXTRACT_ERROR); } if (is_callable(self::$chmodCallback)) { call_user_func(self::$chmodCallback, $destFilePath, 'u+rw'); } else { chmod($destFilePath, 0644); } } } private static function appendGlobToFile($archiveHandle, $destFileHandle, DupArchiveExpanderInfo $writeInfo) { $globHeader = (new DupArchiveGlobHeader($writeInfo->currentFileHeader))->readFromArchive($archiveHandle); $globContent = $globHeader->readContent($archiveHandle); if (fwrite($destFileHandle, $globContent) !== strlen($globContent)) { throw new Exception("Unable to write all bytes of data glob to storage.", self::EXCEPTION_CODE_EXTRACT_ERROR); } } private static function passesInclusionFilter($filter, $candidate) { return (substr($candidate, 0, strlen($filter)) == $filter); } } } namespace Duplicator\Libs\DupArchive\Headers { use Duplicator\Libs\DupArchive\DupArchive; use Exception; abstract class AbstractDupArchiveHeader { const MAX_FILED_LEN = 1024; abstract public function readFromArchive($archiveHandle); abstract public function writeToArchive($archiveHandle); public static function getHeaderField($archiveHandle, $ename) { $expectedStart = '<' . $ename . '>'; $expectedEnd = ''; $startingElement = fread($archiveHandle, strlen($expectedStart)); if ($startingElement !== $expectedStart) { throw new Exception( "Invalid starting element. Was expecting {$expectedStart} but got {$startingElement}", DupArchive::EXCEPTION_CODE_INVALID_MARKER ); } $headerString = stream_get_line($archiveHandle, self::MAX_FILED_LEN, $expectedEnd); if ($headerString === false) { throw new Exception('Error reading line.', DupArchive::EXCEPTION_CODE_EXTRACT_ERROR); } return $headerString; } } } namespace Duplicator\Libs\DupArchive\Headers { use Duplicator\Libs\DupArchive\DupArchive; use Exception; class DupArchiveDirectoryHeader extends AbstractDupArchiveHeader { public $mtime = 0; public $permissions = ''; public $relativePathLength = 1; public $relativePath = ''; public function __construct(DupArchiveHeader $archiveHeader) { } public function readFromArchive($archiveHandle, $skipStartElement = false) { if (!$skipStartElement) { $startElement = fread($archiveHandle, 3); if ($startElement === false) { if (feof($archiveHandle)) { return false; } else { throw new Exception('Error reading directory header', DupArchive::EXCEPTION_CODE_INVALID_MARKER); } } if ($startElement != '') { throw new Exception( "Invalid directory header marker found [{$startElement}] : location " . ftell($archiveHandle), DupArchive::EXCEPTION_CODE_EXTRACT_ERROR ); } } $this->mtime = self::getHeaderField($archiveHandle, 'MT'); $this->permissions = self::getHeaderField($archiveHandle, 'P'); $this->relativePathLength = self::getHeaderField($archiveHandle, 'RPL'); fread($archiveHandle, 4); $this->relativePath = fread($archiveHandle, $this->relativePathLength); fread($archiveHandle, 9); return $this; } public function writeToArchive($archiveHandle) { if ($this->relativePathLength == 0) { return; } $headerString = '' . $this->mtime . '

' . $this->permissions . '

' . $this->relativePathLength . '' . $this->relativePath . '
'; $bytes_written = @fwrite($archiveHandle, $headerString); if ($bytes_written === false) { throw new Exception('Error writing to file.', DupArchive::EXCEPTION_CODE_ADD_ERROR); } else { return $bytes_written; } } } } namespace Duplicator\Libs\DupArchive\Headers { use Duplicator\Libs\DupArchive\DupArchive; use Duplicator\Libs\Snap\SnapIO; use Exception; class DupArchiveFileHeader extends AbstractDupArchiveHeader { const MAX_SIZE_FOR_HASHING = 1000000000; public $fileSize = 0; public $mtime = 0; public $permissions = ''; public $hash = ''; public $relativePathLength = 0; public $relativePath = ''; protected $flags = 0; protected $version = ''; protected $password = ''; public function __construct(DupArchiveHeader $archiveHeader) { $this->flags = $archiveHeader->getFlags(); $this->version = $archiveHeader->getVersion(); $this->password = $archiveHeader->getPassword(); } public function getVersion() { return $this->version; } public function getFlags() { return $this->flags; } public function setFlags($flags) { $this->flags = (int) $flags; } public function isCompressed() { return ($this->flags & DupArchive::FLAG_COMPRESS ? true : false); } public function isCrypt() { return ($this->flags & DupArchive::FLAG_CRYPT ? true : false); } public function getPassword() { return $this->password; } public function createFromFile($filepath, $relativeFilePath) { $this->fileSize = SnapIO::filesize($filepath); $this->permissions = substr(sprintf('%o', fileperms($filepath)), -4); $this->mtime = SnapIO::filemtime($filepath); if ($this->fileSize > self::MAX_SIZE_FOR_HASHING) { $this->hash = "00000000000000000000000000000000"; } else { $this->hash = hash_file(DupArchive::HASH_ALGO, $filepath); } $this->relativePath = $relativeFilePath; $this->relativePathLength = strlen($this->relativePath); return $this; } public function createFromSrc($src, $relativeFilePath, $forceSize = 0) { $this->fileSize = strlen($src); $this->permissions = '0644'; $this->mtime = time(); $srcLen = strlen($src); if ($forceSize > 0 && $srcLen < $forceSize) { $charsToAdd = $forceSize - $srcLen; $src .= str_repeat("\0", $charsToAdd); } if ($this->fileSize > self::MAX_SIZE_FOR_HASHING) { $this->hash = "00000000000000000000000000000000"; } else { $this->hash = hash(DupArchive::HASH_ALGO, $src); } $this->relativePath = $relativeFilePath; $this->relativePathLength = strlen($this->relativePath); return $this; } public function validateFile($filePath) { if ($this->hash === '00000000000000000000000000000000') { return true; } $hash = hash_file(DupArchive::HASH_ALGO, $filePath); return ($hash === $this->hash); } public function writeToArchive($archiveHandle) { $headerString = ''; $headerString .= '' . $this->fileSize . ''; $headerString .= '' . $this->mtime . ''; $headerString .= '

' . $this->permissions . '

'; $headerString .= '' . pack('v', $this->flags) . ''; $headerString .= '' . $this->hash . ''; $headerString .= '' . $this->relativePathLength . ''; $headerString .= '' . $this->relativePath . ''; $headerString .= '
'; $bytes_written = @fwrite($archiveHandle, $headerString); if ($bytes_written === false) { throw new Exception('Error writing to file.', DupArchive::EXCEPTION_CODE_EXTRACT_ERROR); } else { return $bytes_written; } } public function readFromArchive($archiveHandle, $skipContents = false, $skipMarker = false) { if (!$skipMarker) { $marker = @fread($archiveHandle, 3); if ($marker === false) { if (feof($archiveHandle)) { return false; } else { throw new Exception('Error reading file header', DupArchive::EXCEPTION_CODE_EXTRACT_ERROR); } } if ($marker != '') { throw new Exception( "Invalid file header marker found [{$marker}] : location " . ftell($archiveHandle), DupArchive::EXCEPTION_CODE_INVALID_MARKER ); } } $this->fileSize = self::getHeaderField($archiveHandle, 'FS'); $this->mtime = self::getHeaderField($archiveHandle, 'MT'); $this->permissions = self::getHeaderField($archiveHandle, 'P'); if (version_compare($this->version, '5.0.0', '<')) { } else { $falgs = self::getHeaderField($archiveHandle, 'X'); $this->flags = unpack('vflags', $falgs)['flags']; } $this->hash = self::getHeaderField($archiveHandle, 'HA'); $this->relativePathLength = self::getHeaderField($archiveHandle, 'RPL'); fread($archiveHandle, 4); $this->relativePath = fread($archiveHandle, $this->relativePathLength); fread($archiveHandle, 9); if ($skipContents && ($this->fileSize > 0)) { $dataSize = 0; $moreGlobs = true; $globHeader = new DupArchiveGlobHeader($this); while ($moreGlobs) { $globHeader->readFromArchive($archiveHandle, true); $dataSize += $globHeader->originalSize; $moreGlobs = ($dataSize < $this->fileSize); } } return $this; } } } namespace Duplicator\Libs\DupArchive\Headers { use Duplicator\Libs\DupArchive\DupArchive; use Exception; class DupArchiveGlobHeader extends AbstractDupArchiveHeader { public $originalSize = 0; public $storedSize = 0; protected $hash = ''; protected $fileHeader = null; public function __construct(DupArchiveFileHeader $fileHeader) { $this->fileHeader = $fileHeader; } public function setHash($content) { $this->hash = hash(DupArchive::HASH_ALGO, $content); } public function checkHash($content, $storedContent) { if (version_compare($this->fileHeader->getVersion(), '5.0.0', '<')) { $hash = hash(DupArchive::HASH_ALGO, $storedContent); } else { $hash = hash(DupArchive::HASH_ALGO, $content); } return ($hash === $this->hash); } public function writeToArchive($archiveHandle) { $headerString = '' . $this->originalSize . '' . $this->storedSize . '' . $this->hash . ''; $bytes_written = @fwrite($archiveHandle, $headerString); if ($bytes_written === false) { throw new Exception('Error writing to file.'); } else { return $bytes_written; } } public function readFromArchive($archiveHandle, $skipGlob = false) { $startElement = fread($archiveHandle, 3); if ($startElement !== '') { throw new Exception("Invalid glob header marker found {$startElement}. location:" . ftell($archiveHandle)); } $this->originalSize = self::getHeaderField($archiveHandle, 'OS'); $this->storedSize = self::getHeaderField($archiveHandle, 'SS'); $this->hash = self::getHeaderField($archiveHandle, 'HA'); fread($archiveHandle, 4); if ($skipGlob) { if (fseek($archiveHandle, $this->storedSize, SEEK_CUR) === -1) { throw new Exception("Can't fseek when skipping glob at location:" . ftell($archiveHandle)); } } return $this; } public function getContentToWrite($content) { if ($this->fileHeader->isCompressed()) { if (($content = gzdeflate($content, 2)) === false) { throw new Exception("Error gzdeflate glob content", DupArchive::EXCEPTION_CODE_EXTRACT_ERROR); } } if ($this->fileHeader->isCrypt()) { if (($content = DupArchive::encrypt($content, $this->fileHeader->getPassword())) === false) { throw new Exception("Error encrypt glob content", DupArchive::EXCEPTION_CODE_EXTRACT_ERROR); } } return $content; } public function readContent($archiveHandle, $validate = false) { if ($this->storedSize == 0) { return 0; } if (($content = fread($archiveHandle, $this->storedSize)) === false) { throw new Exception("Error reading glob content"); } $result = $content; if ($this->fileHeader->isCrypt()) { if (($result = DupArchive::decrypt($result, $this->fileHeader->getPassword())) === false) { throw new Exception("Error decrypt glob content"); } } if ($this->fileHeader->isCompressed()) { if (($result = gzinflate($result)) === false) { throw new Exception("Error gzinflate glob content"); } } if ($validate && $this->checkHash($result, $content) == false) { throw new Exception('Glob validation fail'); } return $result; } } } namespace Duplicator\Libs\DupArchive\Headers { use Duplicator\Libs\DupArchive\DupArchive; use Duplicator\Libs\Snap\SnapIO; use Exception; class DupArchiveHeader extends AbstractDupArchiveHeader { protected $version = ''; protected $flags = 0; protected $password = ''; protected $hashPassword = ''; public function __construct($flags = 0, $verison = '') { $this->version = (strlen($verison) ? $verison : DupArchive::DUPARCHIVE_VERSION); $this->flags = (int) $flags; } public function getVersion() { return $this->version; } public function getFlags() { return $this->flags; } public function setFlags($flags) { $this->flags = (int) $flags; } public function setPassword($pwd) { if (strlen($pwd) == 0) { $this->flags &= ~DupArchive::FLAG_CRYPT; $this->password = ''; $this->hashPassword = ''; } else { $this->flags = $this->flags | DupArchive::FLAG_CRYPT; $this->password = $pwd; $this->hashPassword = self::pwdToHash($pwd, DupArchive::generateSalt(16)); } } public function getPassword() { return $this->password; } public function writeToArchive($archiveHandle) { $content = ''; $content .= '' . $this->version . ''; $content .= '' . pack('v', $this->flags) . ''; $content .= '

' . $this->hashPassword . '

'; $content .= '
'; return SnapIO::fwrite($archiveHandle, $content); } public function isCompressed() { return ($this->flags & DupArchive::FLAG_COMPRESS ? true : false); } public function isCrypt() { return ($this->flags & DupArchive::FLAG_CRYPT ? true : false); } public function readFromArchive($archiveHandle, $password = '') { $startElement = fgets($archiveHandle, 4); if ($startElement != '') { throw new Exception("Invalid archive header marker found {$startElement}", DupArchive::EXCEPTION_CODE_INVALID_MARKER); } $version = self::getHeaderField($archiveHandle, 'V'); $flags = 0; $hashPassword = ''; if (version_compare($version, '5.0.0', '<')) { if (filter_var(self::getHeaderField($archiveHandle, 'C'), FILTER_VALIDATE_BOOLEAN)) { $flags = DupArchive::FLAG_COMPRESS; } $password = ''; } else { $falgs = self::getHeaderField($archiveHandle, 'X'); $flags = unpack('vflags', $falgs)['flags']; $hashPassword = self::getHeaderField($archiveHandle, 'P'); } if (strlen($hashPassword)) { if (preg_match('/^\$(\d)\$rounds=(\d+)\$(.+)\$(.+)$/', $hashPassword, $matches) !== 1) { throw new Exception("Invalid archive stored password", DupArchive::EXCEPTION_CODE_EXTRACT_ERROR); } $algo = $matches[1]; $rounds = $matches[2]; $salt = $matches[3]; $hash = $matches[4]; if (!hash_equals($hashPassword, self::pwdToHash($password, $salt))) { throw new Exception("Invalid archive password", DupArchive::EXCEPTION_CODE_INVALID_PASSWORD); } } $this->flags = $flags; $this->version = $version; $this->password = $password; $this->hashPassword = $hashPassword; fgets($archiveHandle, 5); return $this; } protected function pwdToHash($password, $salt) { return crypt($password, DupArchive::PWD_ALGO . $salt . '$'); } } } namespace Duplicator\Libs\DupArchive\Info { use Duplicator\Libs\DupArchive\Headers\DupArchiveFileHeader; class DupArchiveExpanderInfo { public $archiveHandle = null; public $currentFileHeader = null; public $destDirectory = null; public $directoryWriteCount = 0; public $fileWriteCount = 0; public $enableWrite = false; public function getCurrentDestFilePath() { if ($this->destDirectory != null) { return "{$this->destDirectory}/{$this->currentFileHeader->relativePath}"; } else { return null; } } } } namespace { use Duplicator\Installer\Bootstrap\BootstrapRunner; use Duplicator\Installer\Bootstrap\BootstrapUtils; use Duplicator\Installer\Bootstrap\BootstrapView; use Duplicator\Installer\Bootstrap\LogHandler; Class InstallerBootstrapData { const ARCHIVE_FILENAME = '20221206_d8aed8b1db8cd8afd981d8a7d984d9_4d0f4d93ee81c7642819_20221206150636_archive.daf'; const ARCHIVE_SIZE = '2699272635'; const INSTALLER_DIR_NAME = 'dup-installer'; const PACKAGE_HASH = '4d0f4d9-06150636'; const SECONDARY_PACKAGE_HASH = '9366eb7-06150636'; const VERSION = '4.5.3.1'; } BootstrapUtils::phpVersionCheck(BootstrapRunner::MINIMUM_PHP_VERSION); BootstrapRunner::initSetValues(); $bootError = null; $view = ''; try { $boot = new BootstrapRunner( InstallerBootstrapData::ARCHIVE_FILENAME, InstallerBootstrapData::ARCHIVE_SIZE, InstallerBootstrapData::INSTALLER_DIR_NAME, InstallerBootstrapData::PACKAGE_HASH, InstallerBootstrapData::SECONDARY_PACKAGE_HASH, InstallerBootstrapData::VERSION ); LogHandler::initErrorHandler(array($boot, 'log')); $bootView = new BootstrapView($boot); $view = $boot->run(); } catch (Exception $e) { $boot->log("[ERROR] Boot msg:" . $e->getMessage() . "\n" . $e->getTraceAsString()); $boot->appendErrorMessage($e->getMessage()); $view = BootstrapView::VIEW_ERROR; } switch ($view) { case BootstrapView::VIEW_REDIRECT: $bootView->redirectToInsaller(); break; case BootstrapView::VIEW_ERROR: $bootView->renderError(); break; case BootstrapView::VIEW_PASSWORD: $bootView->renderPassword(); break; } } /* DUPLICATOR_PRO_INSTALLER_EOF */