OwlCyberSecurity - MANAGER
Edit File: class.ctrl.extraction.php
<?php /** * Extraction class * * @package Duplicator * @copyright (c) 2021, Snapcreek LLC */ use Duplicator\Installer\Core\Deploy\DupArchive\Daws; use Duplicator\Installer\Core\Deploy\Files\FilterMng; use Duplicator\Installer\Core\Deploy\Files\Filters; use Duplicator\Installer\Core\Deploy\Files\RemoveFiles; use Duplicator\Installer\Utils\LinkManager; use Duplicator\Installer\Utils\Log\Log; use Duplicator\Installer\Utils\Log\LogHandler; use Duplicator\Installer\Core\Params\PrmMng; use Duplicator\Libs\DupArchive\DupArchive; use Duplicator\Libs\Snap\JsonSerialize\AbstractJsonSerializable; use Duplicator\Libs\Snap\JsonSerialize\JsonSerialize; use Duplicator\Libs\Snap\SnapIO; use Duplicator\Libs\Snap\SnapJson; use Duplicator\Libs\Snap\SnapLog; use Duplicator\Libs\Snap\SnapWP; class DUP_Extraction extends AbstractJsonSerializable { const DUP_FOLDER_NAME = 'dup-installer'; const ENGINE_MANUAL = 'manual'; const ENGINE_ZIP = 'ziparchive'; const ENGINE_ZIP_CHUNK = 'ziparchivechunking'; const ENGINE_ZIP_SHELL = 'shellexec_unzip'; const ENGINE_DUP = 'duparchive'; const ACTION_DO_NOTHING = 'donothing'; const ACTION_REMOVE_ALL_FILES = 'removeall'; const ACTION_REMOVE_WP_FILES = 'removewpfiles'; const ACTION_REMOVE_UPLOADS = 'removeuoploads'; const FILTER_SKIP_WP_CORE = 'skip-wp-core'; const FILTER_SKIP_CORE_PLUG_THEMES = 'fil-c-p-l'; const FILTER_ONLY_MEDIA_PLUG_THEMES = 'fil-only-m'; const FILTER_NONE = 'none'; const ZIP_THROTTLING_ITERATIONS = 10; const ZIP_THROTTLING_SLEEP_TIME = 100; public $zip_filetime = null; public $archive_action = self::ACTION_DO_NOTHING; public $archive_engine = null; public $extractonStart = 0; public $chunkStart = 0; public $root_path = null; public $archive_path = null; public $ajax1_error_level = E_ALL; public $dawn_status = null; public $archive_offset = 0; public $do_chunking = false; public $chunkedExtractionCompleted = false; public $num_files = 0; public $sub_folder_archive = ''; public $max_size_extract_at_a_time = 0; public $zip_arc_chunk_notice_no = -1; public $zip_arc_chunk_notice_change_last_time = 0; public $zip_arc_chunks_extract_rates = array(); public $archive_items_count = 0; /** @var Filters */ public $filters = null; /** @var Filters */ public $removeFilters = null; /** * * @var self */ protected static $instance = null; /** * * @return self */ public static function getInstance() { if (is_null(self::$instance)) { self::$instance = new self(); } return self::$instance; } /** * Contructor */ private function __construct() { if (!DUPX_Validation_manager::isValidated()) { throw new Exception('Installer isn\'t validated'); } $this->initData(); } /** * Inizialize extraction data * * @return void */ public function initData() { // if data file exists load saved data if (file_exists(self::extractionDataFilePath())) { Log::info('LOAD EXTRACTION DATA FROM JSON', Log::LV_DETAILED); if ($this->loadData() == false) { throw new Exception('Can\'t load extraction data'); } } else { Log::info('INIT EXTRACTION DATA', Log::LV_DETAILED); $this->constructData(); $this->saveData(); $this->logStart(); } if (strlen($relativeAbsPth = DUPX_ArchiveConfig::getInstance()->getRelativePathsInArchive('abs')) > 0) { Log::info('SET RELATIVE ABSPATH: ' . Log::v2str($relativeAbsPth)); SnapWP::setWpCoreRelativeAbsPath(DUPX_ArchiveConfig::getInstance()->getRelativePathsInArchive('abs')); } $this->chunkStart = DUPX_U::getMicrotime(); } /** * Construct persistent data * * @return void */ private function constructData() { $paramsManager = PrmMng::getInstance(); $archiveConfig = DUPX_ArchiveConfig::getInstance(); $this->extractonStart = DUPX_U::getMicrotime(); $this->zip_filetime = $paramsManager->getValue(PrmMng::PARAM_FILE_TIME); $this->archive_action = $paramsManager->getValue(PrmMng::PARAM_ARCHIVE_ACTION); $this->archive_engine = $paramsManager->getValue(PrmMng::PARAM_ARCHIVE_ENGINE); $this->root_path = SnapIO::trailingslashit($paramsManager->getValue(PrmMng::PARAM_PATH_NEW)); $this->archive_path = DUPX_Security::getInstance()->getArchivePath(); $this->dawn_status = null; $this->archive_items_count = $archiveConfig->totalArchiveItemsCount(); $this->ajax1_error_level = error_reporting(); error_reporting(E_ERROR); $this->max_size_extract_at_a_time = DUPX_U::get_default_chunk_size_in_byte(MB_IN_BYTES * 2); if (self::ENGINE_DUP == $this->archive_engine || $this->archive_engine == self::ENGINE_MANUAL) { $this->sub_folder_archive = ''; } elseif (($this->sub_folder_archive = DUPX_U::findDupInstallerFolder(DUPX_Security::getInstance()->getArchivePath())) === false) { Log::info("findDupInstallerFolder error; set no subfolder"); // if not found set not subfolder $this->sub_folder_archive = ''; } $this->filters = FilterMng::getExtractFilters($this->sub_folder_archive); $this->removeFilters = FilterMng::getRemoveFilters($this->filters); } /** * * @return string */ private static function extractionDataFilePath() { static $path = null; if (is_null($path)) { $path = DUPX_INIT . '/dup-installer-extraction__' . DUPX_Package::getPackageHash() . '.json'; } return $path; } /** * * @return boolean */ public function saveData() { if (($json = SnapJson::jsonEncodePPrint($this)) === false) { Log::info('Can\'t encode json data'); return false; } if (@file_put_contents(self::extractionDataFilePath(), $json) === false) { Log::info('Can\'t save extraction data file'); return false; } return true; } /** * * @return boolean */ private function loadData() { if (!file_exists(self::extractionDataFilePath())) { return false; } if (($json = @file_get_contents(self::extractionDataFilePath())) === false) { throw new Exception('Can\'t load extraction data file'); } JsonSerialize::unserializeToObj($json, $this); return true; } /** * reset extraction data * * @return boolean */ public static function resetData() { $result = true; if (file_exists(self::extractionDataFilePath())) { if (@unlink(self::extractionDataFilePath()) === false) { throw new Exception('Can\'t delete extraction data file'); } } return $result; } /** * Preliminary actions before the extraction. * * @return void */ protected function beforeExtraction() { if (!$this->isFirst()) { return; } Log::info('BEFORE EXTRACION ACTIONS'); if (DUPX_ArchiveConfig::getInstance()->exportOnlyDB) { Log::info('EXPORT DB ONLY CHECKS'); $this->exportOnlyDB(); } DUPX_ServerConfig::reset($this->root_path); $remover = new RemoveFiles($this->removeFilters); $remover->remove(); //throw new Exception('FORCE FAIL'); DUPX_U::maintenanceMode(true); $this->createFoldersAndPermissionPrepare(); if (!empty($this->sub_folder_archive)) { Log::info("ARCHIVE dup-installer SUBFOLDER:" . Log::v2str($this->sub_folder_archive)); } else { Log::info("ARCHIVE dup-installer SUBFOLDER:" . Log::v2str($this->sub_folder_archive), Log::LV_DETAILED); } } /** * Shows next step and final report notice files are found WP core folders * * @return void */ protected function configFilesCheckNotice() { //Test if config files are present in main folders $folderList = array( PrmMng::getInstance()->getValue(PrmMng::PARAM_PATH_NEW) . "/wp-admin", PrmMng::getInstance()->getValue(PrmMng::PARAM_PATH_NEW) . "/wp-includes", PrmMng::getInstance()->getValue(PrmMng::PARAM_PATH_CONTENT_NEW) ); $configFiles = array( 'php.ini', '.user.ini', '.htaccess' ); $foundConfigFiles = array(); foreach ($folderList as $dir) { foreach ($configFiles as $file) { if (file_exists($dir . '/' . $file)) { $foundConfigFiles[] = DUPX_U::esc_html('- ' . $dir . '/' . $file); Log::info("WARNING: Found " . $file . " config file in " . $dir, Log::LV_DETAILED); } } } if (!empty($foundConfigFiles)) { $noticeManager = DUPX_NOTICE_MANAGER::getInstance(); $msg = "Config files in WordPress main folders may cause problems with accessing the site after the installation." . " The following config files were found: <br><br>" . implode("<br>", $foundConfigFiles) . "<br><br>Please consider removing those files in case you have problems with your site after the installation."; $noticeManager->addBothNextAndFinalReportNotice(array( 'shortMsg' => 'One or multiple config files were found in main WordPress folders', 'level' => DUPX_NOTICE_ITEM::SOFT_WARNING, 'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_HTML, 'longMsg' => $msg, 'sections' => 'general' )); $noticeManager->saveNotices(); } } /** * Execute extraction * * @throws Exception * * @return void */ public function runExtraction() { $this->beforeExtraction(); switch ($this->archive_engine) { case self::ENGINE_ZIP_CHUNK: $this->runZipArchive(true); break; case self::ENGINE_ZIP: $this->runZipArchive(false); break; case self::ENGINE_MANUAL: break; case self::ENGINE_ZIP_SHELL: $this->runShellExec(); break; case self::ENGINE_DUP: $this->runDupExtraction(); break; default: throw new Exception('No valid engine ' . $this->archive_engine); } } /** * * @return boolean * * @throws Exception */ protected function createFoldersAndPermissionPrepare() { Log::info("\n*** CREATE FOLDER AND PERMISSION PREPARE"); switch (PrmMng::getInstance()->getValue(PrmMng::PARAM_ARCHIVE_ENGINE)) { case self::ENGINE_ZIP_CHUNK: case self::ENGINE_ZIP: case self::ENGINE_DUP: $filters = $this->filters; DUPX_Package::foreachDirCallback(function ($info) use ($filters) { if ($filters->isFiltered($info->p)) { return true; } $destPath = DUPX_ArchiveConfig::getInstance()->destFileFromArchiveName($info->p); if (file_exists($destPath)) { Log::info("PATH " . Log::v2str($destPath) . ' ALEADY EXISTS', Log::LV_DEBUG); } else { Log::info("PATH " . Log::v2str($destPath) . ' NOT EXISTS, CREATE IT', Log::LV_DEBUG); if (SnapIO::mkdirP($destPath) === false) { Log::info("ARCHIVE EXTRACION: can't create folder " . Log::v2str($destPath)); } } if (!SnapIO::dirAddFullPermsAndCheckResult($destPath)) { Log::info("ARCHIVE EXTRACION: can't set writable " . Log::v2str($destPath)); } }); break; case self::ENGINE_ZIP_SHELL: self::setPermsViaShell('u+rwx', 'u+rw'); break; case self::ENGINE_MANUAL: break; default: throw new Exception('No valid engine ' . $this->archive_engine); } Log::info("FOLDER PREPARE DONE"); return true; } /** * * @return boolean * @throws Exception */ public static function setFolderPermissionAfterExtraction() { $paramManager = PrmMng::getInstance(); if (!$paramManager->getValue(PrmMng::PARAM_SET_DIR_PERMS)) { Log::info('\n SKIP FOLDER PERMISSION AFTER EXTRACTION'); return; } Log::info("\n*** SET FOLDER PERMISSION AFTER EXTRACTION"); switch ($paramManager->getValue(PrmMng::PARAM_ARCHIVE_ENGINE)) { case self::ENGINE_ZIP_CHUNK: case self::ENGINE_ZIP: case self::ENGINE_DUP: DUPX_Package::foreachDirCallback(function ($info) { $destPath = DUPX_ArchiveConfig::getInstance()->destFileFromArchiveName($info->p); DUP_Extraction::setPermsFromParams($destPath); }); break; case self::ENGINE_ZIP_SHELL: $dirPerms = ( $paramManager->getValue(PrmMng::PARAM_SET_DIR_PERMS) ? $paramManager->getValue(PrmMng::PARAM_DIR_PERMS_VALUE) : false); $filePerms = ( $paramManager->getValue(PrmMng::PARAM_SET_FILE_PERMS) ? $paramManager->getValue(PrmMng::PARAM_FILE_PERMS_VALUE) : false); self::setPermsViaShell($dirPerms, $filePerms, true); break; case self::ENGINE_MANUAL: break; default: throw new Exception('No valid engine '); } Log::info("SET FOLDER PERMISSION DONE"); return true; } /** * Extract package with duparchive * * @return void */ protected function runDupExtraction() { $paramsManager = PrmMng::getInstance(); $nManager = DUPX_NOTICE_MANAGER::getInstance(); SnapLog::init(Log::getLogFilePath()); SnapLog::$logHandle = Log::getFileHandle(); $params = array( 'action' => $this->isFirst() ? 'start_expand' : 'expand', 'archive_filepath' => DUPX_Security::getInstance()->getArchivePath(), 'restore_directory' => $paramsManager->getValue(PrmMng::PARAM_PATH_NEW), 'worker_time' => DUPX_Constants::CHUNK_EXTRACTION_TIMEOUT_TIME_ZIP, 'filtered_directories' => $this->filters->getDirs(), 'filtered_files' => $this->filters->getFiles(), 'excludedDirWithoutChilds' => $this->filters->getDirsWithoutChilds(), 'includeFiles' => array(), // ignore filtered 'file_renames' => array(), 'file_mode_override' => ( $paramsManager->getValue(PrmMng::PARAM_SET_FILE_PERMS) ? $paramsManager->getValue(PrmMng::PARAM_FILE_PERMS_VALUE) : -1), 'includedFiles' => array(), 'dir_mode_override' => 'u+rwx', 'keep_file_time' => ($paramsManager->getValue(PrmMng::PARAM_FILE_TIME) == 'original') ? true : false ); $params['filtered_files'][] = DupArchive::INDEX_FILE_NAME; if (!file_exists(DUPX_Package::getSqlFilePath())) { Log::info('SQL FILE NOT FOUND SO ADD TO EXTRACTION'); $params['includedFiles'][] = DUPX_Package::getSqlFilePathInArchive(); $params['fileRenames'][DUPX_Package::getSqlFilePathInArchive()] = DUPX_Package::getSqlFilePath(); } $offset = $this->isFirst() ? 0 : $this->dawn_status->archive_offset; Log::info("ARCHIVE OFFSET " . $offset); $daws = new Daws(); $daws->setFailureCallBack(function ($failure) { DUP_Extraction::reportExtractionNotices($failure->subject, $failure->description); }); $dupResult = $daws->processRequest($params); $this->dawn_status = $dupResult->status; $nManager->saveNotices(); } /** * extract package with ziparchive * * @param bool $chunk false no chunk system * * @return void * * @throws Exception */ protected function runZipArchive($chunk = true) { if (!class_exists('ZipArchive')) { Log::info("ERROR: Stopping install process. " . "Trying to extract without ZipArchive module installed. " . "Please use the 'Manual Archive Extraction' mode to extract zip file."); Log::error(ERR_ZIPARCHIVE); } $nManager = DUPX_NOTICE_MANAGER::getInstance(); $archiveConfig = DUPX_ArchiveConfig::getInstance(); $dupInstallerZipPath = ltrim($this->sub_folder_archive . '/' . self::DUP_FOLDER_NAME, '/'); $zip = new ZipArchive(); $time_over = false; Log::info("ARCHIVE OFFSET " . Log::v2str($this->archive_offset)); Log::info('DUP INSTALLER ARCHIVE PATH:"' . $dupInstallerZipPath . '"', Log::LV_DETAILED); if ($zip->open($this->archive_path) !== true) { $faqURL = LinkManager::getDocUrl('how-to-fix-installer-archive-extraction-issues', 'install'); $zip_err_msg = ERR_ZIPOPEN; $zip_err_msg .= '<br/><br/><b>To resolve error see <a href="' . $faqURL . '" target="_blank">' . DUPX_Constants::FAQ_URL . "how-to-fix-installer-archive-extraction-issues/</a></b>"; Log::info($zip_err_msg); throw new Exception("Couldn't open zip archive."); } $this->num_files = $zip->numFiles; $num_files_minus_1 = $this->num_files - 1; $extracted_size = 0; LogHandler::setMode(LogHandler::MODE_VAR, false, false); // Main chunk do { $extract_filename = null; $no_of_files_in_micro_chunk = 0; $size_in_micro_chunk = 0; do { //rsr uncomment if debugging Log::info("c ao " . $this->archive_offset); $stat_data = $zip->statIndex($this->archive_offset); $filename = $stat_data['name']; if ($this->filters->isFiltered($filename)) { if (Log::isLevel(Log::LV_DETAILED)) { // optimization Log::info("FILE EXTRACTION SKIP: " . Log::v2str($filename), Log::LV_DETAILED); } } else { $extract_filename = $filename; $size_in_micro_chunk += $stat_data['size']; $no_of_files_in_micro_chunk++; } $this->archive_offset++; } while ( $this->archive_offset < $num_files_minus_1 && $no_of_files_in_micro_chunk < 1 && $size_in_micro_chunk < $this->max_size_extract_at_a_time ); if (!empty($extract_filename)) { // skip dup-installer folder. Alrady extracted in bootstrap if ( (strpos($extract_filename, $dupInstallerZipPath) === 0) || (strlen($this->sub_folder_archive) > 0 && strpos($extract_filename, $this->sub_folder_archive) !== 0) ) { Log::info("SKIPPING NOT IN ZIPATH:\"" . Log::v2str($extract_filename) . "\"", Log::LV_DETAILED); } else { $destFilePath = $archiveConfig->destFileFromArchiveName($extract_filename); $this->extractFile($zip, $extract_filename, $destFilePath); } } $extracted_size += $size_in_micro_chunk; if ($this->archive_offset == $this->num_files - 1) { if (!empty($this->sub_folder_archive)) { DUPX_U::moveUpfromSubFolder($this->root_path . $this->sub_folder_archive, true); } Log::info("FILE EXTRACTION: done processing last file in list of {$this->num_files}"); $this->chunkedExtractionCompleted = true; break; } if (PrmMng::getInstance()->getValue(PrmMng::PARAM_ZIP_THROTTLING)) { for ($i = 0; $i < self::ZIP_THROTTLING_ITERATIONS; $i++) { usleep(self::ZIP_THROTTLING_SLEEP_TIME); } } if (($time_over = $chunk && (DUPX_U::getMicrotime() - $this->chunkStart) > DUPX_Constants::CHUNK_EXTRACTION_TIMEOUT_TIME_ZIP)) { Log::info("TIME IS OVER - CHUNK", 2); } } while ($this->archive_offset < $num_files_minus_1 && !$time_over); // set handler as default LogHandler::setMode(); $zip->close(); $chunk_time = DUPX_U::getMicrotime() - $this->chunkStart; $chunk_extract_rate = $extracted_size / $chunk_time; $this->zip_arc_chunks_extract_rates[] = $chunk_extract_rate; $zip_arc_chunks_extract_rates = $this->zip_arc_chunks_extract_rates; $average_extract_rate = array_sum($zip_arc_chunks_extract_rates) / count($zip_arc_chunks_extract_rates); $expected_extract_time = $average_extract_rate > 0 ? DUPX_Conf_Utils::archiveSize() / $average_extract_rate : 0; /* Log::info("Expected total archive extract time: {$expected_extract_time}"); Log::info("Total extraction elapsed time until now: {$expected_extract_time}"); */ $elapsed_time = DUPX_U::getMicrotime() - $this->extractonStart; $max_no_of_notices = count($GLOBALS['ZIP_ARC_CHUNK_EXTRACT_NOTICES']) - 1; $zip_arc_chunk_extract_disp_notice_after = $GLOBALS['ZIP_ARC_CHUNK_EXTRACT_DISP_NOTICE_AFTER']; $zip_arc_chunk_extract_disp_notice_min_expected_extract_time = $GLOBALS['ZIP_ARC_CHUNK_EXTRACT_DISP_NOTICE_MIN_EXPECTED_EXTRACT_TIME']; $zip_arc_chunk_extract_disp_next_notice_interval = $GLOBALS['ZIP_ARC_CHUNK_EXTRACT_DISP_NEXT_NOTICE_INTERVAL']; if ($this->zip_arc_chunk_notice_no < 0) { // -1 if ( ( $elapsed_time > $zip_arc_chunk_extract_disp_notice_after && $expected_extract_time > $zip_arc_chunk_extract_disp_notice_min_expected_extract_time ) || $elapsed_time > $zip_arc_chunk_extract_disp_notice_min_expected_extract_time ) { $this->zip_arc_chunk_notice_no++; $this->zip_arc_chunk_notice_change_last_time = DUPX_U::getMicrotime(); } } elseif ($this->zip_arc_chunk_notice_no > 0 && $this->zip_arc_chunk_notice_no < $max_no_of_notices) { $interval_after_last_notice = DUPX_U::getMicrotime() - $this->zip_arc_chunk_notice_change_last_time; Log::info("Interval after last notice: {$interval_after_last_notice}"); if ($interval_after_last_notice > $zip_arc_chunk_extract_disp_next_notice_interval) { $this->zip_arc_chunk_notice_no++; $this->zip_arc_chunk_notice_change_last_time = DUPX_U::getMicrotime(); } } $nManager->saveNotices(); //rsr todo uncomment when debugging Log::info("Zip archive chunk notice no.: {$this->zip_arc_chunk_notice_no}"); } /** * Set files permission * * @param string $path Path * @param boolean $setDir Folders permissions * @param boolean $setFile Files permissions * * @return boolean // false if fail, if file don't exists retur true */ public static function setPermsFromParams($path, $setDir = true, $setFile = true) { static $permsSettings = null; if (is_null($permsSettings)) { $paramsManager = PrmMng::getInstance(); $permsSettings = array( 'fileSet' => $paramsManager->getValue(PrmMng::PARAM_SET_FILE_PERMS), 'fileVal' => $paramsManager->getValue(PrmMng::PARAM_FILE_PERMS_VALUE), 'dirSet' => $paramsManager->getValue(PrmMng::PARAM_SET_DIR_PERMS), 'dirVal' => $paramsManager->getValue(PrmMng::PARAM_DIR_PERMS_VALUE) ); } if (!file_exists($path)) { return true; } if (is_file($path) || is_link($path)) { if ($setFile && $permsSettings['fileSet']) { if (!SnapIO::chmod($path, $permsSettings['fileVal'])) { Log::info('CHMOD FAIL: ' . $path . ' PERMS: ' . SnapIO::permsToString($permsSettings['fileVal'])); return false; } } } else { if ($setDir && $permsSettings['dirSet']) { if (!SnapIO::chmod($path, $permsSettings['dirVal'])) { Log::info('CHMOD FAIL: ' . $path . ' PERMS: ' . SnapIO::permsToString($permsSettings['dirVal'])); return false; } } } return true; } /** * Extract file from zip archive * * @param ZipArchive $zipObj Zip archive object * @param string $zipFilename File name * @param string $newFilePath Path to extract * * @return void */ protected function extractFile(ZipArchive $zipObj, $zipFilename, $newFilePath) { try { //rsr uncomment if debugging Log::info("Attempting to extract {$zipFilename}. Time:". time()); $error = false; // IF EXIST SET READ WRITE PERMISSION if (is_file($newFilePath) || is_link($newFilePath)) { SnapIO::chmod($newFilePath, 'u+rw'); } elseif (is_dir($newFilePath)) { SnapIO::chmod($newFilePath, 'u+rwx'); } if ($this->root_path . ltrim($zipFilename, '\\/') === $newFilePath) { if (Log::isLevel(Log::LV_DEBUG)) { Log::info('EXTRACT FILE [' . $zipFilename . '] TO [' . $newFilePath . ']', Log::LV_DEBUG); } if (!$zipObj->extractTo($this->root_path, $zipFilename)) { $error = true; } } else { if (Log::isLevel(Log::LV_DEBUG)) { Log::info('CUSTOM EXTRACT FILE [' . $zipFilename . '] TO [' . $newFilePath . ']', Log::LV_DEBUG); } if (substr($zipFilename, -1) === '/') { SnapIO::mkdirP(dirname($newFilePath)); } else { if (($destStream = fopen($newFilePath, 'w')) === false) { if (!file_exists(dirname($newFilePath))) { SnapIO::mkdirP(dirname($newFilePath)); if (($destStream = fopen($newFilePath, 'w')) === false) { $error = true; } } else { $error = true; } } if ($error || ($sourceStream = $zipObj->getStream($zipFilename)) === false) { $error = true; } else { while (!feof($sourceStream)) { fwrite($destStream, fread($sourceStream, 1048576)); // 1M } fclose($sourceStream); fclose($destStream); } } } if ($error) { self::reportExtractionNotices($zipFilename, LogHandler::getVarLogClean()); } else { if (Log::isLevel(Log::LV_HARD_DEBUG)) { Log::info("FILE EXTRACTION DONE: " . Log::v2str($zipFilename), Log::LV_HARD_DEBUG); } // SET ONLY FILES self::setPermsFromParams($newFilePath, false); if (PrmMng::getInstance()->getValue(PrmMng::PARAM_FILE_TIME) == 'current') { touch($newFilePath, time()); } } } catch (Exception $ex) { self::reportExtractionNotices($zipFilename, $ex->getMessage()); } } /** * * @param string $fileName package relative path * @param string $errorMessage error message * * @return void */ protected static function reportExtractionNotices($fileName, $errorMessage) { if (DUPX_Custom_Host_Manager::getInstance()->skipWarningExtractionForManaged($fileName)) { // @todo skip warning for managed hostiong (it's a temp solution) return; } $nManager = DUPX_NOTICE_MANAGER::getInstance(); if (SnapWP::isWpCore($fileName, SnapWP::PATH_RELATIVE)) { Log::info("FILE CORE EXTRACTION ERROR: {$fileName} | MSG:" . $errorMessage); $shortMsg = 'Can\'t extract wp core files'; $finalShortMsg = 'Wp core files not extracted'; $errLevel = DUPX_NOTICE_ITEM::CRITICAL; $idManager = 'wp-extract-error-file-core'; } else { Log::info("FILE EXTRACTION ERROR: {$fileName} | MSG:" . $errorMessage); $shortMsg = 'Can\'t extract files'; $finalShortMsg = 'Files not extracted'; $errLevel = DUPX_NOTICE_ITEM::SOFT_WARNING; $idManager = 'wp-extract-error-file-no-core'; } $longMsg = 'FILE: <b>' . htmlspecialchars($fileName) . '</b><br>Message: ' . htmlspecialchars($errorMessage) . '<br><br>'; $nManager->addNextStepNotice(array( 'shortMsg' => $shortMsg, 'longMsg' => $longMsg, 'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_HTML, 'level' => $errLevel ), DUPX_NOTICE_MANAGER::ADD_UNIQUE_APPEND, $idManager); $nManager->addFinalReportNotice(array( 'shortMsg' => $finalShortMsg, 'longMsg' => $longMsg, 'longMsgMode' => DUPX_NOTICE_ITEM::MSG_MODE_HTML, 'level' => $errLevel, 'sections' => array('files'), ), DUPX_NOTICE_MANAGER::ADD_UNIQUE_APPEND, $idManager); } /** * Export db only * * @return void */ protected function exportOnlyDB() { if ($this->archive_engine == self::ENGINE_MANUAL || $this->archive_engine == self::ENGINE_DUP) { $sql_file_path = DUPX_Package::getSqlFilePath(); if (!file_exists(DUPX_Package::getWpconfigArkPath()) && !file_exists($sql_file_path)) { Log::error(ERR_ZIPMANUAL); } } else { if (!is_readable("{$this->archive_path}")) { Log::error("archive file path:<br/>" . ERR_ZIPNOTFOUND); } } } /** * Write extraction log header * * @return void */ protected function logStart() { $paramsManager = PrmMng::getInstance(); Log::info("********************************************************************************"); Log::info('* DUPLICATOR LITE: Install-Log'); Log::info('* STEP-1 START @ ' . @date('h:i:s')); Log::info('* NOTICE: Do NOT post to public sites or forums!!'); Log::info("********************************************************************************"); $labelPadSize = 20; Log::info("USER INPUTS"); Log::info(str_pad('INSTALL TYPE', $labelPadSize, '_', STR_PAD_RIGHT) . ': ' . DUPX_InstallerState::installTypeToString()); Log::info(str_pad('BLOG NAME', $labelPadSize, '_', STR_PAD_RIGHT) . ': ' . Log::v2str($paramsManager->getValue(PrmMng::PARAM_BLOGNAME))); Log::info(str_pad('HOME URL NEW', $labelPadSize, '_', STR_PAD_RIGHT) . ': ' . Log::v2str($paramsManager->getValue(PrmMng::PARAM_URL_NEW))); Log::info(str_pad('SITE URL NEW', $labelPadSize, '_', STR_PAD_RIGHT) . ': ' . Log::v2str($paramsManager->getValue(PrmMng::PARAM_SITE_URL))); Log::info(str_pad('CONTENT URL NEW', $labelPadSize, '_', STR_PAD_RIGHT) . ': ' . Log::v2str($paramsManager->getValue(PrmMng::PARAM_URL_CONTENT_NEW))); Log::info(str_pad('UPLOAD URL NEW', $labelPadSize, '_', STR_PAD_RIGHT) . ': ' . Log::v2str($paramsManager->getValue(PrmMng::PARAM_URL_UPLOADS_NEW))); Log::info(str_pad('PLUGINS URL NEW', $labelPadSize, '_', STR_PAD_RIGHT) . ': ' . Log::v2str($paramsManager->getValue(PrmMng::PARAM_URL_PLUGINS_NEW))); Log::info( str_pad('MUPLUGINS URL NEW', $labelPadSize, '_', STR_PAD_RIGHT) . ': ' . Log::v2str($paramsManager->getValue(PrmMng::PARAM_URL_MUPLUGINS_NEW)) ); Log::info(str_pad('HOME PATH NEW', $labelPadSize, '_', STR_PAD_RIGHT) . ': ' . Log::v2str($paramsManager->getValue(PrmMng::PARAM_PATH_NEW))); Log::info(str_pad('SITE PATH NEW', $labelPadSize, '_', STR_PAD_RIGHT) . ': ' . Log::v2str($paramsManager->getValue(PrmMng::PARAM_PATH_WP_CORE_NEW))); Log::info(str_pad('CONTENT PATH NEW', $labelPadSize, '_', STR_PAD_RIGHT) . ': ' . Log::v2str($paramsManager->getValue(PrmMng::PARAM_PATH_CONTENT_NEW))); Log::info(str_pad('UPLOAD PATH NEW', $labelPadSize, '_', STR_PAD_RIGHT) . ': ' . Log::v2str($paramsManager->getValue(PrmMng::PARAM_PATH_UPLOADS_NEW))); Log::info(str_pad('PLUGINS PATH NEW', $labelPadSize, '_', STR_PAD_RIGHT) . ': ' . Log::v2str($paramsManager->getValue(PrmMng::PARAM_PATH_PLUGINS_NEW))); Log::info( str_pad('MUPLUGINS PATH NEW', $labelPadSize, '_', STR_PAD_RIGHT) . ': ' . Log::v2str($paramsManager->getValue(PrmMng::PARAM_PATH_MUPLUGINS_NEW)) ); Log::info(str_pad('ARCHIVE ACTION', $labelPadSize, '_', STR_PAD_RIGHT) . ': ' . Log::v2str($paramsManager->getValue(PrmMng::PARAM_ARCHIVE_ACTION))); Log::info( str_pad( 'SKIP WP FILES', $labelPadSize, '_', STR_PAD_RIGHT ) . ': ' . Log::v2str($paramsManager->getValue(PrmMng::PARAM_ARCHIVE_ENGINE_SKIP_WP_FILES)) ); Log::info(str_pad('ARCHIVE ENGINE', $labelPadSize, '_', STR_PAD_RIGHT) . ': ' . Log::v2str($paramsManager->getValue(PrmMng::PARAM_ARCHIVE_ENGINE))); Log::info(str_pad('SET DIR PERMS', $labelPadSize, '_', STR_PAD_RIGHT) . ': ' . Log::v2str($paramsManager->getValue(PrmMng::PARAM_SET_DIR_PERMS))); Log::info( str_pad( 'DIR PERMS VALUE', $labelPadSize, '_', STR_PAD_RIGHT ) . ': ' . SnapIO::permsToString($paramsManager->getValue(PrmMng::PARAM_DIR_PERMS_VALUE)) ); Log::info(str_pad('SET FILE PERMS', $labelPadSize, '_', STR_PAD_RIGHT) . ': ' . Log::v2str($paramsManager->getValue(PrmMng::PARAM_SET_FILE_PERMS))); Log::info( str_pad( 'FILE PERMS VALUE', $labelPadSize, '_', STR_PAD_RIGHT ) . ': ' . SnapIO::permsToString($paramsManager->getValue(PrmMng::PARAM_FILE_PERMS_VALUE)) ); Log::info(str_pad('SAFE MODE', $labelPadSize, '_', STR_PAD_RIGHT) . ': ' . Log::v2str($paramsManager->getValue(PrmMng::PARAM_SAFE_MODE))); Log::info(str_pad('LOGGING', $labelPadSize, '_', STR_PAD_RIGHT) . ': ' . Log::v2str($paramsManager->getValue(PrmMng::PARAM_LOGGING))); Log::info(str_pad('ZIP THROTTLING', $labelPadSize, '_', STR_PAD_RIGHT) . ': ' . Log::v2str($paramsManager->getValue(PrmMng::PARAM_ZIP_THROTTLING))); Log::info(str_pad('WP CONFIG', $labelPadSize, '_', STR_PAD_RIGHT) . ': ' . Log::v2str($paramsManager->getValue(PrmMng::PARAM_WP_CONFIG))); Log::info(str_pad('HTACCESS CONFIG', $labelPadSize, '_', STR_PAD_RIGHT) . ': ' . Log::v2str($paramsManager->getValue(PrmMng::PARAM_HTACCESS_CONFIG))); Log::info(str_pad('OTHER CONFIG', $labelPadSize, '_', STR_PAD_RIGHT) . ': ' . Log::v2str($paramsManager->getValue(PrmMng::PARAM_OTHER_CONFIG))); Log::info(str_pad('FILE TIME', $labelPadSize, '_', STR_PAD_RIGHT) . ': ' . Log::v2str($paramsManager->getValue(PrmMng::PARAM_FILE_TIME))); Log::info("********************************************************************************\n"); Log::info('REMOVE FILTERS'); Log::incIndent(); foreach ($this->removeFilters->getDirs() as $path) { Log::info('DIR : ' . Log::v2str($path)); } foreach ($this->removeFilters->getFiles() as $path) { Log::info('FILE: ' . Log::v2str($path)); } foreach ($this->removeFilters->getDirsWithoutChilds() as $path) { Log::info('DIRS WITHOUT CHILDS: ' . Log::v2str($path)); } Log::resetIndent(); Log::info('EXTRACTION FILTERS'); Log::incIndent(); foreach ($this->filters->getDirs() as $path) { Log::info('DIR : ' . Log::v2str($path)); } foreach ($this->filters->getFiles() as $path) { Log::info('FILE: ' . Log::v2str($path)); } foreach ($this->filters->getDirsWithoutChilds() as $path) { Log::info('DIR WITHOUT CHILDS: ' . Log::v2str($path)); } Log::resetIndent(); Log::info("--------------------------------------\n"); switch ($this->archive_engine) { case self::ENGINE_ZIP_CHUNK: Log::info("\nEXTRACTION: ZIP CHUNKING >>> START"); break; case self::ENGINE_ZIP: Log::info("\nEXTRACTION: ZIP STANDARD >>> START"); break; case self::ENGINE_MANUAL: Log::info("\nEXTRACTION: MANUAL MODE >>> START"); break; case self::ENGINE_ZIP_SHELL: Log::info("\nEXTRACTION: ZIP SHELL >>> START"); break; case self::ENGINE_DUP: Log::info("\nEXTRACTION: DUP ARCHIVE >>> START"); break; default: throw new Exception('No valid engine ' . $this->archive_engine); } } /** * Write log extraction end * * @return void */ protected function logComplete() { switch ($this->archive_engine) { case self::ENGINE_ZIP_CHUNK: Log::info("\nEXTRACTION: ZIP CHUNKING >>> DONE"); break; case self::ENGINE_ZIP: Log::info("\nEXTRACTION: ZIP STANDARD >>> DONE"); break; case self::ENGINE_MANUAL: Log::info("\nEXTRACTION: MANUAL MODE >>> DONE"); break; case self::ENGINE_ZIP_SHELL: Log::info("\nEXTRACTION: ZIP SHELL >>> DONE"); break; case self::ENGINE_DUP: $criticalPresent = false; if (count($this->dawn_status->failures) > 0) { $log = ''; foreach ($this->dawn_status->failures as $failure) { if ($failure->isCritical) { $log .= 'DUP EXTRACTION CRITICAL ERROR ' . $failure->description; $criticalPresent = true; } } if (!empty($log)) { Log::info($log); } } if ($criticalPresent) { throw new Exception('Critical Errors present so stopping install.'); } Log::info("\n\nEXTRACTION: DUP ARCHIVE >>> DONE"); break; default: throw new Exception('No valid engine ' . $this->archive_engine); } } /** * Extract zip archive via shell * * @return void */ protected function runShellExec() { $command = escapeshellcmd(DUPX_Server::get_unzip_filepath()) . " -o -qq " . escapeshellarg($this->archive_path) . " -d " . escapeshellarg($this->root_path) . " 2>&1"; if ($this->zip_filetime == 'original') { Log::info("\nShell Exec Current does not support orginal file timestamp please use ZipArchive"); } Log::info('SHELL COMMAND: ' . Log::v2str($command)); $stderr = shell_exec($command); if ($stderr != '') { $faqUrl = LinkManager::getDocUrl('how-to-fix-installer-archive-extraction-issues', 'install', 'shell exec error'); $zip_err_msg = ERR_SHELLEXEC_ZIPOPEN . ": $stderr"; $zip_err_msg .= '<br/><br/><b>To resolve error see <a href="' . $faqUrl . '" target="_blank">' . DUPX_Constants::FAQ_URL . "how-to-fix-installer-archive-extraction-issues</a></b>"; Log::error($zip_err_msg); } } /** * Set file permission via shell * * @param boolean|string $dirPerm folders permissions * @param boolean|string $filePerm files permsission * @param boolean $excludeDupInit if true dont set permsission on dup folder * * @return void */ protected static function setPermsViaShell($dirPerm = false, $filePerm = false, $excludeDupInit = false) { $rootPath = PrmMng::getInstance()->getValue(PrmMng::PARAM_PATH_NEW); $exludeDupFolder = ($excludeDupInit ? "! -path " . escapeshellarg(DUPX_INIT . '*') . " " : ''); if ($filePerm !== false) { $command = "find " . escapeshellarg($rootPath) . " -type d " . $exludeDupFolder . "-exec chmod " . SnapIO::permsToString($dirPerm) . " {} \;"; Log::info('SHELL COMMAND: ' . Log::v2str($command)); shell_exec($command); } if ($dirPerm !== false) { $command = "find " . escapeshellarg($rootPath) . " -type f " . $exludeDupFolder . "-exec chmod " . SnapIO::permsToString($filePerm) . " {} \;"; Log::info('SHELL COMMAND: ' . Log::v2str($command)); shell_exec($command); } } /** * * @return string */ public static function getInitialFileProcessedString() { return 'Files processed: 0 of ' . number_format(DUPX_ArchiveConfig::getInstance()->totalArchiveItemsCount()); } /** * Get extraction result * * @param boolean $complete true if extraction is complate false if chunk is complete * * @return array */ protected function getResultExtraction($complete = false) { $result = array( 'pass' => 0, 'processedFiles' => '', 'perc' => '' ); if ($complete) { $result['pass'] = 1; $result['perc'] = '100%'; switch ($this->archive_engine) { case self::ENGINE_ZIP_CHUNK: case self::ENGINE_ZIP: case self::ENGINE_ZIP_SHELL: case self::ENGINE_DUP: $result['processedFiles'] = 'Files processed: ' . number_format($this->archive_items_count) . ' of ' . number_format($this->archive_items_count); break; case self::ENGINE_MANUAL: break; default: throw new Exception('No valid engine ' . $this->archive_engine); } $deltaTime = DUPX_U::elapsedTime(DUPX_U::getMicrotime(), $this->extractonStart); Log::info("\nEXTRACTION COMPLETE @ " . @date('h:i:s') . " - RUNTIME: {$deltaTime} - " . $result['processedFiles']); } else { $result['pass'] = -1; switch ($this->archive_engine) { case self::ENGINE_ZIP_CHUNK: case self::ENGINE_ZIP: case self::ENGINE_ZIP_SHELL: $result['processedFiles'] = 'Files processed: ' . number_format(min($this->archive_offset, $this->archive_items_count)) . ' of ' . number_format($this->archive_items_count); $result['perc'] = min(100, round(($this->archive_offset * 100 / $this->archive_items_count), 2)) . '%'; break; case self::ENGINE_DUP: $result['processedFiles'] = 'Files processed: ' . number_format(min($this->dawn_status->file_index, $this->archive_items_count)) . ' of ' . number_format($this->archive_items_count); $result['perc'] = min(100, round(($this->dawn_status->file_index * 100 / $this->archive_items_count), 2)) . '%'; break; case self::ENGINE_MANUAL: break; default: throw new Exception('No valid engine ' . $this->archive_engine); } $deltaTime = DUPX_U::elapsedTime(DUPX_U::getMicrotime(), $this->chunkStart); Log::info("CHUNK COMPLETE - RUNTIME: {$deltaTime} - " . $result['processedFiles']); } return $result; } /** * End extraction * * @return array */ protected function finishFullExtraction() { $this->configFilesCheckNotice(); $this->logComplete(); return $this->getResultExtraction(true); } /** * End chunked extraction * * @return array */ protected function finishChunkExtraction() { $this->saveData(); return $this->getResultExtraction(false); } /** * Finish extraction process * * @return array */ public function finishExtraction() { $complete = false; switch ($this->archive_engine) { case self::ENGINE_ZIP_CHUNK: $complete = $this->chunkedExtractionCompleted; break; case self::ENGINE_DUP: $complete = $this->dawn_status->is_done; break; case self::ENGINE_ZIP: case self::ENGINE_MANUAL: case self::ENGINE_ZIP_SHELL: $complete = true; break; default: throw new Exception('No valid engine ' . $this->archive_engine); } if ($complete) { return $this->finishFullExtraction(); } else { return $this->finishChunkExtraction(); } } /** * * @return bool */ protected function isFirst() { switch ($this->archive_engine) { case self::ENGINE_ZIP_CHUNK: return $this->archive_offset == 0 && $this->archive_engine == self::ENGINE_ZIP_CHUNK; case self::ENGINE_DUP: return is_null($this->dawn_status); case self::ENGINE_ZIP: case self::ENGINE_MANUAL: case self::ENGINE_ZIP_SHELL: return true; default: throw new Exception('No valid engine ' . $this->archive_engine); } } }