File: /home/locglobe/public_html/wp-content/plugins/duplicator/classes/package/class.pack.archive.php
<?php
if (!defined('DUPLICATOR_VERSION')) exit; // Exit if accessed directly
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 handleing 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
* @since 1.0.0
*
*/
class DUP_Archive
{
//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 $Files = array();
public $FilterInfo;
//PROTECTED
protected $Package;
private $tmpFilterDirsAll = array();
private $wpCorePaths = array();
/**
* Init this object
*/
public function __construct($package)
{
$this->Package = $package;
$this->FilterOn = false;
$this->ExportOnlyDB = false;
$this->FilterInfo = new DUP_Archive_Filter_Info();
$rootPath = DUP_Util::safePath(rtrim(DUPLICATOR_WPROOTPATH, '//'));
$this->wpCorePaths[] = "{$rootPath}/wp-admin";
$this->wpCorePaths[] = WP_CONTENT_DIR . "/uploads";
$this->wpCorePaths[] = WP_CONTENT_DIR . "/languages";
$this->wpCorePaths[] = WP_PLUGIN_DIR;
$this->wpCorePaths[] = get_theme_root();
$this->wpCorePaths[] = "{$rootPath}/wp-includes";
}
/**
* Builds the archive based on the archive type
*
* @param obj $package The package object that started this process
*
* @return null
*/
public function build($package)
{
try {
$this->Package = $package;
if (!isset($this->PackDir) && !is_dir($this->PackDir)) throw new Exception("The 'PackDir' property must be a valid diretory.");
if (!isset($this->File)) throw new Exception("A 'File' property must be set.");
$this->Package->setStatus(DUP_PackageStatus::ARCSTART);
switch ($this->Format) {
case 'TAR': break;
case 'TAR-GZIP': break;
default:
if (class_exists(ZipArchive)) {
$this->Format = 'ZIP';
DUP_Zip::create($this);
}
break;
}
$storePath = "{$this->Package->StorePath}/{$this->File}";
$this->Size = @filesize($storePath);
$this->Package->setStatus(DUP_PackageStatus::ARCDONE);
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
}
}
/**
* 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 diretories
* @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->createFilterInfo();
$rootPath = DUP_Util::safePath(rtrim(DUPLICATOR_WPROOTPATH, '//'));
$rootPath = (trim($rootPath) == '') ? '/' : $rootPath;
//If the root directory is a filter then skip it all
if (in_array($this->PackDir, $this->FilterDirsAll) || $this->Package->Archive->ExportOnlyDB) {
$this->Dirs = array();
} else {
$this->Dirs[] = $this->PackDir;
$this->getFileLists($rootPath);
$this->setDirFilters();
$this->setFileFilters();
$this->setTreeFilters();
}
$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/;
*
* @returns string A cleaned up list of directory filters
*/
public function parseDirectoryFilter($dirs = "")
{
$dirs = str_replace(array("\n", "\t", "\r"), '', $dirs);
$filters = "";
$dir_array = array_unique(explode(";", $dirs));
$clean_array = array();
foreach ($dir_array as $val) {
if (strlen($val) >= 2) {
$clean_array[] = DUP_Util::safePath(trim(rtrim($val, "/\\"))) ;
}
}
if (count($clean_array)) {
$clean_array = array_unique($clean_array);
sort($clean_array);
$filters = implode(';', $clean_array) . ';';
}
return $filters ;
}
/**
* Properly creates the file filter list that is used for filtering files
*
* @param string $dirs A semi-colon list of dir paths
* /path1_/path/file1.ext;/path1_/path2/file2.ext;
*
* @returns string A cleaned up list of file filters
*/
public function parseFileFilter($files = "")
{
$files = str_replace(array("\n", "\t", "\r"), '', $files);
$filters = "";
$file_array = array_unique(explode(";", $files));
$clean_array = array();
foreach ($file_array as $val) {
if (strlen($val) >= 2) {
$clean_array[] = DUP_Util::safePath(trim(rtrim($val, "/\\"))) ;
}
}
if (count($clean_array)) {
$clean_array = array_unique($clean_array);
sort($clean_array);
$filters = implode(';', $clean_array) . ';';
}
return $filters ;
}
/**
* 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 = array_map('DUP_Util::safePath', explode(";", $this->FilterDirs, -1));
$this->FilterInfo->Files->Instance = array_map('DUP_Util::safePath', explode(";", $this->FilterFiles, -1));
$this->FilterInfo->Exts->Instance = explode(";", $this->FilterExts, -1);
}
//FILTER: CORE ITMES
//Filters Duplicator free packages & All pro local directories
$wp_root = rtrim(DUPLICATOR_WPROOTPATH, '/');
$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
$wp_root . '/wp-snapshots',
//WP-CONTENT
$wp_content . '/backups-dup-pro',
$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-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'
);
$this->FilterDirsAll = array_merge($this->FilterInfo->Dirs->Instance, $this->FilterInfo->Dirs->Core);
$this->FilterExtsAll = array_merge($this->FilterInfo->Exts->Instance, $this->FilterInfo->Exts->Core);
$this->FilterFilesAll = array_merge($this->FilterInfo->Files->Instance);
$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);
}
}
}
}
/**
* Get All Directories then filter
*
* @return null
*/
private function setDirFilters()
{
$this->FilterInfo->Dirs->Warning = array();
$this->FilterInfo->Dirs->Unreadable = array();
$utf8_key_list = array();
$unset_key_list = array();
//Filter directories invalid test checks for:
// - characters over 250
// - invlaid characters
// - empty string
// - directories ending with period (Windows incompatable)
foreach ($this->Dirs as $key => $val) {
$name = basename($val);
//Dir is not readble remove flag for removal
if (! is_readable($this->Dirs[$key])) {
$unset_key_list[] = $key;
$this->FilterInfo->Dirs->Unreadable[] = DUP_Encoding::toUTF8($val);
}
//Locate invalid directories and warn
$invalid_test = strlen($val) > 244
|| preg_match('/(\/|\*|\?|\>|\<|\:|\\|\|)/', $name)
|| trim($name) == ''
|| (strrpos($name, '.') == strlen($name) - 1 && substr($name, -1) == '.')
|| preg_match('/[^\x20-\x7f]/', $name);
if ($invalid_test) {
$utf8_key_list[] = $key;
$this->FilterInfo->Dirs->Warning[] = DUP_Encoding::toUTF8($val);
}
}
//Try to repair utf8 paths
foreach ($utf8_key_list as $key) {
$this->Dirs[$key] = DUP_Encoding::toUTF8($this->Dirs[$key]);
}
//Remove unreadable items outside of main loop for performance
if (count($unset_key_list)) {
foreach ($unset_key_list as $key) {
unset($this->Dirs[$key]);
}
$this->Dirs = array_values($this->Dirs);
}
}
/**
* Get all files and filter out error prone subsets
*
* @return null
*/
private function setFileFilters()
{
//Init for each call to prevent concatination from stored entity objects
$this->Size = 0;
$this->FilterInfo->Files->Size = array();
$this->FilterInfo->Files->Warning = array();
$this->FilterInfo->Files->Unreadable = array();
$utf8_key_list = array();
$unset_key_list = array();
foreach ($this->Files as $key => $filePath) {
$fileName = basename($filePath);
if (! is_readable($filePath)) {
$unset_key_list[] = $key;
$this->FilterInfo->Files->Unreadable[] = $filePath;
continue;
}
$invalid_test = strlen($filePath) > 250
|| preg_match('/(\/|\*|\?|\>|\<|\:|\\|\|)/', $fileName)
|| trim($fileName) == ""
|| preg_match('/[^\x20-\x7f]/', $fileName);
if ($invalid_test) {
$utf8_key_list[] = $key;
$filePath = DUP_Encoding::toUTF8($filePath);
$fileName = basename($filePath);
$this->FilterInfo->Files->Warning[] = array(
'name' => $fileName,
'dir' => pathinfo($filePath, PATHINFO_DIRNAME),
'path' => $filePath);
}
$fileSize = @filesize($filePath);
$fileSize = empty($fileSize) ? 0 : $fileSize;
$this->Size += $fileSize;
if ($fileSize > DUPLICATOR_SCAN_WARNFILESIZE) {
$ext = pathinfo($filePath, PATHINFO_EXTENSION);
$this->FilterInfo->Files->Size[] = array(
'ubytes' => $fileSize,
'bytes' => DUP_Util::byteSize($fileSize, 0),
'name' => $fileName,
'dir' => pathinfo($filePath, PATHINFO_DIRNAME),
'path' => $filePath);
}
}
//Try to repair utf8 paths
foreach ($utf8_key_list as $key) {
$this->Files[$key] = DUP_Encoding::toUTF8($this->Files[$key]);
}
//Remove unreadable items outside of main loop for performance
if (count($unset_key_list)) {
foreach ($unset_key_list as $key) {
unset($this->Files[$key]);
}
$this->Files = array_values($this->Files);
}
}
/**
* 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 pathinfo)
* - basic conclusion wait on the SPL libs untill after php 5.4 is a requiremnt
* - tight recursive loop use caution for speed
*
* @return array Returns an array of directories to include in the archive
*/
private function getFileLists($path)
{
$handle = @opendir($path);
if ($handle) {
while (($file = readdir($handle)) !== false) {
if ($file == '.' || $file == '..') {
continue;
}
$fullPath = str_replace("\\", '/', "{$path}/{$file}");
// @todo: Don't leave it like this. Convert into an option on the package to not follow symbolic links
// if (is_dir($fullPath) && (is_link($fullPath) == false))
if (is_dir($fullPath)) {
$add = true;
//Directory filters
foreach ($this->tmpFilterDirsAll as $key => $val) {
$trimmedFilterDir = rtrim($val, '/');
if ($fullPath == $trimmedFilterDir || strpos($fullPath, $trimmedFilterDir . '/') !== false) {
$add = false;
unset($this->tmpFilterDirsAll[$key]);
break;
}
}
if ($add) {
$this->getFileLists($fullPath);
$this->Dirs[] = $fullPath;
}
} else {
if ( ! (in_array(pathinfo($file, PATHINFO_EXTENSION), $this->FilterExtsAll)
|| in_array($fullPath, $this->FilterFilesAll))) {
$this->Files[] = $fullPath;
}
}
}
closedir($handle);
}
return $this->Dirs;
}
/**
* 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;
}
}
$this->FilterInfo->TreeSize[] = array(
'size' => DUP_Util::byteSize($sum, 0),
'dir' => $dir,
'sdir' => str_replace(DUPLICATOR_WPROOTPATH, '', $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;
}
}
$this->FilterInfo->TreeWarning[] = array(
'dir' => $dir,
'sdir' => str_replace(DUPLICATOR_WPROOTPATH, '', $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;
}
}
$this->FilterInfo->TreeWarning[] = array(
'dir' => $dir,
'sdir' => str_replace(DUPLICATOR_WPROOTPATH, '', $dir),
'iscore' => $iscore,
'count' => 0);
}
}
function _sortDir($a, $b){
return strcmp($a["dir"], $b["dir"]);
}
usort($this->FilterInfo->TreeWarning, "_sortDir");
}
}