?
Current Path : /home1/savoy/public_html/wp-content/plugins/duplicator/installer/ |
Linux gator3171.hostgator.com 4.19.286-203.ELK.el7.x86_64 #1 SMP Wed Jun 14 04:33:55 CDT 2023 x86_64 |
Current File : /home1/savoy/public_html/wp-content/plugins/duplicator/installer/installer.tpl |
<?php /** * Bootstrap utility to exatract the core installer * * @package Duplicator\Installer * * Custom params * * [zipmode] to force extraction zip mode * installer.php?zipmode=auto * installer.php?zipmode=ziparchive * installer.php?zipmode=shellexec * * [force-extract-installer] to force dup-installer folder overwrite * installer.php?force-extract-installer=(1|on|yes) * * [dup_folder] to change dup-installer folder name * installer.php?dup_folder=[custom_folder_name] * * [archive] to set custom archvie path location * can be fullpath with archive name or not * installer.php?archive=[archive path] */ namespace { use Duplicator\Libs\DupArchive\DupArchiveExpandBasicEngine; $disabled_dirs = array( 'backups-dup-lite', 'wp-snapshots' ); if (in_array(basename(dirname(__FILE__)), $disabled_dirs)) { die; } define('KB_IN_BYTES', 1024); define('MB_IN_BYTES', 1024 * KB_IN_BYTES); define('GB_IN_BYTES', 1024 * MB_IN_BYTES); define('DUPLICATOR_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); /** * Check if php.ini value is changeable * * @param string $setting php setting * * @return bool */ function isIniValChangeable($setting) { static $ini_all; if (!isset($ini_all)) { $ini_all = false; // Sometimes `ini_get_all()` is disabled via the `disable_functions` option for "security purposes". 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; } @set_time_limit(3600); if (isIniValChangeable('memory_limit')) { @ini_set('memory_limit', DUPLICATOR_PHP_MAX_MEMORY); } if (isIniValChangeable('max_input_time')) { @ini_set('max_input_time', '-1'); } if (isIniValChangeable('pcre.backtrack_limit')) { @ini_set('pcre.backtrack_limit', PHP_INT_MAX); } if (isIniValChangeable('default_socket_timeout')) { @ini_set('default_socket_timeout', 3600); } LogHandler::init_error_handler(); /** * Bootstrap */ class DUPX_Bootstrap { //@@ Params get dynamically swapped when package is built const ARCHIVE_FILENAME = '@@ARCHIVE@@'; const ARCHIVE_SIZE = '@@ARCHIVE_SIZE@@'; const INSTALLER_DIR_NAME = 'dup-installer'; const PACKAGE_HASH = '@@PACKAGE_HASH@@'; const SECONDARY_PACKAGE_HASH = '@@SECONDARY_PACKAGE_HASH@@'; const VERSION = '@@VERSION@@'; const MINIMUM_PHP_VERSION = '5.6.20'; const ZIP_MODE_AUTO = 0; const ZIP_MODE_ARCHIVE = 1; const ZIP_MODE_SHELL = 2; public $targetRoot = null; public $origDupInstFolder = null; public $targetDupInstFolder = null; public $targetDupInst = null; public $manualExtractFileName = null; public $isCustomDupFolder = false; public $hasZipArchive = false; public $hasShellExecUnzip = false; public $mainInstallerURL; public $archiveExpectedSize = 0; public $archiveActualSize = 0; public $archiveRatio = 0; /** @var self */ private static $instance = null; /** * Instantiate the Bootstrap Object */ private function __construct() { $this->setHTTPHeaders(); $this->targetRoot = self::setSafePath(dirname(__FILE__)); // clean log file $this->log('', true); $archive_filepath = $this->getArchiveFilePath(); $this->origDupInstFolder = self::INSTALLER_DIR_NAME; $this->targetDupInstFolder = filter_input(INPUT_GET, 'dup_folder', FILTER_SANITIZE_SPECIAL_CHARS, array( "options" => array( "default" => self::INSTALLER_DIR_NAME, ), 'flags' => FILTER_FLAG_STRIP_HIGH)); $this->isCustomDupFolder = $this->origDupInstFolder !== $this->targetDupInstFolder; $this->targetDupInst = $this->targetRoot . '/' . $this->targetDupInstFolder; $this->manualExtractFileName = 'dup-manual-extract__' . self::PACKAGE_HASH; if ($this->isCustomDupFolder) { $this->extractionTmpFolder = $this->getTempDir($this->targetRoot); } else { $this->extractionTmpFolder = $this->targetRoot; } DUPX_CSRF::init($this->targetDupInst, self::PACKAGE_HASH); //ARCHIVE_SIZE will be blank with a root filter so we can estimate //the default size of the package around 17.5MB (18088000) $archiveActualSize = @file_exists($archive_filepath) ? @filesize($archive_filepath) : false; $archiveActualSize = ($archiveActualSize !== false) ? $archiveActualSize : 0; $this->hasZipArchive = class_exists('ZipArchive'); $this->hasShellExecUnzip = $this->getUnzipFilePath() != null ? true : false; $this->archiveExpectedSize = strlen(self::ARCHIVE_SIZE) ? self::ARCHIVE_SIZE : 0; $this->archiveActualSize = $archiveActualSize; if ($this->archiveExpectedSize > 0) { $this->archiveRatio = (((1.0) * $this->archiveActualSize) / $this->archiveExpectedSize) * 100; } else { $this->archiveRatio = 100; } } /** * * @return self */ public static function getInstance() { if (is_null(self::$instance)) { self::$instance = new self(); } return self::$instance; } /** * Makes sure no caching mechanism is used during install * * @return void */ 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"); } /** * Return temp dir * * @param string $path path string * * @return boolean|string */ 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; } /** * Check php version * * @return void */ public static function phpVersionCheck() { if (version_compare(PHP_VERSION, self::MINIMUM_PHP_VERSION, '>=')) { return true; } $match = null; if (preg_match("#^\d+(\.\d+)*#", PHP_VERSION, $match)) { $phpVersion = $match[0]; } else { $phpVersion = PHP_VERSION; } ?><!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="robots" content="noindex,nofollow"> <title>Duplicator - issue</title> </head> <body> <div> <h1>DUPLICATOR ISSUE: PHP <?php echo self::MINIMUM_PHP_VERSION; ?> REQUIRED</h1> <p> This server is running PHP: <b><?php echo $phpVersion; ?></b>. <i>A minimum of <b>PHP <?php echo self::MINIMUM_PHP_VERSION; ?></b> is required</i>.<br><br> <b>Contact your hosting provider or server administrator and let them know you would like to upgrade your PHP version.</b> </p> </div> </body> </html> <?php die(); } /** * Run the bootstrap process which includes checking for requirements and running * the extraction process * * @return null | string Returns null if the run was successful otherwise an error message */ 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' . self::VERSION . '=='); $this->log('----------------------------------------------------'); $this->log('Installer bootstrap start'); $archive_filepath = $this->getArchiveFilePath(); $archive_filename = self::ARCHIVE_FILENAME; $error = null; $is_installer_file_valid = true; if (preg_match('/_([a-z0-9]{7})[a-z0-9]+_[0-9]{6}([0-9]{8})_archive.(?:zip|daf)$/', $archive_filename, $matches)) { $expected_package_hash = $matches[1] . '-' . $matches[2]; if (self::PACKAGE_HASH != $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) { $error = "Installer and archive mismatch detected. Ensure uncorrupted installer and matching archive are present."; return $error; } $extract_installer = true; $extract_success = false; $archiveExpectedEasy = $this->readableByteSize($this->archiveExpectedSize); $archiveActualEasy = $this->readableByteSize($this->archiveActualSize); //$archive_extension = strtolower(pathinfo($archive_filepath)['extension']); $archive_extension = strtolower(pathinfo($archive_filepath, PATHINFO_EXTENSION)); $installer_dir_found = ( file_exists($this->targetDupInst) && file_exists($this->targetDupInst . "/main.installer.php") && file_exists($this->targetDupInst . "/dup-archive__" . self::PACKAGE_HASH . ".txt") ); $manual_extract_found = ( $installer_dir_found && file_exists($this->targetDupInst . "/" . $this->manualExtractFileName) ); $isZip = ($archive_extension == 'zip'); //MANUAL EXTRACTION NOT FOUND if (!$manual_extract_found) { //MISSING ARCHIVE FILE if (!file_exists($archive_filepath)) { $this->log("[ERROR] Archive file not found!"); $archive_candidates = ($isZip) ? $this->getFilesWithExtension('zip') : $this->getFilesWithExtension('daf'); $candidate_count = count($archive_candidates); $candidate_html = "- No {$archive_extension} files found -"; if ($candidate_count >= 1) { $candidate_html = "<ol>"; foreach ($archive_candidates as $archive_candidate) { $candidate_html .= '<li class="diff-list"> ' . $this->compareStrings($archive_filename, $archive_candidate) . '</li>'; } $candidate_html .= "</ol>"; } $error = "<style>.diff-list font { font-weight: bold; }</style>" . "<b>Archive not found!</b> The required archive file must be present in the <i>'Extraction Path'</i> below. " . "When the archive file name was created it was named with a secure file name. This file name must be " . "the <i>exact same</i> 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:<br/>" . "<ul>" . "<li>If the archive is not finished downloading please wait for it to complete.</li>" . "<li>Rename the file to it original hash name. See WordPress-Admin ❯ Packages ❯ Details. </li>" . "<li>When downloading, both files both should be from the same package line. </li>" . "<li>Also see: <a href='https://duplicator.com/knowledge-base/how-to-fix-general-installer-ui-bootstrap-archive-issues' target='_blank'>" . "How to fix various errors that show up before step-1 of the installer?</a></li>" . "</ul>"; return $error; } $archive_size = self::ARCHIVE_SIZE; // Sometimes the self::ARCHIVE_SIZE is ''. if (!empty($archive_size) && !self::checkInputValidInt(self::ARCHIVE_SIZE)) { $no_of_bits = PHP_INT_SIZE * 8; $error = 'Current is a ' . $no_of_bits . '-bit SO. This archive is too large for ' . $no_of_bits . '-bit PHP.' . '<br>'; $this->log('[ERROR] ' . $error); $error .= 'Possibibles solutions:<br>'; $error .= '- Use the file filters to get your package lower to support this server or try the package on a Linux server.' . '<br>'; $error .= '- Perform a <a target="_blank" href="https://duplicator.com/knowledge-base/how-to-handle-various-install-scenarios">' . 'Manual Extract Install</a>' . '<br>'; switch ($no_of_bits == 32) { case 32: $error .= '- Ask your host to upgrade the server to 64-bit PHP or install on another system has 64-bit PHP' . '<br>'; break; case 64: $error .= '- Ask your host to upgrade the server to 128-bit PHP or install on another system has 128-bit PHP' . '<br>'; break; } if (self::isWindows()) { $error .= '- <a target="_blank" href="https://duplicator.com/knowledge-base/how-to-work-with-daf-files-and-the-duparchive-extraction-tool">' . 'Windows DupArchive extractor</a> to extract all files from the archive.' . '<br>'; } return $error; } //SIZE CHECK ERROR 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"); $percent = round($this->archiveRatio); $autochecked = isset($_POST['auto-fresh']) ? "checked='true'" : ''; $error = "<b>Archive file size warning.</b><br/> The expected archive size is <b class='pass'>[{$archiveExpectedEasy}]</b>. " . "Currently the archive size is <b class='fail'>[{$archiveActualEasy}]</b>. <br/>" . "The archive file may have <b>not fully been uploaded to the server.</b>" . "<ul>" . "<li>Download the whole archive from the source website (open WordPress Admin > Duplicator > Packages) " . "and validate that the file size is close to the expected size. </li>" . "<li>Make sure to upload the whole archive file to the destination server.</li>" . "<li>If the archive file is still uploading then please refresh this page to get an update on the currently uploaded file size.</li>" . "</ul>"; return $error; } } if ($installer_dir_found) { // INSTALL DIRECTORY: Check if its setup correctly AND we are not in overwrite mode 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 ($extract_installer && file_exists($this->targetDupInst)) { 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( // file pattern => hash file 'dup-archive__' . $hash_pattern . '.txt' => 'dup-archive__' . self::PACKAGE_HASH . '.txt', 'dup-database__' . $hash_pattern . '.sql' => 'dup-database__' . self::PACKAGE_HASH . '.sql', 'dup-installer-data__' . $hash_pattern . '.sql' => 'dup-installer-data__' . self::PACKAGE_HASH . '.sql', 'dup-installer-log__' . $hash_pattern . '.txt' => 'dup-installer-log__' . self::PACKAGE_HASH . '.txt', 'dup-scan__' . $hash_pattern . '.json' => 'dup-scan__' . self::PACKAGE_HASH . '.json', 'dup-scanned-dirs__' . $hash_pattern . '.txt' => 'dup-scanned-dirs__' . self::PACKAGE_HASH . '.txt', 'dup-scanned-files__' . $hash_pattern . '.txt' => 'dup-scanned-files__' . self::PACKAGE_HASH . '.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 { $error .= '[ERROR] Error deleting the file ' . $glob . ' Please manually delete it and try again.'; $this->log($error); } } } } } } //ATTEMPT EXTRACTION: //ZipArchive and Shell Exec if ($extract_installer) { $this->log("Ready to extract the installer"); $this->log("Checking permission of destination folder"); $destination = $this->targetRoot; if (!is_writable($destination)) { $this->log("destination folder for extraction is not writable"); if (self::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."); $error = "NOTICE: The {$destination} directory is not writable on this server please talk to your host or server admin about making "; $error .= "<a target='_blank' href='https://duplicator.com/knowledge-base/how-to-fix-file-permissions-issues'>" . "writable {$destination} directory</a> on this server. <br/>"; return $error; } if ($isZip) { $zip_mode = $this->getZipMode(); if (($zip_mode == self::ZIP_MODE_AUTO) || ($zip_mode == self::ZIP_MODE_ARCHIVE) && class_exists('ZipArchive')) { if ($this->hasZipArchive) { $this->log("ZipArchive exists so using that"); $extract_success = $this->extractInstallerZipArchive($archive_filepath, $this->origDupInstFolder, $this->extractionTmpFolder); if ($extract_success) { $this->log('Successfully extracted with ZipArchive'); } else { if (0 == $this->installer_files_found) { $error = "[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($error); return $error; } else { $error = '[ERROR] Error extracting with ZipArchive. '; $this->log($error); } } } else { $this->log("WARNING: ZipArchive is not enabled."); $error = "NOTICE: ZipArchive is not enabled on this server please talk to your host or server admin about enabling "; $error .= "<a target='_blank' href='https://duplicator.com/knowledge-base/how-to-work-with-the-different-zip-engines'>" . "ZipArchive</a> on this server. <br/>"; } } if (!$extract_success) { if (($zip_mode == self::ZIP_MODE_AUTO) || ($zip_mode == self::ZIP_MODE_SHELL)) { $unzip_filepath = $this->getUnzipFilePath(); if ($unzip_filepath != null) { $extract_success = $this->extractInstallerShellexec($archive_filepath, $this->origDupInstFolder, $this->extractionTmpFolder); $this->log("Resetting perms of items in folder {$this->targetDupInstFolder}"); self::setPermsToDefaultR($this->targetDupInstFolder); if ($extract_success) { $this->log('Successfully extracted with Shell Exec'); $error = null; } else { $error .= '[ERROR] Error extracting with Shell Exec. ' . 'Please manually extract archive then choose Advanced > Manual Extract in installer.'; $this->log($error); } } else { $this->log('WARNING: Shell Exec Zip is not available'); $error .= "NOTICE: Shell Exec is not enabled on this server please talk to your host or server admin about enabling "; $error .= "<a target='_blank' href='http://php.net/manual/en/function.shell-exec.php'>Shell Exec</a> " . "on this server or manually extract archive then choose Advanced > Manual Extract in installer."; } } } // If both ZipArchive and ShellZip are not available, Error message should be combined for both if (!$extract_success && $zip_mode == self::ZIP_MODE_AUTO) { $unzip_filepath = $this->getUnzipFilePath(); if (!class_exists('ZipArchive') && empty($unzip_filepath)) { $this->log("WARNING: ZipArchive and Shell Exec are not enabled on this server."); $error = "NOTICE: ZipArchive and Shell Exec are not enabled on this server please " . "talk to your host or server admin about enabling "; $error .= "<a target='_blank' href='https://duplicator.com/knowledge-base/how-to-work-with-the-different-zip-engines'>ZipArchive</a> " . "or <a target='_blank' href='http://php.net/manual/en/function.shell-exec.php'>Shell Exec</a> " . "on this server or manually extract archive then choose Advanced > Manual Extract in installer."; } } } else { try { DupArchiveExpandBasicEngine::setCallbacks( array($this, 'log'), array($this, 'chmod'), array($this, 'mkdir') ); $offset = DupArchiveExpandBasicEngine::getExtraOffset($archive_filepath); $this->log('Expand directory from offset ' . $offset); DupArchiveExpandBasicEngine::expandDirectory( $archive_filepath, $this->origDupInstFolder, $this->extractionTmpFolder, false, $offset ); //In case of DupArchive just remove the manual extract check file @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 (self::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_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"); } } else { $this->log("NOTICE: Didn't need to extract the installer."); } if (empty($error)) { 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)) { $error = '<b>Archive config file not found in ' . $this->targetDupInstFolder . ' folder.</b> <br><br>'; return $error; } } $uri_start = self::getCurrentUrl(false, false, 1); if ($error === null) { if (!file_exists($this->targetDupInst)) { $error = 'Can\'t extract installer directory. ' . 'See <a target="_blank" href="https://duplicator.com/knowledge-base/how-to-fix-installer-archive-extraction-issues/">this FAQ item</a>' . ' for details on how to resolve.</a>'; } if ($error == null) { $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->mainInstallerURL = $this->mainInstallerURL . "?archive=$encoded_archive_path&bootloader=$bootloader_name&ctrl_action=ctrl-step1"; /* if (isset($_SERVER['QUERY_STRING']) && !empty($_SERVER['QUERY_STRING'])) { $this->mainInstallerURL .= '?'.$_SERVER['QUERY_STRING']; }*/ $this->log("DONE: No detected errors so redirecting to the main installer. Main Installer URI = {$this->mainInstallerURL}"); } } return $error; } /** * Get current url * * @param bool $queryString If true the query string will also be returned. * @param bool $requestUri if true check request uri * @param int $getParentDirLevel if 0 get current script name or parent folder, if 1 parent folder if 2 parent of parent folder ... * * @return void */ public static function getCurrentUrl($queryString = true, $requestUri = false, $getParentDirLevel = 0) { // *** HOST 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 } // *** PROTOCOL 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 { // *** SCRIPT NAME $serverUrlSelf = $_SERVER['SCRIPT_NAME']; for ($i = 0; $i < $getParentDirLevel; $i++) { $serverUrlSelf = preg_match('/^[\\\\\/]?$/', dirname($serverUrlSelf)) ? '' : dirname($serverUrlSelf); } } // *** QUERY STRING $query = ($queryString && isset($_SERVER['QUERY_STRING']) && strlen($_SERVER['QUERY_STRING']) > 0 ) ? '?' . $_SERVER['QUERY_STRING'] : ''; return $protocol . '://' . $host . $serverUrlSelf . $query; } /** * Attempts to set the 'dup-installer' directory permissions * * @return null */ 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); } /** * 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' or 'u+rw' * @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' or 'u+rw' * * @return bool Returns true if the permission was properly set */ private function setPermsOnItem($path, $perms) { if (($result = self::chmod($path, $perms)) === false) { $this->log("ERROR: Couldn't set permissions of $path<br/>"); } else { $this->log("Set permissions of $path<br/>"); } return $result; } /** * Compare two strings and return html text which represts diff * * @param string $oldString old string * @param string $newString new string * * @return string Returns html text */ private function compareStrings($oldString, $newString) { $ret = ''; for ($i = 0; isset($oldString[$i]) || isset($newString[$i]); $i++) { if (!isset($oldString[$i])) { $ret .= '<font color="red">' . $newString[$i] . '</font>'; continue; } for ($char = 0; isset($oldString[$i][$char]) || isset($newString[$i][$char]); $char++) { if (!isset($oldString[$i][$char])) { $ret .= '<font color="red">' . substr($newString[$i], $char) . '</font>'; break; } elseif (!isset($newString[$i][$char])) { break; } if (ord($oldString[$i][$char]) != ord($newString[$i][$char])) { $ret .= '<font color="red">' . $newString[$i][$char] . '</font>'; } else { $ret .= $newString[$i][$char]; } } } return $ret; } /** * Logs a string to the dup-installer-bootlog__[HASH].txt file * * @param string $s The string to log to the log file * @param bool $deleteOld if true delete old file * * @return boog|int This function returns the number of bytes that were written to the file, or FALSE on failure. */ 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 . '] ' . self::postprocessLog($s) . "\n", FILE_APPEND); } /** * get boot log file name the dup-installer-bootlog__[HASH].txt file * * @return string */ public function getBootLogFilePath() { return $this->targetRoot . '/dup-installer-bootlog__' . self::SECONDARY_PACKAGE_HASH . '.txt'; } /** * Post process log and remove hash string * * @param string $str string * * @return string */ protected static function postprocessLog($str) { return str_replace(array( self::getArchiveFileHash(), self::PACKAGE_HASH, self::SECONDARY_PACKAGE_HASH ), '[HASH]', $str); } /** * Return archive file hash * * @return string */ public static function getArchiveFileHash() { static $fileHash = null; if (is_null($fileHash)) { $fileHash = preg_replace('/^.+_([a-z0-9]+)_[0-9]{14}_archive\.(?:daf|zip)$/', '$1', self::ARCHIVE_FILENAME); } return $fileHash; } /** * Extraxt installer * * @param string $archive_filepath The path to the archive file. * @param string $origDupInstFolder relative folder in archive * @param string $destination destination folder * @param bool $checkSubFolder check if is in subfolder * * @return bool Returns true if the data was properly extracted */ 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("Successfully opened archive file."); $folder_prefix = $origDupInstFolder . '/'; $this->log("Extracting all files from archive within " . $origDupInstFolder); $installer_files_found = 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(self::setSafePath($stat['name']), '/'); $tmpArray = explode('/', $safePath); if (count($tmpArray) < 2) { continue; } $tmpSubFolder = $tmpArray[0]; array_shift($tmpArray); $filenameCheck = implode('/', $tmpArray); $filename = $stat['name']; } if ($this->startsWith($filenameCheck, $folder_prefix)) { $installer_files_found++; 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} from archive archive file"); $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 snaplib files aren't present attempt to extract and copy those 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 ($this->startsWith($filename, $folder_prefix)) { $installer_files_found++; 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 && $installer_files_found < 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"); $this->extractInstallerZipArchive($archive_filepath, $origDupInstFolder, $destination, true); } } } else { $this->log("[ERROR] Couldn't open archive archive file with ZipArchive CODE[" . $zipOpenRes . "]"); $success = false; } return $success; } /** * return true if current SO is windows * * @staticvar bool $isWindows * @return bool */ public static function isWindows() { static $isWindows = null; if (is_null($isWindows)) { $isWindows = (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'); } return $isWindows; } /** * return current SO path path len * @staticvar int $maxPath * @return int */ public static function maxPathLen() { static $maxPath = null; if (is_null($maxPath)) { if (defined('PHP_MAXPATHLEN')) { $maxPath = PHP_MAXPATHLEN; } else { // for PHP < 5.3.0 $maxPath = self::isWindows() ? 260 : 4096; } } return $maxPath; } /** * @param string $directory Path for folder to set perms * * @return void */ public static function setPermsToDefaultR($directory) { $dir = new RecursiveDirectoryIterator($directory, FilesystemIterator::SKIP_DOTS); $iterator = new RecursiveIteratorIterator($dir, RecursiveIteratorIterator::SELF_FIRST); // Default permissions $defaultFilePermission = 0666 & ~umask(); $defaultDirPermission = 0777 & ~umask(); foreach ($iterator as $item) { if ($item->isFile()) { self::chmod($item->getPathname(), $defaultFilePermission); } if ($item->isDir()) { self::chmod($item->getPathname(), $defaultDirPermission); } } } /** * this function make a chmod only if the are different from perms input and if chmod function is enabled * * this function handles the variable MODE in a way similar to the chmod of lunux * So the MODE variable can be * 1) an octal number (0755) * 2) a string that defines an octal number ("644") * 3) a string with the following format [ugoa]*([-+=]([rwx]*)+ * * examples * u+rw add read and write at the user * u+rw,uo-wx add read and write ad the user and remove wx at groupd and other * a=rw is equal at 666 * u=rwx,go-rwx is equal at 700 * * @param string $file file path * @param int|string $mode mode * * @return boolean */ 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; } // start by file permission $octalMode = (fileperms($file) & 0777); foreach ($gMatch as $matches) { // [ugo] or a = ugo $group = $matches[1]; if ($group === 'a') { $group = 'ugo'; } // can be + - = $action = $matches[2]; // [rwx] $gPerms = $matches[3]; // reset octal group perms $octalGroupMode = 0; // Init sub perms $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 === '=') { // generate octal group permsissions and ugo mask invert $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; } } // apply = action $octalMode = $octalMode & ($ugoMaskInvert | $octalGroupMode); } else { // generate octal group permsissions 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; } } // apply + or - action switch ($action) { case '+': $octalMode = $octalMode | $octalGroupMode; break; case '-': $octalMode = $octalMode & ~$octalGroupMode; break; } } } } } // if input permissions are equal at file permissions return true without performing chmod if (function_exists('fileperms') && $octalMode === (fileperms($file) & 0777)) { return true; } if (!function_exists('chmod')) { return false; } return @chmod($file, $octalMode); } /** * Check if input is valid int * * @param mixed $input input string or number * * @return bool */ public static function checkInputValidInt($input) { return (filter_var($input, FILTER_VALIDATE_INT) === 0 || filter_var($input, FILTER_VALIDATE_INT)); } /** * this function creates a folder if it does not exist and performs a chmod. * it is different from the normal mkdir function to which an umask is applied to the input permissions. * * this function handles the variable MODE in a way similar to the chmod of lunux * So the MODE variable can be * 1) an octal number (0755) * 2) a string that defines an octal number ("644") * 3) a string with the following format [ugoa]*([-+=]([rwx]*)+ * * @param string $path folder path * @param int|string $mode mode permissions * @param bool $recursive Allows the creation of nested directories specified in the pathname. Default to false. * @param resource $context not used for windows bug * * @return boolean bool TRUE on success or FALSE on failure. * * @todo check recursive true and multiple chmod */ public static function mkdir($path, $mode = 0777, $recursive = false, $context = null) { if (strlen($path) > self::maxPathLen()) { throw new Exception('Skipping a file that exceeds allowed max path length [' . self::maxPathLen() . ']. File: ' . $filepath); } if (!file_exists($path)) { if (!function_exists('mkdir')) { return false; } if (!@mkdir($path, 0777, $recursive)) { return false; } } return self::chmod($path, $mode); } /** * move all folder content up to parent * * @param string $subFolderName full path * @param boolean $deleteSubFolder if true delete subFolder after moved all * * @return boolean */ 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 = self::rrmdir($destination); } if ($success) { $success = rename($cName, $destination); } else { break; } } if ($success && $deleteSubFolder) { $success = self::rrmdir($subFolderName, true); } } if (!$success) { $this->log("[ERROR] Problem om moveUpfromSubFolder subFolder:" . $subFolderName); } return $success; } /** * Extracts only the 'dup-installer' files using Shell-Exec Unzip * * @param string $archive_filepath The path to the archive file. * @param string $origDupInstFolder dup-installer folder * @param string $destination destination folder * * @return bool */ private function extractInstallerShellexec($archive_filepath, $origDupInstFolder, $destination) { $success = false; $this->log("Attempting to use Shell Exec"); $unzip_filepath = $this->getUnzipFilePath(); if ($unzip_filepath != null) { $unzip_command = "$unzip_filepath -q $archive_filepath " . $origDupInstFolder . '/* -d ' . $destination . ' -x ' . $origDupInstFolder . '/' . $this->manualExtractFileName . ' 2>&1'; $this->log("Executing $unzip_command"); $stderr = shell_exec($unzip_command); $lib_directory = $destination . '/' . $origDupInstFolder . '/lib'; $snaplib_directory = $lib_directory . '/snaplib'; // If snaplib files aren't present attempt to extract and copy those if (!file_exists($snaplib_directory)) { $local_lib_directory = $destination . '/snaplib'; $unzip_command = "$unzip_filepath -q $archive_filepath snaplib/* -d '.$destination.' 2>&1"; $this->log("Executing unzip command"); $stderr .= shell_exec($unzip_command); self::mkdir($lib_directory, 'u+rwx'); rename($local_lib_directory, $snaplib_directory); } if ($stderr == '') { $this->log("Shell exec unzip succeeded"); $success = true; } else { $this->log("[ERROR] Shell exec unzip failed. Output={$stderr}"); } } return $success; } /** * Attempts to get the archive file path * * @return string The full path to the archive file */ 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 . '/' . self::ARCHIVE_FILENAME)) { $archive_filepath = $archive_filepath . '/' . self::ARCHIVE_FILENAME; } else { $archive_filepath = $archive_filepath; } } else { $archive_filepath = $this->targetRoot . '/' . self::ARCHIVE_FILENAME; } if (($realPath = realpath($archive_filepath)) !== false) { return $realPath; } else { return $archive_filepath; } } /** * Gets the enum type that should be used * * @return int Returns the current zip mode enum */ 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; } } return $zip_mode; } /** * Checks to see if a string starts with specific characters * * @param string $haystack haystack * @param string $needle needle * * @return bool */ 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; } } if (! function_exists('shell_exec')) { return false; } // Can we issue a simple echo command? if (!@shell_exec('echo duplicator')) { return false; } return true; } /** * Gets the possible system commands for unzip on Linux * * @return string Returns unzip file path that can execute the unzip command */ public function getUnzipFilePath() { $filepath = null; if ($this->hasShellExec()) { if (shell_exec('hash unzip 2>&1') == null) { $filepath = 'unzip'; } else { $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', // RSR TODO put back in when we support shellexec on windows, ); foreach ($possible_paths as $path) { if (file_exists($path)) { $filepath = $path; break; } } } } return $filepath; } /** * Display human readable byte sizes such as 150MB * * @param int $size The size in bytes * * @return string A readable byte size format such as 100MB */ public 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"; } } /** * Returns an array of zip files found in the current executing directory * * @param string $extension extenstion file * * @return array of zip files */ public function getFilesWithExtension($extension) { $files = array(); foreach (glob("*.{$extension}") as $name) { if (file_exists($name)) { $files[] = $name; } } if (count($files) > 0) { return $files; } //FALL BACK: Windows XP has bug with glob, //add secondary check for PHP lameness 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; } /** * Safely remove a directory and recursively files and directory upto multiple sublevels * * @param string $path The full path to the directory to remove * * @return bool Returns true if all content was removed */ 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; } } } /** * Makes path safe for any OS for PHP * * Paths should ALWAYS READ be "/" * uni: /home/path/file.txt * win: D:/home/path/file.txt * * @param string $path TThe path to make safe * * @return string The original $path with a with all slashes facing '/'. */ public static function setSafePath($path) { return str_replace("\\", "/", $path); } } class LogHandler { /** @var bool */ private static $initialized = false; /** * This function only initializes the error handler the first time it is called * * @return void */ public static function init_error_handler() { if (!self::$initialized) { @set_error_handler(array(__CLASS__, 'error')); @register_shutdown_function(array(__CLASS__, 'shutdown')); self::$initialized = true; } } /** * Error handler * * @param integer $errno Error level * @param string $errstr Error message * @param string $errfile Error file * @param integer $errline Error line * @return void */ public static function error($errno, $errstr, $errfile, $errline) { switch ($errno) { case E_ERROR: $log_message = self::getMessage($errno, $errstr, $errfile, $errline); if (DUPX_Bootstrap::getInstance()->log($log_message) === false) { $log_message = "Can\'t wrinte logfile\n\n" . $log_message; } die('<pre>' . htmlspecialchars($log_message) . '</pre>'); break; case E_NOTICE: case E_WARNING: default: $log_message = self::getMessage($errno, $errstr, $errfile, $errline); DUPX_Bootstrap::getInstance()->log($log_message); break; } } /** * Get message from error * * @param int $errno errno * @param string $errstr message * @param string $errfile file * @param int $errline line * * @return string */ 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; } /** * Shutdown handler * * @return void */ public static function shutdown() { if (($error = error_get_last())) { LogHandler::error($error['type'], $error['message'], $error['file'], $error['line']); } } } class DUPX_CSRF { private static $packagHash = null; private static $mainFolder = null; /** * Session var name prefix * @var string */ public static $prefix = '_DUPX_CSRF'; /** * Stores all CSRF values: Key as CSRF name and Val as CRF value * @var array */ private static $CSRFVars = null; /** * Init CSRF * * @param string $mainFolderm main folder * @param string $packageHash hash * * @return void */ public static function init($mainFolderm, $packageHash) { self::$mainFolder = $mainFolderm; self::$packagHash = $packageHash; self::$CSRFVars = null; } /** * Set new CSRF * * @param string $key CSRF Key * @param string $val CSRF Val * * @return Void */ public static function setKeyVal($key, $val) { $CSRFVars = self::getCSRFVars(); $CSRFVars[$key] = $val; self::saveCSRFVars($CSRFVars); self::$CSRFVars = null; } /** * Get CSRF value by passing CSRF key * * @param string $key CSRF key * * @return string|boolean If CSRF value set for give n Key, It returns CRF value otherise returns false */ public static function getVal($key) { $CSRFVars = self::getCSRFVars(); if (isset($CSRFVars[$key])) { return $CSRFVars[$key]; } else { return false; } } /** * Generate DUPX_CSRF value for form * * @param string $form Form name as session key * * @return string token */ public static function generate($form = null) { $keyName = self::getKeyName($form); $existingToken = self::getVal($keyName); if (false !== $existingToken) { $token = $existingToken; } else { $token = DUPX_CSRF::token() . DUPX_CSRF::fingerprint(); } self::setKeyVal($keyName, $token); return $token; } /** * Check DUPX_CSRF value of form * * @param string $token Token * @param string $form Form name as session key * * @return boolean */ public static function check($token, $form = null) { if (empty($form)) { return false; } $keyName = self::getKeyName($form); $CSRFVars = self::getCSRFVars(); if (isset($CSRFVars[$keyName]) && $CSRFVars[$keyName] == $token) { // token OK return true; } return false; } /** Generate token * * @return string */ 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); } /** Returns "digital fingerprint" of user * * @return string - MD5 hashed data */ protected static function fingerprint() { return strtoupper(md5(implode('|', array($_SERVER['REMOTE_ADDR'], $_SERVER['HTTP_USER_AGENT'])))); } /** * Generate CSRF Key name * * @param string $form the form name for which CSRF key need to generate * @return string CSRF key */ private static function getKeyName($form) { return DUPX_CSRF::$prefix . '_' . $form; } /** * Get Package hash * * @return string Package hash */ private static function getPackageHash() { if (is_null(self::$packagHash)) { throw new Exception('Not init CSFR CLASS'); } return self::$packagHash; } /** * Get file path where CSRF tokens are stored in JSON encoded format * * @return string file path where CSRF token stored */ private 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; } /** * Get all CSRF vars in array format * * @return array Key as CSRF name and value as CSRF value */ 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; } /** * Stores all CSRF vars * * @param array $CSRFVars holds all CSRF key val * @return void */ private static function saveCSRFVars($CSRFVars) { $contents = json_encode($CSRFVars); $filePath = self::getFilePath(); file_put_contents($filePath, $contents); } } /* * * CLASS DEFINITION END ** */ $auto_refresh = isset($_POST['auto-fresh']) ? true : false; DUPX_Bootstrap::phpVersionCheck(); try { $boot = DUPX_Bootstrap::getInstance(); $boot_error = $boot->run(); } catch (Exception $e) { $boot_error = $e->getMessage(); } if ($boot_error == null) { $secure_csrf_token = DUPX_CSRF::generate('secure'); $ctrl_csrf_token = DUPX_CSRF::generate('ctrl-step1'); DUPX_CSRF::setKeyVal('installerOrigCall', DUPX_Bootstrap::getCurrentUrl()); DUPX_CSRF::setKeyVal('installerOrigPath', __FILE__); DUPX_CSRF::setKeyVal('archive', $boot->archive); DUPX_CSRF::setKeyVal('bootloader', $boot->bootloader); DUPX_CSRF::setKeyVal('booturl', '//' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']); DUPX_CSRF::setKeyVal('bootLogFile', $boot->getBootLogFilePath()); DUPX_CSRF::setKeyVal('package_hash', DUPX_Bootstrap::PACKAGE_HASH); DUPX_CSRF::setKeyVal('secondaryHash', DUPX_Bootstrap::SECONDARY_PACKAGE_HASH); } ?> <html> <?php if ($boot_error == null) : ?> <head> <meta name="robots" content="noindex,nofollow"> <title>Duplicator Installer</title> <link rel="icon" href="data:;base64,iVBORw0KGgo="> </head> <body> <div style="text-align: center; margin-top: 100px; font-size: 20px;"> Initializing Installer. Please wait... </div> <?php $id = uniqid(); $html = "<form id='{$id}' method='post' action='{$boot->mainInstallerURL}' />\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 .= "<input type='hidden' name='{$name}' value='{$value}' />\n"; } $html .= "</form>\n"; $html .= "<script>window.onload = function() { document.getElementById('{$id}').submit(); }</script>"; echo $html; ?> </body> <?php else : ?> <head> <link rel="icon" href="data:;base64,iVBORw0KGgo="> <style> body {font-family:Verdana,Arial,sans-serif; line-height:18px; font-size: 12px} h2 {font-size:20px; margin:5px 0 5px 0; border-bottom:1px solid #dfdfdf; padding:3px} div#content { border:1px solid #CDCDCD; width:750px; min-height:550px; margin:auto; margin-top:18px; border-radius:5px; box-shadow:0 8px 6px -6px #333; font-size:13px; } div#content-inner {padding:10px 30px; min-height:550px} /* Header */ table.header-wizard { border-top-left-radius:5px; border-top-right-radius:5px; width:100%; box-shadow:0 5px 3px -3px #999; background-color:#F1F1F1; font-weight:bold; } table.header-wizard td.header {font-size:24px; padding:7px 0 7px 0; width:100%;} div.dupx-logfile-link {float:right; font-weight:normal; font-size:12px} .dupx-version { white-space:nowrap; color:#999; font-size:11px; font-style:italic; text-align:right; padding:0 15px 5px 0; line-height:14px; font-weight:normal } .dupx-version a { color:#999; } div.errror-notice {text-align:center; font-style:italic; font-size:11px} div.errror-msg { color:maroon; padding: 10px 0 5px 0} .pass {color:green} .fail {color:red} span.file-info {font-size: 11px; font-style: italic} div.skip-not-found {padding:10px 0 5px 0;} div.skip-not-found label {cursor: pointer} table.settings {width:100%; font-size:12px} table.settings td {padding: 4px} table.settings td:first-child {font-weight: bold} .w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{ color:#000!important;background-color:#f1f1f1!important } .w3-container:after,.w3-container:before,.w3-panel:after, .w3-panel:before,.w3-row:after,.w3-row:before, .w3-row-padding:after,.w3-row-padding:before, .w3-cell-row:before,.w3-cell-row:after, .w3-clear:after,.w3-clear:before,.w3-bar:before,.w3-bar:after { content:"";display:table;clear:both } .w3-green,.w3-hover-green:hover{color:#fff!important;background-color:#4CAF50!important} .w3-container{padding:0.01em 16px} .w3-center{display:inline-block;width:auto; text-align: center !important} </style> </head> <body> <div id="content"> <table cellspacing="0" class="header-wizard"> <tr> <td class="header"> Duplicator - Bootloader</div</td> <td class="dupx-version"> version: <?php echo htmlentities(DUPX_Bootstrap::VERSION); ?> <br/> » <a target='_blank' href='dup-installer-bootlog__<?php echo DUPX_Bootstrap::SECONDARY_PACKAGE_HASH; ?>.txt'> dup-installer-bootlog__[HASH].txt </a> </td> </tr> </table> <form id="error-form" method="post"> <div id="content-inner"> <h2 style="color:maroon">Setup Notice</h2> <div class="errror-notice"> An error has occurred. In order to load the full installer please resolve the issue below. </div> <div class="errror-msg"> <?php echo $boot_error ?> </div> <br/><br/> <h2>Server Settings</h2> <table class='settings'> <tr> <td>ZipArchive:</td> <td><?php echo $boot->hasZipArchive ? '<i class="pass">Enabled</i>' : '<i class="fail">Disabled</i>'; ?> </td> </tr> <tr> <td>Shell Unzip:</td> <td><?php echo $boot->hasShellExecUnzip ? '<i class="pass">Enabled</i>' : '<i class="fail">Disabled</i>'; ?> </td> </tr> <tr> <td>Extraction Path:</td> <td><?php echo $boot->targetRoot; ?></td> </tr> <tr> <td>Installer Path:</td> <td><?php echo $boot->targetDupInstFolder; ?></td> </tr> <tr> <td>Archive Size:</td> <td> <b>Expected Size:</b> <?php echo $boot->readableByteSize($boot->archiveExpectedSize); ?> <b>Actual Size:</b> <?php echo $boot->readableByteSize($boot->archiveActualSize); ?> </td> </tr> <tr> <td>Boot Log</td> <td> <a target='_blank' href='dup-installer-bootlog__<?php echo DUPX_Bootstrap::SECONDARY_PACKAGE_HASH; ?>.txt'> dup-installer-bootlog__[HASH].txt </a> </td> </tr> </table> <br/><br/> <div style="font-size:11px"> Note: For archive.zip files 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 <a href='https://duplicator.com/knowledge-base/how-to-handle-various-install-scenarios' target='_blank'>manually extract</a> before running the installer. </div> <br/><br/> </div> </form> </div> </body> <script> function AutoFresh() { document.getElementById('error-form').submit(); } <?php if ($auto_refresh) : ?> var duration = 10000; //10 seconds var counter = 10; var countElement = document.getElementById('count-down'); setTimeout(function () { window.location.reload(1); }, duration); setInterval(function () { counter--; countElement.innerHTML = (counter > 0) ? counter.toString() : "0"; }, 1000); <?php endif; ?> </script> <?php endif; ?> </html> <?php } @@DUPARCHIVE_MINI_EXPANDER@@