1021 lines
37 KiB
PHP
1021 lines
37 KiB
PHP
<?php
|
|
|
|
use Duplicator\Libs\Snap\SnapIO;
|
|
use Duplicator\Libs\Snap\SnapOS;
|
|
use Duplicator\Libs\Snap\SnapURL;
|
|
use Duplicator\Libs\Snap\SnapUtil;
|
|
use Duplicator\Libs\Snap\SnapWP;
|
|
|
|
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
|
// Exit if accessed directly
|
|
if (!defined('DUPLICATOR_VERSION')) {
|
|
exit;
|
|
}
|
|
|
|
require_once(DUPLICATOR_PLUGIN_PATH . 'classes/package/duparchive/class.pack.archive.duparchive.php');
|
|
require_once(DUPLICATOR_PLUGIN_PATH . 'classes/package/class.pack.archive.file.list.php');
|
|
require_once(DUPLICATOR_PLUGIN_PATH . 'classes/package/class.pack.archive.filters.php');
|
|
require_once(DUPLICATOR_PLUGIN_PATH . 'classes/package/class.pack.archive.zip.php');
|
|
require_once(DUPLICATOR_PLUGIN_PATH . 'lib/forceutf8/Encoding.php');
|
|
/**
|
|
* Class for handling archive setup and build process
|
|
*
|
|
* Standard: PSR-2 (almost)
|
|
*
|
|
* @link http://www.php-fig.org/psr/psr-2
|
|
*
|
|
* @package DUP
|
|
* @subpackage classes/package
|
|
* @copyright (c) 2017, Snapcreek LLC
|
|
* @license https://opensource.org/licenses/GPL-3.0 GNU Public License
|
|
*/
|
|
class DUP_Archive
|
|
{
|
|
const DIRS_LIST_FILE_NAME_SUFFIX = '_dirs.txt';
|
|
const FILES_LIST_FILE_NAME_SUFFIX = '_files.txt';
|
|
|
|
//PUBLIC
|
|
public $FilterDirs;
|
|
public $FilterFiles;
|
|
public $FilterExts;
|
|
public $FilterDirsAll = array();
|
|
public $FilterFilesAll = array();
|
|
public $FilterExtsAll = array();
|
|
public $FilterOn;
|
|
public $ExportOnlyDB;
|
|
public $File;
|
|
public $Format;
|
|
public $PackDir = '';
|
|
public $Size = 0;
|
|
public $Dirs = array();
|
|
public $dirsCount = 0;
|
|
public $Files = array();
|
|
public $filesCount = 0;
|
|
/** @var DUP_Archive_Filter_Info */
|
|
public $FilterInfo = null;
|
|
public $RecursiveLinks = array();
|
|
public $file_count = -1;
|
|
/** @var DUP_Package */
|
|
protected $Package;
|
|
private $tmpFilterDirsAll = array();
|
|
private $wpCorePaths = array();
|
|
private $wpCoreExactPaths = array();
|
|
private $relativeFiltersDir = array();
|
|
|
|
/** @var DUP_Archive_File_List */
|
|
private $listFileObj = null;
|
|
/** @var DUP_Archive_File_List */
|
|
private $listDirObj = null;
|
|
/** @var string|null */
|
|
private $wpContentDirNormalizePath = null;
|
|
|
|
/**
|
|
* Class constructor
|
|
*
|
|
* @param DUP_Package $package
|
|
*/
|
|
public function __construct(DUP_Package $package)
|
|
{
|
|
$this->Package = $package;
|
|
$this->FilterOn = false;
|
|
$this->ExportOnlyDB = false;
|
|
$this->FilterInfo = new DUP_Archive_Filter_Info();
|
|
$this->PackDir = $this->getTargetRootPath();
|
|
|
|
$paths = self::getArchiveListPaths();
|
|
$this->wpCorePaths[] = $paths['abs'] . '/wp-admin';
|
|
$this->wpCorePaths[] = $paths['abs'] . '/wp-includes';
|
|
$this->wpCorePaths[] = $paths['wpcontent'] . '/languages';
|
|
$this->wpCoreExactPaths[] = $paths['home'];
|
|
$this->wpCoreExactPaths[] = $paths['abs'];
|
|
$this->wpCoreExactPaths[] = $paths['wpcontent'];
|
|
$this->wpCoreExactPaths[] = $paths['uploads'];
|
|
$this->wpCoreExactPaths[] = $paths['plugins'];
|
|
$this->wpCoreExactPaths[] = $paths['muplugins'];
|
|
$this->wpCoreExactPaths[] = $paths['themes'];
|
|
|
|
$this->relativeFiltersDir = array(DUP_Settings::getSsdirTmpPath(), 'backups-dup-pro', 'duplicator-backups');
|
|
}
|
|
|
|
/**
|
|
* Builds the archive based on the archive type
|
|
*
|
|
* @param obj $package The package object that started this process
|
|
*
|
|
* @return null
|
|
*/
|
|
public function build($package, $rethrow_exception = false)
|
|
{
|
|
DUP_LOG::trace("b1");
|
|
$this->Package = $package;
|
|
if (!isset($this->PackDir) && !is_dir($this->PackDir)) {
|
|
throw new Exception("The 'PackDir' property must be a valid directory.");
|
|
}
|
|
if (!isset($this->File)) {
|
|
throw new Exception("A 'File' property must be set.");
|
|
}
|
|
|
|
DUP_LOG::trace("b2");
|
|
$completed = false;
|
|
|
|
switch ($this->Format) {
|
|
case 'TAR':
|
|
break;
|
|
case 'TAR-GZIP':
|
|
break;
|
|
case 'DAF':
|
|
$completed = DUP_DupArchive::create($this, $this->Package->BuildProgress, $this->Package);
|
|
$this->Package->Update();
|
|
break;
|
|
default:
|
|
if (class_exists('ZipArchive')) {
|
|
$this->Format = 'ZIP';
|
|
DUP_Zip::create($this, $this->Package->BuildProgress);
|
|
$completed = true;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
DUP_LOG::Trace("Completed build or build thread");
|
|
if ($this->Package->BuildProgress === null) {
|
|
// Zip path
|
|
DUP_LOG::Trace("Completed Zip");
|
|
$storePath = DUP_Settings::getSsdirTmpPath() . "/{$this->File}";
|
|
$this->Size = @filesize($storePath);
|
|
$this->Package->setStatus(DUP_PackageStatus::ARCDONE);
|
|
} elseif ($completed) {
|
|
// Completed DupArchive path
|
|
DUP_LOG::Trace("Completed DupArchive build");
|
|
if ($this->Package->BuildProgress->failed) {
|
|
DUP_LOG::Trace("Error building DupArchive");
|
|
$this->Package->setStatus(DUP_PackageStatus::ERROR);
|
|
} else {
|
|
$filepath = DUP_Settings::getSsdirTmpPath() . "/{$this->File}";
|
|
$this->Size = @filesize($filepath);
|
|
$this->Package->setStatus(DUP_PackageStatus::ARCDONE);
|
|
DUP_LOG::Trace("Done building archive");
|
|
}
|
|
} else {
|
|
DUP_Log::trace("DupArchive chunk done but package not completed yet");
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @return int return DUP_Archive_Build_Mode
|
|
*/
|
|
public function getBuildMode()
|
|
{
|
|
switch ($this->Format) {
|
|
case 'TAR':
|
|
break;
|
|
case 'TAR-GZIP':
|
|
break;
|
|
case 'DAF':
|
|
return DUP_Archive_Build_Mode::DupArchive;
|
|
default:
|
|
if (class_exists('ZipArchive')) {
|
|
return DUP_Archive_Build_Mode::ZipArchive;
|
|
} else {
|
|
return DUP_Archive_Build_Mode::Unconfigured;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initializes the file list handles. Handles are set-up as properties for
|
|
* performance improvement. Otherwise each handle would be opened and closed
|
|
* with each path added.
|
|
*/
|
|
private function initFileListHandles()
|
|
{
|
|
DUP_Log::trace('Inif list files');
|
|
if (is_null($this->listDirObj)) {
|
|
$this->listDirObj = new DUP_Archive_File_List(DUP_Settings::getSsdirTmpPath() . '/' . $this->Package->get_dirs_list_filename());
|
|
}
|
|
if (is_null($this->listFileObj)) {
|
|
$this->listFileObj = new DUP_Archive_File_List(DUP_Settings::getSsdirTmpPath() . '/' . $this->Package->get_files_list_filename());
|
|
}
|
|
$this->listDirObj->open(true);
|
|
$this->listFileObj->open(true);
|
|
}
|
|
|
|
/**
|
|
* Closes file and dir list handles
|
|
*/
|
|
private function closeFileListHandles()
|
|
{
|
|
$this->listDirObj->close();
|
|
$this->listFileObj->close();
|
|
}
|
|
|
|
/**
|
|
* Builds a list of files and directories to be included in the archive
|
|
*
|
|
* Get the directory size recursively, but don't calc the snapshot directory, exclusion directories
|
|
*
|
|
* @link http://msdn.microsoft.com/en-us/library/aa365247%28VS.85%29.aspx Windows filename restrictions
|
|
*
|
|
* @return obj Returns a DUP_Archive object
|
|
*/
|
|
public function getScannerData()
|
|
{
|
|
$this->initFileListHandles();
|
|
$this->createFilterInfo();
|
|
$rootPath = $this->getTargetRootPath();
|
|
if (strlen($rootPath) == 0) {
|
|
$rootPath = '/';
|
|
}
|
|
|
|
$this->RecursiveLinks = array();
|
|
// If the root directory is a filter then skip it all
|
|
if (in_array($rootPath, $this->FilterDirsAll) || $this->Package->Archive->ExportOnlyDB) {
|
|
$this->Dirs = array();
|
|
$this->dirsCount = 0;
|
|
$this->Files = array();
|
|
$this->filesCount = 0;
|
|
} else {
|
|
$mainSize = 0;
|
|
$mainNodes = 0;
|
|
$pathsToScan = self::getScanPaths();
|
|
foreach ($pathsToScan as $path) {
|
|
DUP_Log::trace('START SCAN PATH: ' . $path);
|
|
$relativePath = SnapIO::getRelativePath($path, $rootPath);
|
|
if (($result = $this->getFileLists($path, $relativePath)) != false) {
|
|
$this->addToDirList($path, $relativePath, $result['size'], $result['nodes'] + 1);
|
|
$mainSize += $result['size'];
|
|
$mainNodes += $result['nodes'] + 1;
|
|
} else {
|
|
DUP_Log::trace('Can\'t scan the folder ' . $rootPath);
|
|
}
|
|
}
|
|
|
|
if (!in_array($rootPath, $pathsToScan)) {
|
|
$this->addToDirList($rootPath, '', $mainSize, $mainNodes + 1);
|
|
}
|
|
|
|
$this->setTreeFilters();
|
|
}
|
|
|
|
$this->closeFileListHandles();
|
|
|
|
$this->FilterDirsAll = array_merge($this->FilterDirsAll, $this->FilterInfo->Dirs->Unreadable);
|
|
$this->FilterFilesAll = array_merge($this->FilterFilesAll, $this->FilterInfo->Files->Unreadable);
|
|
sort($this->FilterDirsAll);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Save any property of this class through reflection
|
|
*
|
|
* @param $property A valid public property in this class
|
|
* @param $value The value for the new dynamic property
|
|
*
|
|
* @return bool Returns true if the value has changed.
|
|
*/
|
|
public function saveActiveItem($package, $property, $value)
|
|
{
|
|
$package = DUP_Package::getActive();
|
|
$reflectionClass = new ReflectionClass($package->Archive);
|
|
$reflectionClass->getProperty($property)->setValue($package->Archive, $value);
|
|
return update_option(DUP_Package::OPT_ACTIVE, $package);
|
|
}
|
|
|
|
/**
|
|
* Properly creates the directory filter list that is used for filtering directories
|
|
*
|
|
* @param string $dirs A semi-colon list of dir paths /path1_/path;/path1_/path2;
|
|
*
|
|
* @return string A cleaned up list of directory filters
|
|
*/
|
|
public function parseDirectoryFilter($dirs = "")
|
|
{
|
|
$clean_array = array();
|
|
$dir_array = array_unique(explode(";", $dirs));
|
|
foreach ($dir_array as $val) {
|
|
$val = SnapIO::safePathUntrailingslashit(SnapUtil::sanitizeNSCharsNewlineTrim($val));
|
|
if (strlen($val) >= 2 && is_dir($val)) {
|
|
$clean_array[] = $val;
|
|
}
|
|
}
|
|
|
|
if (count($clean_array)) {
|
|
return implode(';', array_unique($clean_array));
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* Properly creates the file filter list that is used for filtering files
|
|
*
|
|
* @param string $files A semi-colon list of file paths /path1_/path/file1.ext;/path1_/path2/file2.ext;
|
|
*
|
|
* @return string A cleaned up list of file filters
|
|
*/
|
|
public function parseFileFilter($files = "")
|
|
{
|
|
$clean_array = array();
|
|
$file_array = array_unique(explode(";", $files));
|
|
foreach ($file_array as $val) {
|
|
$val = SnapIO::safePathUntrailingslashit(SnapUtil::sanitizeNSCharsNewlineTrim($val));
|
|
if (strlen($val) >= 2 && !is_dir($val)) {
|
|
$clean_array[] = $val;
|
|
}
|
|
}
|
|
|
|
if (count($clean_array)) {
|
|
return implode(';', array_unique($clean_array));
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* Properly creates the extension filter list that is used for filtering extensions
|
|
*
|
|
* @param string $dirs A semi-colon list of dir paths
|
|
* .jpg;.zip;.gif;
|
|
*
|
|
* @returns string A cleaned up list of extension filters
|
|
*/
|
|
public function parseExtensionFilter($extensions = "")
|
|
{
|
|
$filter_exts = "";
|
|
if (strlen($extensions) >= 1 && $extensions != ";") {
|
|
$filter_exts = str_replace(array(' ', '.'), '', $extensions);
|
|
$filter_exts = str_replace(",", ";", $filter_exts);
|
|
$filter_exts = DUP_Util::appendOnce($extensions, ";");
|
|
}
|
|
return $filter_exts;
|
|
}
|
|
|
|
/**
|
|
* Creates the filter info setup data used for filtering the archive
|
|
*
|
|
* @return null
|
|
*/
|
|
private function createFilterInfo()
|
|
{
|
|
//FILTER: INSTANCE ITEMS
|
|
//Add the items generated at create time
|
|
if ($this->FilterOn) {
|
|
$this->FilterInfo->Dirs->Instance = strlen($this->FilterDirs) > 0 ? array_map('DUP_Util::safePath', explode(";", $this->FilterDirs)) : array();
|
|
$this->FilterInfo->Files->Instance = strlen($this->FilterFiles) > 0 ? array_map('DUP_Util::safePath', explode(";", $this->FilterFiles)) : array();
|
|
$this->FilterInfo->Exts->Instance = explode(";", $this->FilterExts);
|
|
// Remove blank entries
|
|
$this->FilterInfo->Exts->Instance = array_filter(array_map('trim', $this->FilterInfo->Exts->Instance));
|
|
}
|
|
|
|
//FILTER: CORE ITMES
|
|
//Filters Duplicator free packages & All pro local directories
|
|
$wp_root = duplicator_get_abs_path();
|
|
$upload_dir = wp_upload_dir();
|
|
$upload_dir = isset($upload_dir['basedir']) ? basename($upload_dir['basedir']) : 'uploads';
|
|
$wp_content = str_replace("\\", "/", WP_CONTENT_DIR);
|
|
$wp_content_upload = "{$wp_content}/{$upload_dir}";
|
|
$this->FilterInfo->Dirs->Core = array(
|
|
//WP-ROOT
|
|
DUP_Settings::getSsdirPathLegacy(),
|
|
DUP_Settings::getSsdirPathWpCont(),
|
|
$wp_root . '/.opcache',
|
|
//WP-CONTENT
|
|
$wp_content . '/backups-dup-pro',
|
|
$wp_content . '/duplicator-backups',
|
|
$wp_content . '/ai1wm-backups',
|
|
$wp_content . '/backupwordpress',
|
|
$wp_content . '/content/cache',
|
|
$wp_content . '/contents/cache',
|
|
$wp_content . '/infinitewp/backups',
|
|
$wp_content . '/managewp/backups',
|
|
$wp_content . '/old-cache',
|
|
$wp_content . '/plugins/all-in-one-wp-migration/storage',
|
|
$wp_content . '/updraft',
|
|
$wp_content . '/wishlist-backup',
|
|
$wp_content . '/wfcache',
|
|
$wp_content . '/bps-backup', // BulletProof Security backup folder
|
|
$wp_content . '/cache',
|
|
//WP-CONTENT-UPLOADS
|
|
$wp_content_upload . '/aiowps_backups',
|
|
$wp_content_upload . '/backupbuddy_temp',
|
|
$wp_content_upload . '/backupbuddy_backups',
|
|
$wp_content_upload . '/ithemes-security/backups',
|
|
$wp_content_upload . '/mainwp/backup',
|
|
$wp_content_upload . '/pb_backupbuddy',
|
|
$wp_content_upload . '/snapshots',
|
|
$wp_content_upload . '/sucuri',
|
|
$wp_content_upload . '/wp-clone',
|
|
$wp_content_upload . '/wp_all_backup',
|
|
$wp_content_upload . '/wpbackitup_backups'
|
|
);
|
|
if (class_exists('BackWPup')) {
|
|
$upload_dir = wp_upload_dir(null, false, true);
|
|
$this->FilterInfo->Dirs->Core[] = trailingslashit(str_replace(
|
|
'\\',
|
|
'/',
|
|
$upload_dir['basedir']
|
|
)) . 'backwpup-' . BackWPup::get_plugin_data('hash') . '-backups/';
|
|
$backwpup_cfg_logfolder = get_site_option('backwpup_cfg_logfolder');
|
|
if (false !== $backwpup_cfg_logfolder) {
|
|
$this->FilterInfo->Dirs->Core[] = $wp_content . '/' . $backwpup_cfg_logfolder;
|
|
}
|
|
}
|
|
|
|
if ($GLOBALS['DUPLICATOR_GLOBAL_FILE_FILTERS_ON']) {
|
|
$duplicator_global_file_filters = apply_filters('duplicator_global_file_filters', $GLOBALS['DUPLICATOR_GLOBAL_FILE_FILTERS']);
|
|
$this->FilterInfo->Files->Global = $duplicator_global_file_filters;
|
|
}
|
|
|
|
//Config files are handled separately
|
|
foreach ($this->Package->Installer->getConfigFilePaths() as $config_file_path) {
|
|
$this->FilterInfo->Files->Global[] = $config_file_path;
|
|
}
|
|
|
|
$this->FilterDirsAll = array_merge($this->FilterInfo->Dirs->Instance, $this->FilterInfo->Dirs->Core);
|
|
$this->FilterExtsAll = array_merge($this->FilterInfo->Exts->Instance, $this->FilterInfo->Exts->Core);
|
|
DUP_Log::traceObject('FilterExtsAll:', $this->FilterExtsAll);
|
|
$this->FilterFilesAll = array_merge($this->FilterInfo->Files->Instance, $this->FilterInfo->Files->Global);
|
|
$abs_path = duplicator_get_abs_path();
|
|
$this->FilterFilesAll[] = $abs_path . '/.htaccess';
|
|
$this->FilterFilesAll[] = $abs_path . '/web.config';
|
|
$this->FilterFilesAll[] = $abs_path . '/wp-config.php';
|
|
$this->tmpFilterDirsAll = $this->FilterDirsAll;
|
|
//PHP 5 on windows decode patch
|
|
if (!DUP_Util::$PHP7_plus && DUP_Util::isWindows()) {
|
|
foreach ($this->tmpFilterDirsAll as $key => $value) {
|
|
if (preg_match('/[^\x20-\x7f]/', $value)) {
|
|
$this->tmpFilterDirsAll[$key] = utf8_decode($value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Recursive function to get all directories in a wp install
|
|
*
|
|
* @notes:
|
|
* Older PHP logic which is more stable on older version of PHP
|
|
* NOTE RecursiveIteratorIterator is problematic on some systems issues include:
|
|
* - error 'too many files open' for recursion
|
|
* - $file->getExtension() is not reliable as it silently fails at least in php 5.2.17
|
|
* - issues with when a file has a permission such as 705 and trying to get info (had to fallback to path-info)
|
|
* - basic conclusion wait on the SPL libs until after php 5.4 is a requirments
|
|
* - tight recursive loop use caution for speed
|
|
*
|
|
* @return array Returns an array of directories to include in the archive
|
|
*/
|
|
private function getFileLists($path, $relativePath)
|
|
{
|
|
if (($handle = opendir((strlen($path) === 0 ? '/' : $path))) === false) {
|
|
DUP_Log::trace('Can\'t open dir: ' . $path);
|
|
return false;
|
|
}
|
|
|
|
$result = array(
|
|
'size' => 0,
|
|
'nodes' => 1
|
|
);
|
|
$trimmedRelativePath = ltrim($relativePath . '/', '/');
|
|
while (($currentName = readdir($handle)) !== false) {
|
|
if ($currentName == '.' || $currentName == '..') {
|
|
continue;
|
|
}
|
|
$currentPath = $path . '/' . $currentName;
|
|
|
|
if (is_dir($currentPath)) {
|
|
//Don't clutter the log with these
|
|
//DUP_Log::trace(' Scan dir: ' . $currentPath);
|
|
$add = true;
|
|
if (is_link($currentPath)) {
|
|
//Get real path of link
|
|
//trailing slash is essential!
|
|
$realPath = SnapIO::safePathTrailingslashit($currentPath, true);
|
|
//if $currentPath starts with $realPath and is link
|
|
//=> $currentPath is located in $realPath and points back to it
|
|
if (strpos($currentPath, $realPath) === 0) {
|
|
$this->RecursiveLinks[] = $currentPath;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (in_array($currentName, $this->relativeFiltersDir)) {
|
|
$add = false;
|
|
} else {
|
|
foreach ($this->tmpFilterDirsAll as $filteredDir) {
|
|
if (SnapIO::isChildPath($currentPath, $filteredDir)) {
|
|
$add = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($add) {
|
|
$childResult = $this->getFileLists($currentPath, $trimmedRelativePath . $currentName);
|
|
$result['size'] += $childResult['size'];
|
|
$result['nodes'] += $childResult['nodes'];
|
|
$this->addToDirList($currentPath, $trimmedRelativePath . $currentName, $childResult['size'], $childResult['nodes']);
|
|
}
|
|
} else {
|
|
// Note: The last clause is present to perform just a filename check
|
|
if (
|
|
!(in_array(pathinfo($currentName, PATHINFO_EXTENSION), $this->FilterExtsAll) ||
|
|
in_array($currentPath, $this->FilterFilesAll) ||
|
|
in_array($currentName, $this->FilterFilesAll))
|
|
) {
|
|
$fileSize = (int) @filesize($currentPath);
|
|
$result['size'] += $fileSize;
|
|
$result['nodes'] += 1;
|
|
$this->addToFileList($currentPath, $trimmedRelativePath . $currentName, $fileSize);
|
|
}
|
|
}
|
|
}
|
|
closedir($handle);
|
|
return $result;
|
|
}
|
|
|
|
public static function isValidEncoding($string)
|
|
{
|
|
return !preg_match('/([\/\*\?><\:\\\\\|]|[^\x20-\x7f])/', $string);
|
|
}
|
|
|
|
private function addToDirList($dirPath, $relativePath, $size, $nodes)
|
|
{
|
|
//Dir is not readble remove and flag
|
|
if (!SnapOS::isWindows() && !is_readable($dirPath)) {
|
|
$this->FilterInfo->Dirs->Unreadable[] = $dirPath;
|
|
return;
|
|
}
|
|
|
|
// is relative path is empty is the root path
|
|
if (strlen($relativePath) && !DUP_Settings::Get('skip_archive_scan')) {
|
|
$name = basename($dirPath);
|
|
// Locate invalid directories and warn
|
|
$invalid_encoding = !self::isValidEncoding($name);
|
|
if ($invalid_encoding) {
|
|
$dirPath = DUP_Encoding::toUTF8($dirPath);
|
|
}
|
|
$trimmedName = trim($name);
|
|
$invalid_name = (
|
|
$invalid_encoding ||
|
|
(defined('PHP_MAXPATHLEN') && strlen($dirPath) > PHP_MAXPATHLEN) ||
|
|
strlen($trimmedName) === 0 ||
|
|
$name[strlen($name) - 1] === '.'
|
|
);
|
|
if ($invalid_name) {
|
|
$this->FilterInfo->Dirs->Warning[] = $dirPath;
|
|
}
|
|
|
|
if ($size > DUPLICATOR_SCAN_WARN_DIR_SIZE) {
|
|
$dirData = array(
|
|
'ubytes' => $size,
|
|
'bytes' => DUP_Util::byteSize($size, 0),
|
|
'nodes' => $nodes,
|
|
'name' => $name,
|
|
'dir' => pathinfo($dirPath, PATHINFO_DIRNAME),
|
|
'path' => $dirPath
|
|
);
|
|
$this->FilterInfo->Dirs->Size[] = $dirData;
|
|
DUP_Log::traceObject('ADD DIR SIZE:', $dirData);
|
|
}
|
|
|
|
//Check for other WordPress installs
|
|
if (!self::isCurrentWordpressInstallPath($dirPath) && SnapWP::isWpHomeFolder($dirPath)) {
|
|
$this->FilterInfo->Dirs->AddonSites[] = $dirPath;
|
|
}
|
|
}
|
|
|
|
$this->listDirObj->addEntry($relativePath, $size, $nodes);
|
|
$this->Dirs[] = $dirPath;
|
|
$this->dirsCount++;
|
|
}
|
|
|
|
private function addToFileList($filePath, $relativePath, $fileSize)
|
|
{
|
|
if (!is_readable($filePath)) {
|
|
$this->FilterInfo->Files->Unreadable[] = $filePath;
|
|
return;
|
|
}
|
|
|
|
if (!DUP_Settings::Get('skip_archive_scan')) {
|
|
$fileName = basename($filePath);
|
|
//File Warnings
|
|
$invalid_encoding = !self::isValidEncoding($fileName);
|
|
$trimmed_name = trim($fileName);
|
|
$invalid_name = $invalid_encoding || (defined('PHP_MAXPATHLEN') && strlen($filePath) > PHP_MAXPATHLEN) || strlen($trimmed_name) === 0;
|
|
if ($invalid_encoding) {
|
|
$filePath = DUP_Encoding::toUTF8($filePath);
|
|
$fileName = DUP_Encoding::toUTF8($fileName);
|
|
}
|
|
|
|
if ($invalid_name) {
|
|
$this->FilterInfo->Files->Warning[] = array(
|
|
'name' => $fileName,
|
|
'dir' => pathinfo($filePath, PATHINFO_DIRNAME),
|
|
'path' => $filePath
|
|
);
|
|
}
|
|
|
|
if ($fileSize > DUPLICATOR_SCAN_WARNFILESIZE) {
|
|
$this->FilterInfo->Files->Size[] = array(
|
|
'ubytes' => $fileSize,
|
|
'bytes' => DUP_Util::byteSize($fileSize, 0),
|
|
'nodes' => 1,
|
|
'name' => $fileName,
|
|
'dir' => pathinfo($filePath, PATHINFO_DIRNAME),
|
|
'path' => $filePath
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
$this->Size += $fileSize;
|
|
$this->listFileObj->addEntry($relativePath, $fileSize, 1);
|
|
$this->Files[] = $filePath;
|
|
$this->filesCount++;
|
|
}
|
|
|
|
/**
|
|
* Builds a tree for both file size warnings and name check warnings
|
|
* The trees are used to apply filters from the scan screen
|
|
*
|
|
* @return null
|
|
*/
|
|
private function setTreeFilters()
|
|
{
|
|
//-------------------------
|
|
//SIZE TREE
|
|
//BUILD: File Size tree
|
|
$dir_group = DUP_Util::array_group_by($this->FilterInfo->Files->Size, "dir");
|
|
ksort($dir_group);
|
|
foreach ($dir_group as $dir => $files) {
|
|
$sum = 0;
|
|
foreach ($files as $key => $value) {
|
|
$sum += $value['ubytes'];
|
|
}
|
|
|
|
//Locate core paths, wp-admin, wp-includes, etc.
|
|
$iscore = 0;
|
|
foreach ($this->wpCorePaths as $core_dir) {
|
|
if (strpos(DUP_Util::safePath($dir), DUP_Util::safePath($core_dir)) !== false) {
|
|
$iscore = 1;
|
|
break;
|
|
}
|
|
}
|
|
// Check root and content exact dir
|
|
if (!$iscore) {
|
|
if (in_array($dir, $this->wpCoreExactPaths)) {
|
|
$iscore = 1;
|
|
}
|
|
}
|
|
|
|
$this->FilterInfo->TreeSize[] = array(
|
|
'size' => DUP_Util::byteSize($sum, 0),
|
|
'dir' => $dir,
|
|
'sdir' => str_replace(duplicator_get_abs_path(), '/', $dir),
|
|
'iscore' => $iscore,
|
|
'files' => $files
|
|
);
|
|
}
|
|
|
|
//-------------------------
|
|
//NAME TREE
|
|
//BUILD: Warning tree for file names
|
|
$dir_group = DUP_Util::array_group_by($this->FilterInfo->Files->Warning, "dir");
|
|
ksort($dir_group);
|
|
foreach ($dir_group as $dir => $files) {
|
|
//Locate core paths, wp-admin, wp-includes, etc.
|
|
$iscore = 0;
|
|
foreach ($this->wpCorePaths as $core_dir) {
|
|
if (strpos($dir, $core_dir) !== false) {
|
|
$iscore = 1;
|
|
break;
|
|
}
|
|
}
|
|
// Check root and content exact dir
|
|
if (!$iscore) {
|
|
if (in_array($dir, $this->wpCoreExactPaths)) {
|
|
$iscore = 1;
|
|
}
|
|
}
|
|
|
|
$this->FilterInfo->TreeWarning[] = array(
|
|
'dir' => $dir,
|
|
'sdir' => str_replace(duplicator_get_abs_path(), '/', $dir),
|
|
'iscore' => $iscore,
|
|
'count' => count($files),
|
|
'files' => $files);
|
|
}
|
|
|
|
//BUILD: Warning tree for dir names
|
|
foreach ($this->FilterInfo->Dirs->Warning as $dir) {
|
|
$add_dir = true;
|
|
foreach ($this->FilterInfo->TreeWarning as $key => $value) {
|
|
if ($value['dir'] == $dir) {
|
|
$add_dir = false;
|
|
break;
|
|
}
|
|
}
|
|
if ($add_dir) {
|
|
//Locate core paths, wp-admin, wp-includes, etc.
|
|
$iscore = 0;
|
|
foreach ($this->wpCorePaths as $core_dir) {
|
|
if (strpos(DUP_Util::safePath($dir), DUP_Util::safePath($core_dir)) !== false) {
|
|
$iscore = 1;
|
|
break;
|
|
}
|
|
}
|
|
// Check root and content exact dir
|
|
if (!$iscore) {
|
|
if (in_array($dir, $this->wpCoreExactPaths)) {
|
|
$iscore = 1;
|
|
}
|
|
}
|
|
|
|
$this->FilterInfo->TreeWarning[] = array(
|
|
'dir' => $dir,
|
|
'sdir' => str_replace(duplicator_get_abs_path(), '/', $dir),
|
|
'iscore' => $iscore,
|
|
'count' => 0);
|
|
}
|
|
}
|
|
|
|
function _sortDir($a, $b)
|
|
{
|
|
return strcmp($a["dir"], $b["dir"]);
|
|
}
|
|
usort($this->FilterInfo->TreeWarning, "_sortDir");
|
|
}
|
|
|
|
public function getWPConfigFilePath()
|
|
{
|
|
$wpconfig_filepath = '';
|
|
$abs_path = duplicator_get_abs_path();
|
|
if (file_exists($abs_path . '/wp-config.php')) {
|
|
$wpconfig_filepath = $abs_path . '/wp-config.php';
|
|
} elseif (@file_exists(dirname($abs_path) . '/wp-config.php') && !@file_exists(dirname($abs_path) . '/wp-settings.php')) {
|
|
$wpconfig_filepath = dirname($abs_path) . '/wp-config.php';
|
|
}
|
|
return $wpconfig_filepath;
|
|
}
|
|
|
|
/**
|
|
* get the main target root path to make archive
|
|
*
|
|
* @staticvar type $targerRoorPath
|
|
* @return string
|
|
*/
|
|
public static function getTargetRootPath()
|
|
{
|
|
static $targetRoorPath = null;
|
|
if (is_null($targetRoorPath)) {
|
|
$paths = self::getArchiveListPaths();
|
|
unset($paths['wpconfig']);
|
|
$targetRoorPath = SnapIO::getCommonPath($paths);
|
|
}
|
|
return $targetRoorPath;
|
|
}
|
|
|
|
/**
|
|
* @param null|string $urlKey if set will only return the url identified by that key
|
|
*
|
|
* @return array|string|bool
|
|
*/
|
|
public static function getOriginalUrls($urlKey = null)
|
|
{
|
|
static $origUrls = null;
|
|
if (is_null($origUrls)) {
|
|
$restoreMultisite = false;
|
|
if (is_multisite() && SnapWP::getMainSiteId() !== get_current_blog_id()) {
|
|
$restoreMultisite = true;
|
|
restore_current_blog();
|
|
switch_to_blog(SnapWP::getMainSiteId());
|
|
}
|
|
|
|
$updDirs = wp_upload_dir(null, false, true);
|
|
if (($wpConfigDir = SnapWP::getWPConfigPath()) !== false) {
|
|
$wpConfigDir = dirname($wpConfigDir);
|
|
}
|
|
|
|
$homeUrl = home_url();
|
|
$homeParse = SnapURL::parseUrl(home_url());
|
|
$absParse = SnapURL::parseUrl(site_url());
|
|
if ($homeParse['host'] === $absParse['host'] && SnapIO::isChildPath($homeParse['path'], $absParse['path'], false, false)) {
|
|
$homeParse['path'] = $absParse['path'];
|
|
$homeUrl = SnapURL::buildUrl($homeParse);
|
|
}
|
|
|
|
$origUrls = array(
|
|
'home' => $homeUrl,
|
|
'abs' => site_url(),
|
|
'login' => wp_login_url(),
|
|
'wpcontent' => content_url(),
|
|
'uploads' => $updDirs['baseurl'],
|
|
'plugins' => plugins_url(),
|
|
'muplugins' => WPMU_PLUGIN_URL,
|
|
'themes' => get_theme_root_uri()
|
|
);
|
|
if ($restoreMultisite) {
|
|
restore_current_blog();
|
|
}
|
|
}
|
|
|
|
if ($urlKey === null) {
|
|
return $origUrls;
|
|
}
|
|
|
|
if (isset($origUrls[$urlKey])) {
|
|
return $origUrls[$urlKey];
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* return all paths to scan
|
|
*
|
|
* @return string[]
|
|
*/
|
|
public static function getScanPaths()
|
|
{
|
|
static $scanPaths = null;
|
|
if (is_null($scanPaths)) {
|
|
$paths = self::getArchiveListPaths();
|
|
// The folder that contains wp-config must not be scanned in full but only added
|
|
unset($paths['wpconfig']);
|
|
$scanPaths = array(
|
|
$paths['home']
|
|
);
|
|
unset($paths['home']);
|
|
foreach ($paths as $path) {
|
|
$addPath = true;
|
|
foreach ($scanPaths as $resPath) {
|
|
if (SnapIO::getRelativePath($path, $resPath) !== false) {
|
|
$addPath = false;
|
|
break;
|
|
}
|
|
}
|
|
if ($addPath) {
|
|
$scanPaths[] = $path;
|
|
}
|
|
}
|
|
$scanPaths = array_values(array_unique($scanPaths));
|
|
}
|
|
return $scanPaths;
|
|
}
|
|
|
|
/**
|
|
* return the wordpress original dir paths
|
|
*
|
|
* @param string|null $pathKey
|
|
*
|
|
* @return string[]|string|bool return false if key doesn\'t exist
|
|
*/
|
|
public static function getOriginalPaths($pathKey = null)
|
|
{
|
|
static $origPaths = null;
|
|
if (is_null($origPaths)) {
|
|
$restoreMultisite = false;
|
|
if (is_multisite() && SnapWP::getMainSiteId() !== get_current_blog_id()) {
|
|
$restoreMultisite = true;
|
|
restore_current_blog();
|
|
switch_to_blog(SnapWP::getMainSiteId());
|
|
}
|
|
|
|
$updDirs = wp_upload_dir(null, false, true);
|
|
// fix for old network installation
|
|
$baseDir = preg_replace('/^(.+\/blogs\.dir)\/\d+\/files$/', '$1', $updDirs['basedir']);
|
|
if (($wpConfigDir = SnapWP::getWPConfigPath()) !== false) {
|
|
$wpConfigDir = dirname($wpConfigDir);
|
|
}
|
|
$origPaths = array(
|
|
'home' => SnapWP::getHomePath(),
|
|
'abs' => ABSPATH,
|
|
'wpconfig' => $wpConfigDir,
|
|
'wpcontent' => WP_CONTENT_DIR,
|
|
'uploads' => $baseDir,
|
|
'plugins' => WP_PLUGIN_DIR,
|
|
'muplugins' => WPMU_PLUGIN_DIR,
|
|
'themes' => get_theme_root()
|
|
);
|
|
if ($restoreMultisite) {
|
|
restore_current_blog();
|
|
}
|
|
}
|
|
|
|
if (!empty($pathKey)) {
|
|
if (array_key_exists($pathKey, $origPaths)) {
|
|
return $origPaths[$pathKey];
|
|
} else {
|
|
return false;
|
|
}
|
|
} else {
|
|
return $origPaths;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* return the wordpress original dir paths
|
|
*
|
|
* @param string|null $pathKey
|
|
*
|
|
* @return string[]|string|bool return false if key doesn\'t exist
|
|
*/
|
|
public static function getArchiveListPaths($pathKey = null)
|
|
{
|
|
static $archivePaths = null;
|
|
if (is_null($archivePaths)) {
|
|
$archivePaths = array();
|
|
$originalPaths = self::getOriginalPaths();
|
|
|
|
$archivePaths = array(
|
|
'home' => SnapIO::safePathUntrailingslashit($originalPaths['home'], true)
|
|
);
|
|
unset($originalPaths['home']);
|
|
|
|
foreach ($originalPaths as $key => $originalPath) {
|
|
$path = SnapIO::safePathUntrailingslashit($originalPath, false);
|
|
$realPath = SnapIO::safePathUntrailingslashit($originalPath, true);
|
|
|
|
if ($path == $realPath) {
|
|
$archivePaths[$key] = $path;
|
|
} elseif (
|
|
!SnapIO::isChildPath($realPath, $archivePaths['home']) &&
|
|
SnapIO::isChildPath($path, $archivePaths['home'])
|
|
) {
|
|
$archivePaths[$key] = $path;
|
|
} else {
|
|
$archivePaths[$key] = $realPath;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!empty($pathKey)) {
|
|
if (array_key_exists($pathKey, $archivePaths)) {
|
|
return $archivePaths[$pathKey];
|
|
} else {
|
|
return false;
|
|
}
|
|
} else {
|
|
return $archivePaths;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* return true if path is child of duplicator backup path
|
|
*
|
|
* @param string $path
|
|
*
|
|
* @return boolean
|
|
*/
|
|
public static function isBackupPathChild($path)
|
|
{
|
|
return (preg_match('/[\/]' . preg_quote(DUP_Settings::getSsdirTmpPath(), '/') . '[\/][^\/]+$/', $path) === 1);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param string $path
|
|
*
|
|
* @return bool return true if path is a path of current wordpress installation
|
|
*/
|
|
public static function isCurrentWordpressInstallPath($path)
|
|
{
|
|
static $currentWpPaths = null;
|
|
|
|
if (is_null($currentWpPaths)) {
|
|
$currentWpPaths = array_merge(self::getOriginalPaths(), self::getArchiveListPaths());
|
|
$currentWpPaths = array_map('trailingslashit', $currentWpPaths);
|
|
$currentWpPaths = array_values(array_unique($currentWpPaths));
|
|
}
|
|
return in_array(trailingslashit($path), $currentWpPaths);
|
|
}
|
|
|
|
public function wpContentDirNormalizePath()
|
|
{
|
|
if (!isset($this->wpContentDirNormalizePath)) {
|
|
$this->wpContentDirNormalizePath = trailingslashit(wp_normalize_path(WP_CONTENT_DIR));
|
|
}
|
|
return $this->wpContentDirNormalizePath;
|
|
}
|
|
|
|
public function getUrl()
|
|
{
|
|
return DUP_Settings::getSsdirUrl() . "/" . $this->File;
|
|
}
|
|
|
|
public function getLocalDirPath($dir, $basePath = '')
|
|
{
|
|
$safeDir = SnapIO::safePathUntrailingslashit($dir);
|
|
return ltrim($basePath . preg_replace('/^' . preg_quote($this->PackDir, '/') . '(.*)/m', '$1', $safeDir), '/');
|
|
}
|
|
|
|
public function getLocalFilePath($file, $basePath = '')
|
|
{
|
|
$safeFile = SnapIO::safePathUntrailingslashit($file);
|
|
return ltrim($basePath . preg_replace('/^' . preg_quote($this->PackDir, '/') . '(.*)/m', '$1', $safeFile), '/');
|
|
}
|
|
}
|