*/ public $Size = 0; //SETUP /** @var int ENUM ArchiveDescriptor::SECURE_MODE_* */ public $OptsSecureOn = ArchiveDescriptor::SECURE_MODE_NONE; /** @var string */ public $passowrd = ''; /** @var string */ public $OptsSecurePass = ''; // Old installer password managed before 4.5.3, /** @var bool */ public $OptsSkipScan = false; //BASIC /** @var string */ public $OptsDBHost = ''; /** @var string */ public $OptsDBName = ''; /** @var string */ public $OptsDBUser = ''; //CPANEL /** @var string */ public $OptsCPNLHost = ''; /** @var string */ public $OptsCPNLUser = ''; /** @var string */ public $OptsCPNLPass = ''; /** @var bool */ public $OptsCPNLEnable = false; /** @var bool */ public $OptsCPNLConnect = false; //CPANEL DB //1 = Create New, 2 = Connect Remove /** @var string */ public $OptsCPNLDBAction = 'create'; /** @var string */ public $OptsCPNLDBHost = ''; /** @var string */ public $OptsCPNLDBName = ''; /** @var string */ public $OptsCPNLDBUser = ''; /** @var SnapOrigFileManager */ protected $origFileManger = null; /** @var DUP_PRO_Package */ protected $Package; /** @var int<0,max> */ public $numFilesAdded = 0; /** @var int<0,max> */ public $numDirsAdded = 0; /** @var ?WPConfigTransformer */ private $configTransformer = null; /** * CLass constructor * * @param DUP_PRO_Package $package Package */ public function __construct(DUP_PRO_Package $package) { $this->Package = $package; $this->loadInit(); } /** * Init after load * * @return void */ protected function loadInit() { $this->origFileManger = new SnapOrigFileManager( DUP_PRO_Archive::getArchiveListPaths('home'), DUPLICATOR_PRO_SSDIR_PATH_TMP, $this->Package->get_package_hash() ); if (($wpConfigPath = SnapWP::getWPConfigPath()) !== false) { $this->configTransformer = new WPConfigTransformer($wpConfigPath); } } /** * Return serialize data for json encode * * @return array */ public function __serialize() // phpcs:ignore PHPCompatibility.FunctionNameRestrictions.NewMagicMethods.__serializeFound { $data = get_object_vars($this); foreach (['origFileManger', 'Package', 'configTransformer'] as $removeProp) { unset($data[$removeProp]); } $data['OptsSecurePass'] = ''; // empty old password $data['passowrd'] = CryptBlowfish::encrypt($data['passowrd'], null, true); return $data; } /** * Called after json decode * * @return void */ public function __wakeup() { $this->loadInit(); if (strlen($this->OptsSecurePass) > 0) { $this->passowrd = base64_decode($this->OptsSecurePass); } elseif (strlen($this->passowrd) > 0) { $this->passowrd = CryptBlowfish::decrypt($this->passowrd, null, true); } $this->OptsSecurePass = ''; } /** * Returns real and normalized path to the saved installer file at default backup location * * @return string */ public function getSafeFilePath() { return SnapIO::safePath(DUPLICATOR_PRO_SSDIR_PATH . "/" . $this->getInstallerLocalName()); } /** * Return local fil name * * @return string */ public function getInstallerLocalName() { return pathinfo($this->File, PATHINFO_FILENAME) . self::INSTALLER_SERVER_EXTENSION; } /** * Get the installer file name * * @return string */ public function getInstallerName() { return $this->File; } /** * * @return string */ public function getDownloadName() { switch (DUP_PRO_Global_Entity::getInstance()->installer_name_mode) { case DUP_PRO_Global_Entity::INSTALLER_NAME_MODE_SIMPLE: return DUP_PRO_Global_Entity::getInstance()->installer_base_name; case DUP_PRO_Global_Entity::INSTALLER_NAME_MODE_WITH_HASH: default: $info = pathinfo($this->getInstallerName()); return $info['basename']; } } /** * Set the installer file name * * @param string $nameHash The hash to use in the file name * * @return void */ public function setFileName($nameHash) { $this->File = $nameHash . '_' . DUP_PRO_Global_Entity::getInstance()->installer_base_name; } /** * Return true if a installer security system is enabled * * @return bool */ public function isSecure() { return $this->OptsSecureOn != ArchiveDescriptor::SECURE_MODE_NONE; } /** * Build * * @param BuildProgress $build_progress Build progress * * @return void */ public function build(BuildProgress $build_progress) { /* @var $package DUP_PRO_Package */ DUP_PRO_Log::trace("building installer"); $success = false; if ($this->create_enhanced_installer_files()) { $success = $this->add_extra_files(); } if ($success) { $build_progress->installer_built = true; } else { DUP_PRO_Log::infoTrace("Error in create_enhanced_installer_files, set build failed"); $build_progress->failed = true; } } /** * Create enhanced installer files * * @return bool */ private function create_enhanced_installer_files() { $success = false; if ($this->create_enhanced_installer()) { $success = $this->create_archive_config_file(); } else { DUP_PRO_Log::infoTrace("Error in create_enhanced_installer, set build failed"); } return $success; } /** * Create installer.php file * * @return bool */ private function create_enhanced_installer() { $success = true; $archive_filepath = SnapIO::safePath("{$this->Package->StorePath}/{$this->Package->Archive->File}"); $installer_filepath = SnapIO::safePath(DUPLICATOR_PRO_SSDIR_PATH_TMP) . "/" . $this->getInstallerLocalName(); $template_filepath = DUPLICATOR____PATH . '/installer/installer.tpl'; // Replace the @@ARCHIVE@@ token $header = <<
Package->build_progress->current_build_mode == DUP_PRO_Archive_Build_Mode::DupArchive) { $dupLib = DUPLICATOR____PATH . '/src/Libs/DupArchive/'; $dupExpanderCoder .= SnapCode::getSrcClassCode($dupLib . 'DupArchive.php') . "\n"; $dupExpanderCoder .= SnapCode::getSrcClassCode($dupLib . 'DupArchiveExpandBasicEngine.php') . "\n"; $dupExpanderCoder .= SnapCode::getSrcClassCode($dupLib . 'Headers/AbstractDupArchiveHeader.php') . "\n"; $dupExpanderCoder .= SnapCode::getSrcClassCode($dupLib . 'Headers/DupArchiveDirectoryHeader.php') . "\n"; $dupExpanderCoder .= SnapCode::getSrcClassCode($dupLib . 'Headers/DupArchiveFileHeader.php') . "\n"; $dupExpanderCoder .= SnapCode::getSrcClassCode($dupLib . 'Headers/DupArchiveGlobHeader.php') . "\n"; $dupExpanderCoder .= SnapCode::getSrcClassCode($dupLib . 'Headers/DupArchiveHeader.php') . "\n"; $dupExpanderCoder .= SnapCode::getSrcClassCode($dupLib . 'Info/DupArchiveExpanderInfo.php') . "\n"; } $search_array = array( '#@@DUP_INSTALLER_CLASSES_EXPANDER@@#', '@@ARCHIVE@@', '@@VERSION@@', '@@ARCHIVE_SIZE@@', '@@PACKAGE_HASH@@', '@@SECONDARY_PACKAGE_HASH@@', ); $package_hash = $this->Package->get_package_hash(); $secondary_package_hash = $this->Package->getSecondaryPackageHash(); $replace_array = array( $dupExpanderCoder, $this->Package->Archive->File, DUPLICATOR_PRO_VERSION, @filesize($archive_filepath), $package_hash, $secondary_package_hash, ); $installer_contents = str_replace($search_array, $replace_array, $installer_contents); if (@file_put_contents($installer_filepath, $installer_contents) === false) { $details = sprintf(__('Couldn\'t write to %s', 'duplicator-pro'), $installer_filepath); DUP_PRO_Log::error(__('Error writing installer contents', 'duplicator-pro'), $details, false); $success = false; } if ($success) { $this->Size = @filesize($installer_filepath); } return $success; } /** * Create archive.txt file * * @return bool */ private function create_archive_config_file() { global $wpdb; if (is_multisite()) { restore_current_blog(); } $global = DUP_PRO_Global_Entity::getInstance(); $success = true; $archive_config_filepath = SnapIO::safePath(DUPLICATOR_PRO_SSDIR_PATH_TMP) . "/{$this->Package->NameHash}_archive.txt"; $ac = new ArchiveDescriptor(); $extension = strtolower($this->Package->Archive->Format); //READ-ONLY: COMPARE VALUES $ac->created = $this->Package->getCreated(); $ac->version_dup = DUPLICATOR_PRO_VERSION; $ac->version_wp = $this->Package->VersionWP; $ac->version_db = $this->Package->VersionDB; $ac->version_php = $this->Package->VersionPHP; $ac->version_os = $this->Package->VersionOS; $ac->dbInfo = $this->Package->Database->info->cloneToArchiveDbInfo(); $ac->packInfo = new DescriptorPackageInfo(); $ac->packInfo->packageId = $this->Package->ID; $ac->packInfo->packageName = $this->Package->Name; $ac->packInfo->packageHash = $this->Package->get_package_hash(); $ac->packInfo->secondaryHash = $this->Package->getSecondaryPackageHash(); $ac->fileInfo = new DescriptorFileInfo(); $ac->fileInfo->fileCount = $this->Package->Archive->FileCount; $ac->fileInfo->dirCount = $this->Package->Archive->DirCount; $ac->fileInfo->size = $this->Package->Archive->Size; $ac->wpInfo = $this->getWpInfo(); //READ-ONLY: GENERAL $ac->installer_base_name = $global->installer_base_name; $ac->installer_backup_name = $this->getInstallerBackupName(); $ac->package_name = "{$this->Package->NameHash}_archive.{$extension}"; $ac->package_hash = $this->Package->get_package_hash(); $ac->package_notes = $this->Package->notes; $ac->opts_delete = $GLOBALS['DUPLICATOR_PRO_OPTS_DELETE']; $ac->blogname = sanitize_text_field(get_option('blogname')); $ac->defaultStorageId = StoragesUtil::getDefaultStorageId(); $ac->exportOnlyDB = $this->Package->isDBOnly(); $ac->components = $this->Package->components; //PRE-FILLED: GENERAL $ac->secure_on = $this->OptsSecureOn; $ac->secure_pass = $ac->secure_on ? Security::passwordHash($this->passowrd) : ''; $ac->mu_mode = DUP_PRO_MU::getMode(); $ac->wp_tableprefix = $wpdb->base_prefix; $ac->mu_generation = DUP_PRO_MU::getGeneration(); $ac->mu_is_filtered = !empty($this->Package->Multisite->FilterSites) ? true : false; $ac->mu_siteadmins = array_values(get_super_admins()); $filteredTables = ($this->Package->Database->FilterOn ? explode(',', $this->Package->Database->FilterTables) : array()); $ac->subsites = DUP_PRO_MU::getSubsites($this->Package->Multisite->FilterSites, $filteredTables, $this->Package->Archive->FilterInfo->Dirs->Instance); $ac->main_site_id = SnapWP::getMainSiteId(); //BRAND $ac->brand = $this->the_brand_setup($this->Package->Brand_ID); //LICENSING $ac->license_type = LicenseData::getInstance()->getLicenseType(); $ac->license_limit = LicenseData::getInstance()->getLicenseLimit(); // OVERWRITE PARAMS $ac->overwriteInstallerParams = apply_filters('duplicator_pro_overwrite_params_data', $this->getPrefillParams()); $json = JsonSerialize::serialize($ac, JSON_PRETTY_PRINT); DUP_PRO_Log::traceObject('json', $json); if (file_put_contents($archive_config_filepath, $json) === false) { DUP_PRO_Log::error("Error writing archive config", "Couldn't write archive config at $archive_config_filepath", false); $success = false; } return $success; } /** * Get prefill installer params * * @return array */ private function getPrefillParams() { $result = array(); if (strlen($this->OptsDBHost) > 0) { $result['dbhost'] = array('value' => $this->OptsDBHost); } if (strlen($this->OptsDBName) > 0) { $result['dbname'] = array('value' => $this->OptsDBName); } if (strlen($this->OptsDBUser) > 0) { $result['dbuser'] = array('value' => $this->OptsDBUser); } if (filter_var($this->OptsCPNLEnable, FILTER_VALIDATE_BOOLEAN)) { $result['view_mode'] = array('value' => 'cpnl'); } if (strlen($this->OptsCPNLDBAction) > 0) { $result['cpnl-dbaction'] = array('value' => $this->OptsCPNLDBAction); } if (strlen($this->OptsCPNLHost) > 0) { $result['cpnl-host'] = array('value' => $this->OptsCPNLHost); } if (strlen($this->OptsCPNLUser) > 0) { $result['cpnl-user'] = array('value' => $this->OptsCPNLUser); } if (strlen($this->OptsCPNLPass) > 0) { $result['cpnl-pass'] = array('value' => $this->OptsCPNLPass); } if (strlen($this->OptsCPNLDBHost) > 0) { $result['cpnl-dbhost'] = array('value' => $this->OptsCPNLDBHost); } if (strlen($this->OptsCPNLDBName) > 0) { $result['cpnl-dbname-txt'] = array('value' => $this->OptsCPNLDBName); } if (strlen($this->OptsCPNLDBUser) > 0) { $result['cpnl-dbuser-txt'] = array('value' => $this->OptsCPNLDBUser); } return $result; } /** * Return list of extra files to att to archive * * @param bool $checkExists Check if file exists * * @return array */ private function getExtraFilesLists($checkExists = true) { $result = array(); $result[] = array( 'sourcePath' => SnapIO::safePath(DUPLICATOR_PRO_SSDIR_PATH_TMP) . "/" . $this->getInstallerLocalName(), 'archivePath' => $this->getInstallerBackupName(), 'label' => 'installer backup file', ); $result[] = array( 'sourcePath' => DUPLICATOR____PATH . '/installer/dup-installer', 'archivePath' => 'dup-installer', 'label' => 'dup installer folder', ); $result[] = array( 'sourcePath' => DUPLICATOR____PATH . '/src/Libs/Snap', 'archivePath' => 'dup-installer/libs/Snap', 'label' => 'dup snaplib folder', ); $result[] = array( 'sourcePath' => DUPLICATOR____PATH . '/src/Libs/Shell', 'archivePath' => 'dup-installer/libs/Shell', 'label' => 'dup shell folder', ); $result[] = array( 'sourcePath' => DUPLICATOR____PATH . '/src/Libs/Chunking', 'archivePath' => 'dup-installer/libs/Chunking', 'label' => 'dup snaplib folder', ); $result[] = array( 'sourcePath' => DUPLICATOR____PATH . '/src/Libs/DupArchive', 'archivePath' => 'dup-installer/libs/DupArchive', 'label' => 'dup snaplib folder', ); $result[] = array( 'sourcePath' => DUPLICATOR____PATH . '/src/Libs/WpConfig', 'archivePath' => 'dup-installer/libs/WpConfig', 'label' => 'lib config folder', ); $result[] = array( 'sourcePath' => DUPLICATOR____PATH . '/src/Libs/Certificates', 'archivePath' => 'dup-installer/libs/Certificates', 'label' => 'SSL certificates', ); $result[] = array( 'sourcePath' => DUPLICATOR____PATH . '/vendor-prefixed/andreamk/jsonserialize', 'archivePath' => 'dup-installer/vendor-prefixed/andreamk/jsonserialize', 'label' => 'Requests library', ); $result[] = array( 'sourcePath' => DUPLICATOR____PATH . '/vendor-prefixed/rmccue/requests', 'archivePath' => 'dup-installer/vendor-prefixed/rmccue/requests', 'label' => 'Requests library', ); $result[] = array( 'sourcePath' => DUPLICATOR____PATH . '/assets/js/duplicator-tooltip.js', 'archivePath' => 'dup-installer/assets/js/duplicator-tooltip.js', 'label' => 'Duplicator tooltip script', ); $result[] = array( 'sourcePath' => DUPLICATOR____PATH . '/assets/js/popper', 'archivePath' => 'dup-installer/assets/js/popper', 'label' => 'popper js', ); $result[] = array( 'sourcePath' => DUPLICATOR____PATH . '/assets/js/tippy', 'archivePath' => 'dup-installer/assets/js/tippy', 'label' => 'tippy js', ); $result[] = array( 'sourcePath' => DUPLICATOR____PATH . '/assets/js/select2', 'archivePath' => 'dup-installer/assets/js/select2', 'label' => 'select2 js', ); $result[] = array( 'sourcePath' => DUPLICATOR____PATH . '/assets/css/font-awesome', 'archivePath' => 'dup-installer/assets/font-awesome', 'label' => 'font awesome', ); $result[] = array( 'sourcePath' => $this->origFileManger->getMainFolder(), 'archivePath' => 'dup-installer/' . basename($this->origFileManger->getMainFolder()), 'label' => 'original files folder', ); $result[] = array( 'sourcePath' => SnapIO::safePath(DUPLICATOR_PRO_SSDIR_PATH_TMP) . "/{$this->Package->NameHash}_archive.txt", 'archivePath' => $this->getArchiveTxtFilePath(), 'label' => 'archive descriptor file', ); $result[] = array( 'sourcePath' => SnapIO::safePath(DUPLICATOR_PRO_SSDIR_PATH_TMP) . "/{$this->Package->NameHash}_scan.json", 'archivePath' => $this->getEmbeddedScanFilePath(), 'label' => 'scan file', ); $result[] = array( 'sourcePath' => SnapIO::safePath(DUPLICATOR_PRO_SSDIR_PATH_TMP) . '/' . $this->Package->NameHash . DUP_PRO_Archive::FILES_LIST_FILE_NAME_SUFFIX, 'archivePath' => $this->getEmbeddedScanFileList(), 'label' => 'files list file', ); $result[] = array( 'sourcePath' => SnapIO::safePath(DUPLICATOR_PRO_SSDIR_PATH_TMP) . '/' . $this->Package->NameHash . DUP_PRO_Archive::DIRS_LIST_FILE_NAME_SUFFIX, 'archivePath' => $this->getEmbeddedScanDirList(), 'label' => 'folders list file', ); $result[] = array( 'sourcePath' => $this->getManualExtractFilePath(), 'archivePath' => $this->getEmbeddedManualExtractFilePath(), 'label' => 'manual extract file', ); foreach (\Duplicator\Core\Addons\AddonsManager::getInstance()->getEnabledAddons() as $addon) { if (!is_readable($addon->getAddonInstallerPath())) { continue; } $result[] = array( 'sourcePath' => $addon->getAddonInstallerPath(), 'archivePath' => 'dup-installer/addons/' . basename($addon->getAddonInstallerPath()), 'label' => 'addon ' . $addon->getSlug(), ); } // sql file should be the last one if ($this->Package->build_progress->current_build_mode == DUP_PRO_Archive_Build_Mode::Shell_Exec) { $result[] = array( 'sourcePath' => SnapIO::safePath("{$this->Package->StorePath}/{$this->Package->Database->File}"), 'archivePath' => $this->getEmbeddedSqlFile(), 'label' => 'Sql dump file', ); } if ($checkExists) { foreach ($result as $item) { if (!is_readable($item['sourcePath'])) { throw new Exception('INSTALLER FILES: "' . $item['label'] . '" doesn\'t exist ' . $item['sourcePath']); } } } return $result; } /** * Get wpInfo object * * @return DescriptorWpInfo */ private function getWpInfo() { $wpInfo = new DescriptorWpInfo(); $wpInfo->version = $this->Package->VersionWP; $wpInfo->is_multisite = is_multisite(); if (function_exists('get_current_network_id')) { $wpInfo->network_id = get_current_network_id(); } else { $wpInfo->network_id = 1; } $wpInfo->targetRoot = DUP_PRO_Archive::getTargetRootPath(); $wpInfo->targetPaths = DUP_PRO_Archive::getScanPaths(); $wpInfo->adminUsers = SnapWP::getAdminUserLists(); $pluginFiltes = ( in_array(BuildComponents::COMP_PLUGINS_ACTIVE, $this->Package->components) ? SnapWP::PLUGIN_INFO_ACTIVE : SnapWP::PLUGIN_INFO_ALL ); if (!in_array(BuildComponents::COMP_PLUGINS, $this->Package->components)) { $pathFilters = true; } else { $pathFilters = $this->Package->Archive->FilterDirsAll; } $pluginsData = SnapWP::getPluginsInfo($pluginFiltes, $pathFilters); foreach ($pluginsData as $pluginData) { $wpInfo->plugins[$pluginData['slug']] = new DescriptorPlugin($pluginData); } $themesData = SnapWP::getThemesInfo(); foreach ($themesData as $themeData) { $wpInfo->themes[$themeData['slug']] = new DescriptorTheme($themeData); } $this->addDefineIfExists($wpInfo->configs->defines, 'ABSPATH'); $this->addDefineIfExists($wpInfo->configs->defines, 'DB_CHARSET'); $this->addDefineIfExists($wpInfo->configs->defines, 'DB_COLLATE'); $this->addDefineIfExists( $wpInfo->configs->defines, 'MYSQL_CLIENT_FLAGS', array( 'Duplicator\\Libs\\Snap\\SnapDB', 'getMysqlConnectFlagsFromMaskVal', ) ); $this->addDefineIfExists($wpInfo->configs->defines, 'AUTH_KEY'); $this->addDefineIfExists($wpInfo->configs->defines, 'SECURE_AUTH_KEY'); $this->addDefineIfExists($wpInfo->configs->defines, 'LOGGED_IN_KEY'); $this->addDefineIfExists($wpInfo->configs->defines, 'NONCE_KEY'); $this->addDefineIfExists($wpInfo->configs->defines, 'AUTH_SALT'); $this->addDefineIfExists($wpInfo->configs->defines, 'SECURE_AUTH_SALT'); $this->addDefineIfExists($wpInfo->configs->defines, 'LOGGED_IN_SALT'); $this->addDefineIfExists($wpInfo->configs->defines, 'NONCE_SALT'); $this->addDefineIfExists($wpInfo->configs->defines, 'WP_SITEURL'); $this->addDefineIfExists($wpInfo->configs->defines, 'WP_HOME'); $this->addDefineIfExists($wpInfo->configs->defines, 'WP_CONTENT_DIR'); $this->addDefineIfExists($wpInfo->configs->defines, 'WP_CONTENT_URL'); $this->addDefineIfExists($wpInfo->configs->defines, 'WP_PLUGIN_DIR'); $this->addDefineIfExists($wpInfo->configs->defines, 'WP_PLUGIN_URL'); $this->addDefineIfExists($wpInfo->configs->defines, 'PLUGINDIR'); $this->addDefineIfExists($wpInfo->configs->defines, 'UPLOADS'); $this->addDefineIfExists($wpInfo->configs->defines, 'AUTOSAVE_INTERVAL'); $this->addDefineIfExists($wpInfo->configs->defines, 'WP_POST_REVISIONS'); $this->addDefineIfExists($wpInfo->configs->defines, 'COOKIE_DOMAIN'); $this->addDefineIfExists($wpInfo->configs->defines, 'WP_ALLOW_MULTISITE'); $this->addDefineIfExists($wpInfo->configs->defines, 'ALLOW_MULTISITE'); $this->addDefineIfExists($wpInfo->configs->defines, 'MULTISITE'); $this->addDefineIfExists($wpInfo->configs->defines, 'DOMAIN_CURRENT_SITE'); $this->addDefineIfExists($wpInfo->configs->defines, 'PATH_CURRENT_SITE'); $this->addDefineIfExists($wpInfo->configs->defines, 'SITE_ID_CURRENT_SITE'); $this->addDefineIfExists($wpInfo->configs->defines, 'BLOG_ID_CURRENT_SITE'); $this->addDefineIfExists($wpInfo->configs->defines, 'SUBDOMAIN_INSTALL'); $this->addDefineIfExists($wpInfo->configs->defines, 'VHOST'); $this->addDefineIfExists($wpInfo->configs->defines, 'SUNRISE'); $this->addDefineIfExists($wpInfo->configs->defines, 'NOBLOGREDIRECT'); $this->addDefineIfExists($wpInfo->configs->defines, 'WP_DEBUG'); $this->addDefineIfExists($wpInfo->configs->defines, 'SCRIPT_DEBUG'); $this->addDefineIfExists($wpInfo->configs->defines, 'CONCATENATE_SCRIPTS'); $this->addDefineIfExists($wpInfo->configs->defines, 'WP_DEBUG_LOG'); $this->addDefineIfExists($wpInfo->configs->defines, 'WP_DEBUG_DISPLAY'); $this->addDefineIfExists($wpInfo->configs->defines, 'WP_MEMORY_LIMIT'); $this->addDefineIfExists($wpInfo->configs->defines, 'WP_MAX_MEMORY_LIMIT'); $this->addDefineIfExists($wpInfo->configs->defines, 'WP_CACHE'); // wp super cache define $this->addDefineIfExists($wpInfo->configs->defines, 'WPCACHEHOME'); $this->addDefineIfExists($wpInfo->configs->defines, 'WP_TEMP_DIR'); $this->addDefineIfExists($wpInfo->configs->defines, 'CUSTOM_USER_TABLE'); $this->addDefineIfExists($wpInfo->configs->defines, 'CUSTOM_USER_META_TABLE'); $this->addDefineIfExists($wpInfo->configs->defines, 'WPLANG'); $this->addDefineIfExists($wpInfo->configs->defines, 'WP_LANG_DIR'); $this->addDefineIfExists($wpInfo->configs->defines, 'SAVEQUERIES'); $this->addDefineIfExists($wpInfo->configs->defines, 'FS_CHMOD_DIR'); $this->addDefineIfExists($wpInfo->configs->defines, 'FS_CHMOD_FILE'); $this->addDefineIfExists($wpInfo->configs->defines, 'FS_METHOD'); /** $this->addDefineIfExists($wpInfo->configs->defines, 'FTP_BASE'); $this->addDefineIfExists($wpInfo->configs->defines, 'FTP_CONTENT_DIR'); $this->addDefineIfExists($wpInfo->configs->defines, 'FTP_PLUGIN_DIR'); $this->addDefineIfExists($wpInfo->configs->defines, 'FTP_PUBKEY'); $this->addDefineIfExists($wpInfo->configs->defines, 'FTP_PRIKEY'); $this->addDefineIfExists($wpInfo->configs->defines, 'FTP_USER'); $this->addDefineIfExists($wpInfo->configs->defines, 'FTP_PASS'); $this->addDefineIfExists($wpInfo->configs->defines, 'FTP_HOST'); $this->addDefineIfExists($wpInfo->configs->defines, 'FTP_SSL'); * */ $this->addDefineIfExists($wpInfo->configs->defines, 'ALTERNATE_WP_CRON'); $this->addDefineIfExists($wpInfo->configs->defines, 'DISABLE_WP_CRON'); $this->addDefineIfExists($wpInfo->configs->defines, 'WP_CRON_LOCK_TIMEOUT'); $this->addDefineIfExists($wpInfo->configs->defines, 'COOKIEPATH'); $this->addDefineIfExists($wpInfo->configs->defines, 'SITECOOKIEPATH'); $this->addDefineIfExists($wpInfo->configs->defines, 'ADMIN_COOKIE_PATH'); $this->addDefineIfExists($wpInfo->configs->defines, 'PLUGINS_COOKIE_PATH'); $this->addDefineIfExists($wpInfo->configs->defines, 'TEMPLATEPATH'); $this->addDefineIfExists($wpInfo->configs->defines, 'STYLESHEETPATH'); $this->addDefineIfExists($wpInfo->configs->defines, 'EMPTY_TRASH_DAYS'); $this->addDefineIfExists($wpInfo->configs->defines, 'WP_ALLOW_REPAIR'); $this->addDefineIfExists($wpInfo->configs->defines, 'DO_NOT_UPGRADE_GLOBAL_TABLES'); $this->addDefineIfExists($wpInfo->configs->defines, 'DISALLOW_FILE_EDIT'); $this->addDefineIfExists($wpInfo->configs->defines, 'DISALLOW_FILE_MODS'); $this->addDefineIfExists($wpInfo->configs->defines, 'FORCE_SSL_ADMIN'); $this->addDefineIfExists($wpInfo->configs->defines, 'WP_HTTP_BLOCK_EXTERNAL'); $this->addDefineIfExists($wpInfo->configs->defines, 'WP_ACCESSIBLE_HOSTS'); $this->addDefineIfExists($wpInfo->configs->defines, 'AUTOMATIC_UPDATER_DISABLED'); $this->addDefineIfExists($wpInfo->configs->defines, 'WP_AUTO_UPDATE_CORE'); $this->addDefineIfExists($wpInfo->configs->defines, 'IMAGE_EDIT_OVERWRITE'); $this->addDefineIfExists($wpInfo->configs->defines, 'WPMU_PLUGIN_DIR'); $this->addDefineIfExists($wpInfo->configs->defines, 'WPMU_PLUGIN_URL'); $this->addDefineIfExists($wpInfo->configs->defines, 'MUPLUGINDIR'); $originalUrls = DUP_PRO_Archive::getOriginalUrls(); $wpInfo->configs->realValues->siteUrl = $originalUrls['abs']; $wpInfo->configs->realValues->homeUrl = $originalUrls['home']; $wpInfo->configs->realValues->loginUrl = $originalUrls['login']; $wpInfo->configs->realValues->contentUrl = $originalUrls['wpcontent']; $wpInfo->configs->realValues->uploadBaseUrl = $originalUrls['uploads']; $wpInfo->configs->realValues->pluginsUrl = $originalUrls['plugins']; $wpInfo->configs->realValues->mupluginsUrl = $originalUrls['muplugins']; $wpInfo->configs->realValues->themesUrl = $originalUrls['themes']; $wpInfo->configs->realValues->originalPaths = array(); $originalpaths = DUP_PRO_Archive::getOriginalPaths(); foreach ($originalpaths as $key => $val) { $originalpaths[$key] = rtrim($val, '\\/'); } $wpInfo->configs->realValues->originalPaths = (object) $originalpaths; $wpInfo->configs->realValues->archivePaths = (object) array_merge( $originalpaths, DUP_PRO_Archive::getArchiveListPaths() ); return $wpInfo; } /** * Check if $define is defined and add a prop to $obj * * @param object $obj object to add prop * @param string $define constant name * @param null|callable $transformCallback if it is different from null the function is applied to the value * * @return boolean return true if define is added of false */ private function addDefineIfExists($obj, $define, $transformCallback = null) { if (!defined($define)) { return false; } $obj->{$define} = new StdClass(); if (is_callable($transformCallback)) { $obj->{$define}->value = call_user_func($transformCallback, constant($define)); } else { if ($transformCallback !== null) { throw new Exception('transformCallback isn\'t callable'); } $obj->{$define}->value = constant($define); } if (!is_null($this->configTransformer)) { $obj->{$define}->inWpConfig = $this->configTransformer->exists('constant', $define); } else { $obj->{$define}->inWpConfig = false; } return true; } /** * Brand setup * * @param int $id brand id * * @return array{isDefault:bool,name:string,logo:string,enabled:bool,style:array} */ private function the_brand_setup($id) { // initialize brand $brand = BrandEntity::getByIdOrDefault((int) $id); // Prepare default fields $brand_property_default = array( 'name' => 'Duplicator Professional', 'isDefault' => true, 'logo' => '', 'enabled' => false, 'style' => array(), ); // Returns property $brand_property = array(); // Is default brand selected? $brand_property['isDefault'] = $brand->isDefault(); // Set brand name $brand_property['name'] = $brand_property['isDefault'] ? 'Duplicator Professional' : $brand->name; // Set logo and hosted images path $brand_property['logo'] = $brand->logo; // Find images preg_match_all('//', $brand->logo, $arr_img, PREG_PATTERN_ORDER); // https://regex101.com/r/eEyf5S/2 // Fix hosted image url path if (isset($arr_img[1]) && count($brand->attachments) > 0 && count($arr_img[1]) === count($brand->attachments)) { foreach ($arr_img[1] as $i => $find) { $brand_property['logo'] = str_replace($find, 'assets/images/brand' . $brand->attachments[$i], $brand_property['logo']); } } $brand_property['logo'] = stripslashes($brand_property['logo']); // Set is enabled if (!empty($brand_property['logo']) && $brand->active) { $brand_property['enabled'] = true; } $brand_property['style'] = $brand->style; // Merge data properly $brand_property = array_replace($brand_property_default, $brand_property); return $brand_property; } /** * Get archive full path * * @return string */ public function getArchiveFullPath() { return SnapIO::safePath($this->Package->StorePath) . '/' . $this->Package->Archive->File; } /** * CreateZipBackup * Puts an installer zip file in the archive for backup purposes. * * @return bool */ private function add_extra_files() { $success = false; $archive_filepath = SnapIO::safePath("{$this->Package->StorePath}/{$this->Package->Archive->File}"); $this->initConfigFiles(); $this->createManualExtractCheckFile(); if ($this->Package->Archive->file_count != 2) { DUP_PRO_Log::trace("Doing archive file check"); // Only way it's 2 is if the root was part of the filter in which case the archive won't be there if (file_exists($archive_filepath) == false) { $error_text = sprintf(__("Zip archive %1s not present.", 'duplicator-pro'), $archive_filepath); $fix_text = __("Click on button to set archive engine to DupArchive.", 'duplicator-pro'); DUP_PRO_Log::error("$error_text. **RECOMMENDATION: $fix_text", '', false); $system_global = SystemGlobalEntity::getInstance(); $system_global->addQuickFix( $error_text, $fix_text, array( 'global' => array('archive_build_mode' => 3), ) ); return false; } } DUP_PRO_Log::trace("Add extra files: Current build mode = " . $this->Package->build_progress->current_build_mode); if ($this->Package->build_progress->current_build_mode == DUP_PRO_Archive_Build_Mode::ZipArchive) { $success = $this->zipArchiveAddExtra(); } elseif ($this->Package->build_progress->current_build_mode == DUP_PRO_Archive_Build_Mode::Shell_Exec) { // Adding the shellexec fail text fix if (($success = $this->shellZipAddExtra()) == false) { $error_text = __("Problem adding installer to archive", 'duplicator-pro'); $fix_text = __("Click on button to set archive engine to DupArchive.", 'duplicator-pro'); $system_global = SystemGlobalEntity::getInstance(); $system_global->addQuickFix( $error_text, $fix_text, array( 'global' => array('archive_build_mode' => 3), ) ); } } elseif ($this->Package->build_progress->current_build_mode == DUP_PRO_Archive_Build_Mode::DupArchive) { $success = $this->dupArchiveAddExtra(); } try { $archive_config_filepath = SnapIO::safePath(DUPLICATOR_PRO_SSDIR_PATH_TMP) . "/{$this->Package->NameHash}_archive.txt"; // No sense keeping these files @unlink($archive_config_filepath); $this->origFileManger->deleteMainFolder(); $this->deleteManualExtractCheckFile(); } catch (Exception $e) { DUP_PRO_Log::infoTrace("Error clean temp installer file, but continue. Message: " . $e->getMessage()); } $this->Package->Archive->Size = @filesize($archive_filepath); return $success; } /** * Get installer backup name * * @return string */ public function getInstallerBackupName() { return $this->Package->NameHash . '_' . DUP_PRO_Global_Entity::getInstance()->get_installer_backup_filename(); } /** * Add extra files in duparchive * * @return bool */ private function dupArchiveAddExtra() { $logger = new \Duplicator\Package\Create\DupArchive\Logger(); DupArchiveEngine::init($logger, null); $archivePath = $this->getArchiveFullPath(); $extraPoistion = filesize($archivePath); $password = $this->Package->Archive->getArchivePassword(); foreach ($this->getExtraFilesLists() as $extraItem) { if (is_dir($extraItem['sourcePath'])) { $result = DupArchiveEngine::addDirectoryToArchiveST( $archivePath, $extraItem['sourcePath'], $extraItem['archivePath'], $password, true ); $this->numFilesAdded += $result->numFilesAdded; $this->numDirsAdded += $result->numDirsAdded; } else { DupArchiveEngine::addRelativeFileToArchiveST( $archivePath, $extraItem['sourcePath'], $extraItem['archivePath'], $password ); $this->numFilesAdded++; } } // store extra files position $src = json_encode(array(DupArchiveEngine::EXTRA_FILES_POS_KEY => $extraPoistion)); $src .= str_repeat("\0", DupArchiveEngine::INDEX_FILE_SIZE - strlen($src)); DupArchiveEngine::replaceFileContent( $archivePath, $src, DupArchiveEngine::INDEX_FILE_NAME, $password, 0, 3000 ); return true; } /** * Add extra files in zip archive * * @return bool */ private function zipArchiveAddExtra() { $zipArchive = new ZipArchiveExtended($this->getArchiveFullPath()); $zipArchive->setCompressed($this->Package->build_progress->current_build_compression); if ($this->Package->Archive->isArchiveEncrypt()) { $zipArchive->setEncrypt(true, $this->Package->Archive->getArchivePassword()); } if ($zipArchive->open() !== true) { throw new \Exception("Couldn't open zip archive "); } DUP_PRO_Log::trace("Successfully opened zip"); foreach ($this->getExtraFilesLists() as $extraItem) { if (is_dir($extraItem['sourcePath'])) { $zipArchive->addDir($extraItem['sourcePath'], $extraItem['archivePath']); } else { $zipArchive->addFile($extraItem['sourcePath'], $extraItem['archivePath']); } } if ($zipArchive->close() === false) { throw new \Exception("Couldn't close zip archive "); } DUP_PRO_Log::trace('After ziparchive close when adding installer'); $this->zipArchiveCheck(); return true; } /** * Check zip archive * * @return void */ private function zipArchiveCheck() { /* ------ ZIP CONSISTENCY CHECK ------ */ DUP_PRO_Log::trace("Running ZipArchive consistency check"); $zip = new ZipArchive(); // ZipArchive::CHECKCONS will enforce additional consistency checks $res = $zip->open($this->getArchiveFullPath(), ZipArchive::CHECKCONS); if ($res !== true) { $consistency_error = sprintf(__('ERROR: Cannot open created archive. Error code = %1$s', 'duplicator-pro'), $res); DUP_PRO_Log::trace($consistency_error); switch ($res) { case ZipArchive::ER_NOZIP: $consistency_error = __('ERROR: Archive is not valid zip archive.', 'duplicator-pro'); break; case ZipArchive::ER_INCONS: $consistency_error = __("ERROR: Archive doesn't pass consistency check.", 'duplicator-pro'); break; case ZipArchive::ER_CRC: $consistency_error = __("ERROR: Archive checksum is bad.", 'duplicator-pro'); break; } throw new \Exception($consistency_error); } $failed = false; foreach ($this->getInstallerPathsForIntegrityCheck() as $path) { if ($zip->locateName($path) === false) { $failed = true; $msg = sprintf(__('Couldn\'t find %1$s in archive', 'duplicator-pro'), $path); DUP_PRO_Log::infoTrace($msg); } } if ($failed) { DUP_PRO_Log::info(__('ARCHIVE CONSISTENCY TEST: FAIL', 'duplicator-pro')); throw new \Exception("Zip for package " . $this->Package->ID . " didn't passed consistency test"); } else { DUP_PRO_Log::info(__('ARCHIVE CONSISTENCY TEST: PASS', 'duplicator-pro')); DUP_PRO_Log::trace("Zip for package " . $this->Package->ID . " passed consistency test"); } $zip->close(); } /** * Add extra files in shell zip * * @return bool */ private function shellZipAddExtra() { $tmpExtraFolder = SnapIO::safePath(DUPLICATOR_PRO_SSDIR_PATH_TMP) . '/extras/'; if (file_exists($tmpExtraFolder)) { if (SnapIO::rrmdir($tmpExtraFolder) === false) { throw new \Exception("Error deleting $tmpExtraFolder"); } } if (!wp_mkdir_p($tmpExtraFolder)) { throw new \Exception("Error creating extras directory"); } SnapIO::createSilenceIndex($tmpExtraFolder); foreach ($this->getExtraFilesLists() as $extraItem) { $destPath = $tmpExtraFolder . $extraItem['archivePath']; if (!wp_mkdir_p(dirname($destPath))) { throw new \Exception("Error creating extras directory, Couldn't create " . dirname($destPath)); } if (!SnapIO::rcopy($extraItem['sourcePath'], $destPath)) { throw new \Exception("Error copy " . $extraItem['sourcePath'] . ' to ' . $destPath); } } //-- STAGE 1 ADD $params = Shell::getCompressionParam($this->Package->build_progress->current_build_compression); if (strlen($this->Package->Archive->getArchivePassword()) > 0) { $params .= ' --password ' . escapeshellarg($this->Package->Archive->getArchivePassword()); } $params .= ' -g -rq'; $paramsPostfix = ' -x "index.php"'; $command = 'cd ' . escapeshellarg(SnapIO::safePath($tmpExtraFolder)); $command .= ' && ' . escapeshellcmd(DUP_PRO_Zip_U::getShellExecZipPath()) . ' ' . $params . ' '; $command .= escapeshellarg($this->getArchiveFullPath()) . ' ./* ./.[^.]*' . $paramsPostfix; DUP_PRO_Log::infoTrace('EXECUTING SHELL COMMAND'); DUP_PRO_Log::infoTrace("SHELL COMMAND: $command"); $shellOutput = Shell::runCommand($command, Shell::AVAILABLE_COMMANDS); if ($shellOutput !== false && !$shellOutput->isEmpty()) { throw new \Exception("Error excecuting shell command: " . $command . ' MSG: ' . $shellOutput->getOutputAsString()); } $this->shellZipFilesCheck(); if (!SnapIO::rrmdir($tmpExtraFolder)) { DUP_PRO_Log::trace("Couldn't recursively delete {$tmpExtraFolder}"); } return true; } /** * Check if extra files are in the archive * * @return bool */ private function shellZipFilesCheck() { if (DUP_PRO_U::getExeFilepath('unzip') == null) { DUP_PRO_Log::trace("unzip doesn't exist so not doing the extra file check"); return false; } $filesToValidate = $this->getInstallerPathsForIntegrityCheck(); DUP_PRO_Log::infoTrace('CHECK FILES ' . \Duplicator\Libs\Snap\SnapLog::v2str($filesToValidate)); $params = '-Z1'; // Verify the essential extras got in there $extraCountString = "unzip " . $params . ' ' . escapeshellarg($this->getArchiveFullPath()) . " | grep '^\(" . implode("\|", $filesToValidate) . "\)' | wc -l"; DUP_PRO_Log::info("Executing extra count string $extraCountString"); $shellOutput = Shell::runCommand($extraCountString . ' | awk \'{print $1 }\'', Shell::AVAILABLE_COMMANDS); $extraCount = ($shellOutput !== false) ? trim($shellOutput->getOutputAsString()) : null; if (is_numeric($extraCount)) { // Accounting for the sql and installer back files if ($extraCount != count($filesToValidate)) { throw new \Exception("Tried to verify core extra files but one or more were missing. Count = $extraCount"); } } else { throw new \Exception("Error retrieving extra count in shell zip " . $extraCount); } DUP_PRO_Log::trace("Core extra files confirmed to be in the archive"); return true; } /** * Creates the original_files_ folder in the tmp directory where all config files are saved * to be later added to the archives * * @return void */ public function initConfigFiles() { $this->origFileManger->init(); $configFilePaths = $this->getConfigFilePaths(); foreach ($configFilePaths as $identifier => $path) { if ($path !== false) { try { $this->origFileManger->addEntry($identifier, $path, SnapOrigFileManager::MODE_COPY, self::CONFIG_ORIG_FILE_FOLDER_PREFIX . $identifier); } catch (Exception $ex) { DUP_PRO_Log::infoTrace("Error while handling config files: " . $ex->getMessage()); } } } //Clean sensitive information from wp-config.php file. self::cleanTempWPConfArkFilePath($this->origFileManger->getEntryStoredPath(self::CONFIG_ORIG_FILE_WPCONFIG_ID)); } /** * Gets config files path * * @return string[] array of config files in identifier => path format */ public function getConfigFilePaths() { $home = DUP_PRO_Archive::getArchiveListPaths('home'); $configFiles = array( self::CONFIG_ORIG_FILE_USERINI_ID => $home . '/.user.ini', self::CONFIG_ORIG_FILE_PHPINI_ID => $home . '/php.ini', self::CONFIG_ORIG_FILE_WEBCONFIG_ID => $home . '/web.config', self::CONFIG_ORIG_FILE_HTACCESS_ID => $home . '/.htaccess', self::CONFIG_ORIG_FILE_WPCONFIG_ID => SnapWP::getWPConfigPath(), ); foreach ($configFiles as $identifier => $path) { if (!file_exists($path)) { unset($configFiles[$identifier]); } } return $configFiles; } /** * Get path list for integrity check * * @return string[] */ public function getInstallerPathsForIntegrityCheck() { $filesToValidate = array( 'dup-installer/api/class.api.php', 'dup-installer/assets/index.php', 'dup-installer/classes/index.php', 'dup-installer/ctrls/index.php', 'dup-installer/src/Utils/Autoloader.php', 'dup-installer/templates/default/page-help.php', 'dup-installer/main.installer.php', ); foreach ($this->getExtraFilesLists() as $extraItem) { if (is_file($extraItem['sourcePath'])) { $filesToValidate[] = $extraItem['archivePath']; } else { if (file_exists(trailingslashit($extraItem['sourcePath']) . 'index.php')) { $filesToValidate[] = ltrim(trailingslashit($extraItem['archivePath']), '\\/') . 'index.php'; } else { // SKIP CHECK } } } return array_unique($filesToValidate); } /** * Create manual extract check file * * @return bool */ private function createManualExtractCheckFile() { $file_path = $this->getManualExtractFilePath(); return SnapIO::filePutContents($file_path, ''); } /** * Get manual extract check file path * * @return string */ private function getManualExtractFilePath() { $tmp = SnapIO::safePath(DUPLICATOR_PRO_SSDIR_PATH_TMP); return $tmp . '/dup-manual-extract__' . $this->Package->get_package_hash(); } /** * Get embedded manual extract file path * * @return string */ private function getEmbeddedManualExtractFilePath() { $embedded_filepath = 'dup-installer/dup-manual-extract__' . $this->Package->get_package_hash(); return $embedded_filepath; } /** * Delete manual extract check file * * @return void */ private function deleteManualExtractCheckFile() { SnapIO::rm($this->getManualExtractFilePath()); } /** * Clear out sensitive database connection information * * @param string $temp_conf_ark_file_path Temp config file path * * @return void */ private static function cleanTempWPConfArkFilePath($temp_conf_ark_file_path) { try { if (function_exists('token_get_all')) { $transformer = new WPConfigTransformer($temp_conf_ark_file_path); $constants = array( 'DB_NAME', 'DB_USER', 'DB_PASSWORD', 'DB_HOST', ); foreach ($constants as $constant) { if ($transformer->exists('constant', $constant)) { $transformer->update('constant', $constant, ''); } } } } catch (Exception $e) { DUP_PRO_Log::infoTrace("Can\'t inizialize wp-config transformer Message: " . $e->getMessage()); } catch (Error $e) { DUP_PRO_Log::infoTrace("Can\'t inizialize wp-config transformer Message: " . $e->getMessage()); } } /** * Get embedded scan file list * * @return string */ private function getEmbeddedScanFileList() { return 'dup-installer/dup-scanned-files__' . $this->Package->get_package_hash() . '.txt'; } /** * Get embedded scan dir list * * @return string */ private function getEmbeddedScanDirList() { return 'dup-installer/dup-scanned-dirs__' . $this->Package->get_package_hash() . '.txt'; } /** * Get scan.json file path along with name in archive file * * @return string */ private function getEmbeddedScanFilePath() { return 'dup-installer/dup-scan__' . $this->Package->get_package_hash() . '.json'; } /** * Get archive.txt file path along with name in archive file * * @return string */ private function getArchiveTxtFilePath() { return 'dup-installer/dup-archive__' . $this->Package->get_package_hash() . '.txt'; } /** * Get archive.txt file path along with name in archive file * * @return string */ private function getEmbeddedSqlFile() { return 'dup-installer/dup-database__' . $this->Package->get_package_hash() . '.sql'; } }