Download all files FTP
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
defined("ABSPATH") or die("");
|
||||
/**
|
||||
* Utility class working with date time values
|
||||
*
|
||||
* Standard: PSR-2
|
||||
*
|
||||
* @link http://www.php-fig.org/psr/psr-2
|
||||
*
|
||||
* @package DUP_PRO
|
||||
* @subpackage classes/utilities
|
||||
* @copyright (c) 2017, Snapcreek LLC
|
||||
* @license https://opensource.org/licenses/GPL-3.0 GNU Public License
|
||||
* @since 3.0.0
|
||||
*
|
||||
* @todo Finish Docs
|
||||
*/
|
||||
class DUP_PRO_DATE
|
||||
{
|
||||
/**
|
||||
* Get local time from GMT
|
||||
*
|
||||
* @param int $timestamp timestamp
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getLocalTimeFromGMT($timestamp)
|
||||
{
|
||||
$local_ticks = self::getLocalTicksFromGMT($timestamp);
|
||||
$date_portion = date('M j,', $local_ticks);
|
||||
$time_portion = date('g:i:s a', $local_ticks);
|
||||
return "$date_portion $time_portion";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get local ticks from GMT
|
||||
*
|
||||
* @param int $timestamp timestamp
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function getLocalTicksFromGMT($timestamp)
|
||||
{
|
||||
return $timestamp + \Duplicator\Libs\Snap\SnapWP::getGMTOffset();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get local time from GMT ticks
|
||||
*
|
||||
* @param int $ticks timestamp
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getLocalTimeFromGMTTicks($ticks)
|
||||
{
|
||||
return self::getStandardTime($ticks + \Duplicator\Libs\Snap\SnapWP::getGMTOffset());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current GMT time in ticks
|
||||
*
|
||||
* @param int $ticks timestamp
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getStandardTime($ticks)
|
||||
{
|
||||
//return date('D, d M Y H:i:s', $ticks);
|
||||
return date('D, d M H:i:s', $ticks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of the GMT time in the format of the WP date and time settings
|
||||
*
|
||||
* @param int $timestamp The GMT timestamp
|
||||
* @param bool $includeDate Whether to include the date portion
|
||||
* @param bool $includeTime Whether to include the time portion
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getWPTimeFromGMTTime($timestamp, $includeDate = true, $includeTime = true)
|
||||
{
|
||||
$ticks = self::getLocalTicksFromGMT($timestamp);
|
||||
$date_format = get_option('date_format');
|
||||
$time_format = get_option('time_format');
|
||||
if ($includeDate) {
|
||||
$date_portion = date($date_format, $ticks);
|
||||
} else {
|
||||
$date_portion = '';
|
||||
}
|
||||
|
||||
if ($includeTime) {
|
||||
$time_portion = date($time_format, $ticks);
|
||||
} else {
|
||||
$time_portion = '';
|
||||
}
|
||||
|
||||
if ($includeDate && $includeTime) {
|
||||
$seperator = ' ';
|
||||
} else {
|
||||
$seperator = '';
|
||||
}
|
||||
|
||||
return "$date_portion$seperator$time_portion";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
<?php
|
||||
|
||||
defined("ABSPATH") or die("");
|
||||
|
||||
use Duplicator\Installer\Package\DescriptorSubsite;
|
||||
use Duplicator\Libs\Snap\SnapDB;
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
use Duplicator\Libs\Snap\SnapURL;
|
||||
use Duplicator\Libs\Snap\SnapWP;
|
||||
|
||||
class DUP_PRO_MU_Generations
|
||||
{
|
||||
const NotMultisite = 0;
|
||||
const PreThreeFive = 1;
|
||||
const ThreeFivePlus = 2;
|
||||
}
|
||||
|
||||
class DUP_PRO_MU
|
||||
{
|
||||
/**
|
||||
* return multisite mode
|
||||
* 0 = single site
|
||||
* 1 = multisite subdomain
|
||||
* 2 = multisite subdirectory
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function getMode()
|
||||
{
|
||||
|
||||
if (is_multisite()) {
|
||||
if (defined('SUBDOMAIN_INSTALL') && SUBDOMAIN_INSTALL) {
|
||||
return 1;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is wrong because it assumes that if the folder sites exist, blogs.dir cannot exist.
|
||||
* This is not true because if the network is old but a new site is created after the WordPress update both blogs.dir and sites folders exist.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function getGeneration()
|
||||
{
|
||||
if (self::getMode() == 0) {
|
||||
return DUP_PRO_MU_Generations::NotMultisite;
|
||||
} else {
|
||||
$sitesDir = WP_CONTENT_DIR . '/uploads/sites';
|
||||
|
||||
if (file_exists($sitesDir)) {
|
||||
return DUP_PRO_MU_Generations::ThreeFivePlus;
|
||||
} else {
|
||||
return DUP_PRO_MU_Generations::PreThreeFive;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the subsite info list
|
||||
*
|
||||
* @param int[] $filteredSites List of sites to filter
|
||||
* @param string[] $filteredTables List of tables to filter
|
||||
* @param string[] $filteredPaths List of paths to filter
|
||||
*
|
||||
* @return DescriptorSubsite[]
|
||||
*/
|
||||
public static function getSubsites($filteredSites = array(), $filteredTables = array(), $filteredPaths = array())
|
||||
{
|
||||
if (!is_multisite()) {
|
||||
return array(self::getSubsiteInfo(1, $filteredTables, $filteredPaths));
|
||||
}
|
||||
|
||||
$site_array = array();
|
||||
$filteredSites = is_array($filteredSites) ? $filteredSites : array();
|
||||
|
||||
DUP_PRO_Log::trace("NETWORK SITES");
|
||||
|
||||
foreach (SnapWP::getSitesIds() as $siteId) {
|
||||
if (in_array($siteId, $filteredSites)) {
|
||||
continue;
|
||||
}
|
||||
if (($siteInfo = self::getSubsiteInfo($siteId, $filteredTables, $filteredPaths)) == false) {
|
||||
continue;
|
||||
}
|
||||
array_push($site_array, $siteInfo);
|
||||
DUP_PRO_Log::trace("Multisite subsite detected. ID={$siteInfo->id} Domain={$siteInfo->domain} Path={$siteInfo->path} Blogname={$siteInfo->blogname}");
|
||||
}
|
||||
|
||||
return $site_array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the subsite info by id
|
||||
*
|
||||
* @param int $subsiteId subsite id
|
||||
*
|
||||
* @return false|DescriptorSubsite false on failure
|
||||
*/
|
||||
public static function getSubsiteInfoById($subsiteId)
|
||||
{
|
||||
if (!is_multisite()) {
|
||||
$subsiteId = 1;
|
||||
}
|
||||
return self::getSubsiteInfo($subsiteId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get subsite info
|
||||
*
|
||||
* @param int $siteId subsite id
|
||||
* @param string[] $filteredTables Filtered tables
|
||||
* @param false|string[] $filteredPaths Filtered paths
|
||||
*
|
||||
* @return false|DescriptorSubsite false on failure
|
||||
*/
|
||||
public static function getSubsiteInfo($siteId = 1, $filteredTables = array(), $filteredPaths = array())
|
||||
{
|
||||
if (is_multisite()) {
|
||||
if (($siteDetails = get_blog_details($siteId)) == false) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$siteId = 1;
|
||||
$siteDetails = new stdClass();
|
||||
$home = DUP_PRO_Archive::getOriginalUrls('home');
|
||||
$parsedHome = SnapURL::parseUrl($home);
|
||||
$siteDetails->domain = $parsedHome['host'];
|
||||
$siteDetails->path = trailingslashit($parsedHome['path']);
|
||||
$siteDetails->blogname = sanitize_text_field(get_option('blogname'));
|
||||
}
|
||||
|
||||
$subsiteID = $siteId;
|
||||
$siteInfo = new DescriptorSubsite();
|
||||
$siteInfo->id = $subsiteID;
|
||||
$siteInfo->domain = $siteDetails->domain;
|
||||
$siteInfo->path = $siteDetails->path;
|
||||
$siteInfo->blogname = $siteDetails->blogname;
|
||||
$siteInfo->blog_prefix = $GLOBALS['wpdb']->get_blog_prefix($subsiteID);
|
||||
if (count($filteredTables) > 0) {
|
||||
$siteInfo->filteredTables = array_values(array_intersect(self::getSubsiteTables($subsiteID), $filteredTables));
|
||||
} else {
|
||||
$siteInfo->filteredTables = array();
|
||||
}
|
||||
$siteInfo->adminUsers = SnapWP::getAdminUserLists($siteInfo->id);
|
||||
$siteInfo->fullHomeUrl = get_home_url($siteId);
|
||||
$siteInfo->fullSiteUrl = get_site_url($siteId);
|
||||
|
||||
if ($siteId > 1) {
|
||||
switch_to_blog($siteId);
|
||||
}
|
||||
|
||||
$uploadData = wp_upload_dir();
|
||||
$uploadPath = $uploadData['basedir'];
|
||||
$siteInfo->uploadPath = SnapIO::getRelativePath($uploadPath, DUP_PRO_Archive::getTargetRootPath(), true);
|
||||
$siteInfo->fullUploadPath = untrailingslashit($uploadPath);
|
||||
$siteInfo->fullUploadSafePath = SnapIO::safePathUntrailingslashit($uploadPath);
|
||||
$siteInfo->fullUploadUrl = $uploadData['baseurl'];
|
||||
if (count($filteredPaths)) {
|
||||
$globalDirFilters = DUP_PRO_Archive::getDefaultGlobalDirFilter();
|
||||
$siteInfo->filteredPaths = array_values(array_filter($filteredPaths, function ($path) use ($uploadPath, $subsiteID, $globalDirFilters) {
|
||||
if (
|
||||
($relativeUpload = SnapIO::getRelativePath($path, $uploadPath)) === false ||
|
||||
in_array($path, $globalDirFilters)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($subsiteID > 1) {
|
||||
return true;
|
||||
} else {
|
||||
// no check on blogs.dir because in wp-content/blogs.dir not in upload folder
|
||||
return !(strpos($relativeUpload, 'sites') === 0);
|
||||
}
|
||||
}));
|
||||
} else {
|
||||
$siteInfo->filteredPaths = array();
|
||||
}
|
||||
|
||||
if ($siteId > 1) {
|
||||
restore_current_blog();
|
||||
}
|
||||
return $siteInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $subsiteID subsite id
|
||||
*
|
||||
* @return string[] List of tables belonging to subsite
|
||||
*/
|
||||
public static function getSubsiteTables($subsiteID)
|
||||
{
|
||||
/** @var \wpdb $wpdb */
|
||||
global $wpdb;
|
||||
|
||||
$basePrefix = $wpdb->base_prefix;
|
||||
$subsitePrefix = $wpdb->get_blog_prefix($subsiteID);
|
||||
|
||||
$sharedTables = array(
|
||||
$basePrefix . 'users',
|
||||
$basePrefix . 'usermeta',
|
||||
);
|
||||
$multisiteOnlyTables = array(
|
||||
$basePrefix . 'blogmeta',
|
||||
$basePrefix . 'blogs',
|
||||
$basePrefix . 'blog_versions',
|
||||
$basePrefix . 'registration_log',
|
||||
$basePrefix . 'signups',
|
||||
$basePrefix . 'site',
|
||||
$basePrefix . 'sitemeta',
|
||||
);
|
||||
|
||||
$subsiteTables = array();
|
||||
$sql = "";
|
||||
$dbnameSafe = esc_sql(DB_NAME);
|
||||
|
||||
if ($subsiteID != 1) {
|
||||
$regex = '^' . SnapDB::quoteRegex($subsitePrefix);
|
||||
$regexpSafe = esc_sql($regex);
|
||||
|
||||
$sharedTablesSafe = "'" . implode(
|
||||
"', '",
|
||||
esc_sql($sharedTables)
|
||||
) . "'";
|
||||
$sql = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '$dbnameSafe' AND ";
|
||||
$sql .= "(TABLE_NAME REGEXP '$regexpSafe' OR TABLE_NAME IN ($sharedTablesSafe))";
|
||||
} else {
|
||||
$regexMain = '^' . SnapDB::quoteRegex($basePrefix);
|
||||
$regexpMainSafe = esc_sql($regexMain);
|
||||
$regexNotSub = '^' . SnapDB::quoteRegex($basePrefix) . '[0-9]+_';
|
||||
$regexpNotSubSafe = esc_sql($regexNotSub);
|
||||
|
||||
$multisiteOnlyTablesSafe = "'" . implode(
|
||||
"', '",
|
||||
esc_sql($multisiteOnlyTables)
|
||||
) . "'";
|
||||
$sql = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '$dbnameSafe' AND ";
|
||||
$sql .= "TABLE_NAME REGEXP '$regexpMainSafe' AND ";
|
||||
$sql .= "TABLE_NAME NOT REGEXP '$regexpNotSubSafe' AND ";
|
||||
$sql .= "TABLE_NAME NOT IN ($multisiteOnlyTablesSafe)";
|
||||
}
|
||||
$subsiteTables = $wpdb->get_col($sql);
|
||||
return $subsiteTables;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,556 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Utility class used for various task
|
||||
*
|
||||
* Standard: PSR-2
|
||||
*
|
||||
* @link http://www.php-fig.org/psr/psr-2
|
||||
*
|
||||
* @package DUP_PRO
|
||||
* @subpackage classes/utilities
|
||||
* @copyright (c) 2017, Snapcreek LLC
|
||||
* @license https://opensource.org/licenses/GPL-3.0 GNU Public License
|
||||
*/
|
||||
|
||||
defined("ABSPATH") or die("");
|
||||
|
||||
use Duplicator\Libs\Shell\Shell;
|
||||
|
||||
class DUP_PRO_U
|
||||
{
|
||||
/**
|
||||
* return absolute path for the directories that are core directories
|
||||
*
|
||||
* @param bool $original If true it returns yes the original realpaths and paths, in case they are links, Otherwise it returns only the realpaths.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public static function getWPCoreDirs($original = false)
|
||||
{
|
||||
$corePaths = DUP_PRO_Archive::getArchiveListPaths();
|
||||
$corePaths[] = $corePaths['abs'] . '/wp-admin';
|
||||
$corePaths[] = $corePaths['abs'] . '/wp-includes';
|
||||
|
||||
if ($original) {
|
||||
$origPaths = DUP_PRO_Archive::getOriginalPaths();
|
||||
$origPaths[] = $origPaths['abs'] . '/wp-admin';
|
||||
$origPaths[] = $origPaths['abs'] . '/wp-includes';
|
||||
|
||||
$corePaths = array_merge($corePaths, $origPaths);
|
||||
}
|
||||
|
||||
return array_values(array_unique($corePaths));
|
||||
}
|
||||
|
||||
/**
|
||||
* return absolute path for the files that are core directories
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public static function getWPCoreFiles()
|
||||
{
|
||||
return array(DUP_PRO_Archive::getArchiveListPaths('wpconfig') . '/wp-config.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an absolute path to a relative path
|
||||
*
|
||||
* @param string $from The the path relative to $to
|
||||
* @param string $to The full path of the directory to transform
|
||||
*
|
||||
* @return string A string of the result
|
||||
*/
|
||||
public static function getRelativePath($from, $to)
|
||||
{
|
||||
// some compatibility fixes for Windows paths
|
||||
$from = is_dir($from) ? rtrim($from, '\/') . '/' : $from;
|
||||
$to = is_dir($to) ? rtrim($to, '\/') . '/' : $to;
|
||||
$from = str_replace('\\', '/', $from);
|
||||
$to = str_replace('\\', '/', $to);
|
||||
|
||||
$from = explode('/', $from);
|
||||
$to = explode('/', $to);
|
||||
$relPath = $to;
|
||||
|
||||
foreach ($from as $depth => $dir) {
|
||||
// find first non-matching dir
|
||||
if ($dir === $to[$depth]) {
|
||||
// ignore this directory
|
||||
array_shift($relPath);
|
||||
} else {
|
||||
// get number of remaining dirs to $from
|
||||
$remaining = count($from) - $depth;
|
||||
if ($remaining > 1) {
|
||||
// add traversals up to first matching dir
|
||||
$padLength = (count($relPath) + $remaining - 1) * -1;
|
||||
$relPath = array_pad($relPath, $padLength, '..');
|
||||
break;
|
||||
} else {
|
||||
//$relPath[0] = './' . $relPath[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
return implode('/', $relPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the percentage of one value to another
|
||||
* example:
|
||||
* $val1 = 100
|
||||
* $val2 = 400
|
||||
* $res = 25
|
||||
*
|
||||
* @param int|float $val1 The value to calculate the percentage
|
||||
* @param int|float $val2 The total value to calculate the percentage against
|
||||
* @param int $precision The number of decimal places to round to
|
||||
*
|
||||
* @return float Returns the results
|
||||
*/
|
||||
public static function percentage($val1, $val2, $precision = 0)
|
||||
{
|
||||
$division = $val1 / (float) $val2;
|
||||
$res = $division * 100;
|
||||
return round($res, $precision);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display human readable byte sizes
|
||||
*
|
||||
* @param int $size The size in bytes
|
||||
*
|
||||
* @return string The size of bytes readable such as 100KB, 20MB, 1GB etc.
|
||||
*/
|
||||
public static function byteSize($size)
|
||||
{
|
||||
try {
|
||||
$size = (int) $size;
|
||||
$units = array(
|
||||
'B',
|
||||
'KB',
|
||||
'MB',
|
||||
'GB',
|
||||
'TB',
|
||||
);
|
||||
for ($i = 0; $size >= 1024 && $i < 4; $i++) {
|
||||
$size /= 1024;
|
||||
}
|
||||
return round($size, 2) . $units[$i];
|
||||
} catch (Exception $e) {
|
||||
return "n/a";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a string with the elapsed time in seconds
|
||||
*
|
||||
* @see getMicrotime()
|
||||
*
|
||||
* @param int|float $end The final time in the sequence to measure
|
||||
* @param int|float $start The start time in the sequence to measure
|
||||
*
|
||||
* @return string The time elapsed from $start to $end as 5.89 sec.
|
||||
*/
|
||||
public static function elapsedTime($end, $start)
|
||||
{
|
||||
|
||||
return sprintf('%.3f sec.', abs($end - $start));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a float with the elapsed time in seconds
|
||||
*
|
||||
* @see getMicrotime(), elapsedTime()
|
||||
*
|
||||
* @param int|float $end The final time in the sequence to measure
|
||||
* @param int|float $start The start time in the sequence to measure
|
||||
*
|
||||
* @return string The time elapsed from $start to $end as 5.89
|
||||
*/
|
||||
public static function elapsedTimeU($end, $start)
|
||||
{
|
||||
return sprintf('%.3f', abs($end - $start));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the contents of the file as an attachment type
|
||||
*
|
||||
* @param string $filepath The full path the file to read
|
||||
* @param string $contentType The header content type to force when pushing the attachment
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function getDownloadAttachment($filepath, $contentType)
|
||||
{
|
||||
// Clean previous or after eny notice texts
|
||||
ob_clean();
|
||||
ob_start();
|
||||
$filename = basename($filepath);
|
||||
|
||||
header("Content-Type: {$contentType}");
|
||||
header("Content-Disposition: attachment; filename={$filename}");
|
||||
header("Pragma: public");
|
||||
|
||||
if (readfile($filepath) === false) {
|
||||
$msg = sprintf(__('Couldn\'t read %s', 'duplicator-pro'), $filepath);
|
||||
throw new Exception($msg);
|
||||
}
|
||||
ob_end_flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the path of an executable program
|
||||
*
|
||||
* @param string $exeFilename A file name or path to a file name of the executable
|
||||
*
|
||||
* @return string|null Returns the full path of the executable or null if not found
|
||||
*/
|
||||
public static function getExeFilepath($exeFilename)
|
||||
{
|
||||
$filepath = null;
|
||||
|
||||
if (!Shell::test()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$shellOutput = Shell::runCommand("hash $exeFilename 2>&1", Shell::AVAILABLE_COMMANDS);
|
||||
if ($shellOutput !== false && $shellOutput->isEmpty()) {
|
||||
$filepath = $exeFilename;
|
||||
} else {
|
||||
$possible_paths = array(
|
||||
"/usr/bin/$exeFilename",
|
||||
"/opt/local/bin/$exeFilename",
|
||||
);
|
||||
|
||||
foreach ($possible_paths as $path) {
|
||||
if (@file_exists($path)) {
|
||||
$filepath = $path;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $filepath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current microtime as a float. Method is used for simple profiling
|
||||
*
|
||||
* @see elapsedTime
|
||||
*
|
||||
* @return float A float in the form "msec sec", where sec is the number of seconds since the Unix epoch
|
||||
*/
|
||||
public static function getMicrotime()
|
||||
{
|
||||
return microtime(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an SQL lock request
|
||||
*
|
||||
* @see releaseSqlLock()
|
||||
*
|
||||
* @param string $lock_name The name of the lock to check
|
||||
*
|
||||
* @return bool Returns true if an SQL lock request was successful
|
||||
*/
|
||||
public static function getSqlLock($lock_name = 'duplicator_pro_lock')
|
||||
{
|
||||
global $wpdb;
|
||||
|
||||
$query_string = "select GET_LOCK('{$lock_name}', 0)";
|
||||
|
||||
$ret_val = $wpdb->get_var($query_string);
|
||||
|
||||
if ($ret_val == 0) {
|
||||
DUP_PRO_Log::trace("Mysql lock {$lock_name} denied");
|
||||
return false;
|
||||
} elseif ($ret_val == null) {
|
||||
DUP_PRO_Log::trace("Error retrieving mysql lock {$lock_name}");
|
||||
return false;
|
||||
}
|
||||
|
||||
DUP_PRO_Log::trace("Mysql lock {$lock_name} acquired");
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an SQL lock request
|
||||
*
|
||||
* @see releaseSqlLock()
|
||||
*
|
||||
* @param string $lock_name The name of the lock to check
|
||||
*
|
||||
* @return bool Returns true if an SQL lock request was successful
|
||||
*/
|
||||
public static function isSqlLockLocked($lock_name = 'duplicator_pro_lock')
|
||||
{
|
||||
global $wpdb;
|
||||
|
||||
$query_string = "select IS_FREE_LOCK('{$lock_name}')";
|
||||
|
||||
$ret_val = $wpdb->get_var($query_string);
|
||||
|
||||
if ($ret_val == 0) {
|
||||
DUP_PRO_Log::trace("MySQL lock {$lock_name} is in use");
|
||||
return true;
|
||||
} elseif ($ret_val == null) {
|
||||
DUP_PRO_Log::trace("Error retrieving mysql lock {$lock_name}");
|
||||
return false;
|
||||
} else {
|
||||
DUP_PRO_Log::trace("MySQL lock {$lock_name} is free");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that a correct security nonce was used. If correct nonce is not used, It will cause to die
|
||||
*
|
||||
* A nonce is valid for 24 hours (by default).
|
||||
*
|
||||
* @param string $nonce Nonce value that was used for verification, usually via a form field.
|
||||
* @param string|int $action Should give context to what is taking place and be the same when nonce was created.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function verifyNonce($nonce, $action)
|
||||
{
|
||||
if (!wp_verify_nonce($nonce, $action)) {
|
||||
die('Security issue');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the current user have the capability
|
||||
* Dies if user doesn't have the correct capability
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function checkAjax()
|
||||
{
|
||||
if (!wp_doing_ajax()) {
|
||||
$errorMsg = esc_html__('You do not have called from AJAX to access this page.', 'duplicator-pro');
|
||||
DUP_PRO_Log::trace($errorMsg);
|
||||
error_log($errorMsg);
|
||||
wp_die(esc_html($errorMsg));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rturn true if sql lock is set
|
||||
*
|
||||
* @param string $lock_name lock nam
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function checkSqlLock($lock_name = 'duplicator_pro_lock')
|
||||
{
|
||||
global $wpdb;
|
||||
|
||||
$query_string = "SELECT IS_USED_LOCK('{$lock_name}')";
|
||||
$ret_val = $wpdb->get_var($query_string);
|
||||
|
||||
return $ret_val > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases the SQL lock request
|
||||
*
|
||||
* @see getSqlLock()
|
||||
*
|
||||
* @param string $lock_name The name of the lock to release
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function releaseSqlLock($lock_name = 'duplicator_pro_lock')
|
||||
{
|
||||
global $wpdb;
|
||||
|
||||
$query_string = "select RELEASE_LOCK('{$lock_name}')";
|
||||
$ret_val = $wpdb->get_var($query_string);
|
||||
|
||||
if ($ret_val == 0) {
|
||||
DUP_PRO_Log::trace("Failed releasing sql lock {$lock_name} because it wasn't established by this thread");
|
||||
} elseif ($ret_val == null) {
|
||||
DUP_PRO_Log::trace("Tried to release sql lock {$lock_name} but it didn't exist");
|
||||
} else {
|
||||
// Lock was released
|
||||
DUP_PRO_Log::trace("SQL lock {$lock_name} released");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a value or returns a default
|
||||
*
|
||||
* @param mixed $val The value to set
|
||||
* @param mixed $default The value to default to if the val is not set
|
||||
*
|
||||
* @return mixed A value or a default
|
||||
*/
|
||||
public static function setVal($val, $default = null)
|
||||
{
|
||||
return isset($val) ? $val : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check is set and not empty, sets a value or returns a default
|
||||
*
|
||||
* @param mixed $val The value to set
|
||||
* @param mixed $default The value to default to if the val is not set
|
||||
*
|
||||
* @return mixed A value or a default
|
||||
*/
|
||||
public static function isEmpty($val, $default = null)
|
||||
{
|
||||
return isset($val) && !empty($val) ? $val : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last N lines of a file. Simular to tail command
|
||||
*
|
||||
* @param string $filepath The full path to the file to be tailed
|
||||
* @param int $lines The number of lines to return with each tail call
|
||||
*
|
||||
* @return false|string The last N parts of the file, false on failure
|
||||
*/
|
||||
public static function tailFile($filepath, $lines = 2)
|
||||
{
|
||||
// Open file
|
||||
$f = @fopen($filepath, "rb");
|
||||
if ($f === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Sets buffer size
|
||||
$buffer = 256;
|
||||
|
||||
// Jump to last character
|
||||
fseek($f, -1, SEEK_END);
|
||||
|
||||
// Read it and adjust line number if necessary
|
||||
// (Otherwise the result would be wrong if file doesn't end with a blank line)
|
||||
if (fread($f, 1) != "\n") {
|
||||
$lines -= 1;
|
||||
}
|
||||
|
||||
// Start reading
|
||||
$output = '';
|
||||
$chunk = '';
|
||||
|
||||
// While we would like more
|
||||
while (ftell($f) > 0 && $lines >= 0) {
|
||||
// Figure out how far back we should jump
|
||||
$seek = min(ftell($f), $buffer);
|
||||
// Do the jump (backwards, relative to where we are)
|
||||
fseek($f, -$seek, SEEK_CUR);
|
||||
// Read a chunk and prepend it to our output
|
||||
$output = ($chunk = fread($f, $seek)) . $output;
|
||||
// Jump back to where we started reading
|
||||
fseek($f, -mb_strlen($chunk, '8bit'), SEEK_CUR);
|
||||
// Decrease our line counter
|
||||
$lines -= substr_count($chunk, "\n");
|
||||
}
|
||||
|
||||
// While we have too many lines
|
||||
// (Because of buffer size we might have read too many)
|
||||
while ($lines++ < 0) {
|
||||
// Find first newline and remove all text before that
|
||||
$output = substr($output, strpos($output, "\n") + 1);
|
||||
}
|
||||
fclose($f);
|
||||
return trim($output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check given table is exist in real
|
||||
*
|
||||
* @param string $table string Table name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isTableExists($table)
|
||||
{
|
||||
// It will clear the $GLOBALS['wpdb']->last_error var
|
||||
$GLOBALS['wpdb']->flush();
|
||||
$sql = "SELECT 1 FROM `" . esc_sql($table) . "` LIMIT 1;";
|
||||
$ret = $GLOBALS['wpdb']->get_var($sql);
|
||||
if (empty($GLOBALS['wpdb']->last_error)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds if its a valid executable or not
|
||||
*
|
||||
* @param string $cmd A non zero length executable path to find if that is executable or not.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isExecutable($cmd)
|
||||
{
|
||||
if (strlen($cmd) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
@is_executable($cmd)
|
||||
|| !Shell::runCommand($cmd, Shell::AVAILABLE_COMMANDS)->isEmpty()
|
||||
|| !Shell::runCommand($cmd . ' -?', Shell::AVAILABLE_COMMANDS)->isEmpty()
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look into string and try to fix its natural expected value type
|
||||
*
|
||||
* @param mixed $data Simple string
|
||||
*
|
||||
* @return mixed value with it's natural string type
|
||||
*/
|
||||
public static function valType($data)
|
||||
{
|
||||
if (is_string($data)) {
|
||||
if (is_numeric($data)) {
|
||||
if ((int) $data == $data) {
|
||||
return (int) $data;
|
||||
} elseif ((float) $data == $data) {
|
||||
return (float) $data;
|
||||
}
|
||||
} elseif (in_array(strtolower($data), array('true', 'false'), true)) {
|
||||
return ($data == 'true');
|
||||
}
|
||||
} elseif (is_array($data)) {
|
||||
foreach ($data as $key => $str) {
|
||||
$data[$key] = DUP_PRO_U::valType($str);
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check given var is curl resource or instance of CurlHandle or CurlMultiHandle
|
||||
* It is used for check curl_init() return, because
|
||||
* curl_init() returns resource in lower PHP version than 8.0
|
||||
* curl_init() returns class instance in PHP version 8.0
|
||||
* Ref. https://php.watch/versions/8.0/resource-CurlHandle
|
||||
*
|
||||
* @param resource|object $var var to check
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function isCurlResourceOrInstance($var)
|
||||
{
|
||||
// CurlHandle class instance return of curl_init() in php 8.0
|
||||
// CurlMultiHandle class instance return of curl_multi_init() in php 8.0
|
||||
|
||||
if (is_resource($var) || ($var instanceof CurlHandle) || ($var instanceof CurlMultiHandle)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
defined("ABSPATH") or die("");
|
||||
/**
|
||||
* Utility class working with strings
|
||||
*
|
||||
* Standard: PSR-2
|
||||
*
|
||||
* @link http://www.php-fig.org/psr/psr-2
|
||||
*
|
||||
* @package DUP_PRO
|
||||
* @subpackage classes/utilities
|
||||
* @copyright (c) 2017, Snapcreek LLC
|
||||
* @license https://opensource.org/licenses/GPL-3.0 GNU Public License
|
||||
* @since 3.0.0
|
||||
*/
|
||||
class DUP_PRO_STR
|
||||
{
|
||||
/**
|
||||
* Append the value to the string if it doesn't already exist
|
||||
*
|
||||
* @param string $string The string to append to
|
||||
* @param string $value The string to append to the $string
|
||||
*
|
||||
* @return string Returns the string with the $value appended once
|
||||
*/
|
||||
public static function appendOnce($string, $value)
|
||||
{
|
||||
return $string . (substr($string, -1) == $value ? '' : $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the string contains UTF8 characters
|
||||
*
|
||||
* @see http://php.net/manual/en/function.mb-detect-encoding.php
|
||||
*
|
||||
* @param string $string The class name where the $destArray exists
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function hasUTF8($string)
|
||||
{
|
||||
return (preg_match('%(?:
|
||||
[\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
|
||||
|\xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
|
||||
|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
|
||||
|\xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
|
||||
|\xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
|
||||
|[\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
|
||||
|\xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
|
||||
)+%xs', $string) === 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the $needle is found in the $haystack
|
||||
*
|
||||
* @param string $haystack The full string to search in
|
||||
* @param string $needle The string to for
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function contains($haystack, $needle)
|
||||
{
|
||||
$pos = strpos($haystack, $needle);
|
||||
return ($pos !== false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the boolean type to a string version of 'True', 'False'
|
||||
*
|
||||
* @param bool $b The boolean value
|
||||
*
|
||||
* @return string Returns the boolean type to a string version of 'True', 'False'
|
||||
*/
|
||||
public static function boolToString($b)
|
||||
{
|
||||
return ($b ? __('True', 'duplicator-pro') : __('False', 'duplicator-pro'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the $haystack string starts with the $needle
|
||||
*
|
||||
* @param string $haystack The full string to search in
|
||||
* @param string $needle The string to for
|
||||
*
|
||||
* @return bool Returns true if the $haystack string starts with the $needle
|
||||
*/
|
||||
public static function startsWith($haystack, $needle)
|
||||
{
|
||||
$length = strlen($needle);
|
||||
return (substr($haystack, 0, $length) === $needle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the $haystack string ends with the $needle
|
||||
*
|
||||
* @param string $haystack The full string to search in
|
||||
* @param string $needle The string to for
|
||||
*
|
||||
* @return bool Returns true if the $haystack string ends with the $needle
|
||||
*/
|
||||
public static function endsWith($haystack, $needle)
|
||||
{
|
||||
$length = strlen($needle);
|
||||
if ($length == 0) {
|
||||
return true;
|
||||
}
|
||||
return (substr($haystack, -$length) === $needle);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,390 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Utility class to create a tree structure from paths
|
||||
*
|
||||
* Standard: PSR-2
|
||||
*
|
||||
* @link http://www.php-fig.org/psr/psr-2
|
||||
*
|
||||
* @package DUP_PRO
|
||||
* @subpackage classes/utilities
|
||||
* @copyright (c) 2017, Snapcreek LLC
|
||||
* @license https://opensource.org/licenses/GPL-3.0 GNU Public License
|
||||
* @since 3.8.1
|
||||
*/
|
||||
|
||||
defined("ABSPATH") or die("");
|
||||
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
|
||||
/**
|
||||
* Tree files utility
|
||||
*/
|
||||
class DUP_PRO_Tree_files
|
||||
{
|
||||
/**
|
||||
* All props must be public for json encode
|
||||
*/
|
||||
/**
|
||||
*
|
||||
* @var DUP_PRO_Tree_files_node[]
|
||||
*/
|
||||
protected $treeList = array();
|
||||
|
||||
/**
|
||||
* Class contructor
|
||||
*
|
||||
* @param string|string[] $rootPaths root paths
|
||||
* @param bool $addAllChilds if true add all childs
|
||||
* @param null|false|string|string[] $excludeList if null or false no exclude else exclude list
|
||||
*/
|
||||
public function __construct($rootPaths, $addAllChilds = true, $excludeList = array())
|
||||
{
|
||||
if (is_null($excludeList) || $excludeList === false) {
|
||||
$excludeList = array();
|
||||
} elseif (!is_array($excludeList)) {
|
||||
$excludeList = array($excludeList);
|
||||
}
|
||||
|
||||
if (!is_array($rootPaths)) {
|
||||
$rootPaths = array($rootPaths);
|
||||
}
|
||||
$rootPaths = array_map(array('\\Duplicator\\Libs\\Snap\\SnapIO', 'safePathUntrailingslashit'), $rootPaths);
|
||||
foreach ($rootPaths as $path) {
|
||||
$this->treeList[$path] = new DUP_PRO_Tree_files_node($path);
|
||||
if ($addAllChilds) {
|
||||
$this->treeList[$path]->addAllChilds($excludeList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $path full path
|
||||
* @param array<string,mixed> $data optiona data associated at node
|
||||
*
|
||||
* @return bool|DUP_PRO_Tree_files_node
|
||||
*/
|
||||
public function addElement($path, $data = array())
|
||||
{
|
||||
foreach ($this->treeList as $rootPath => $tree) {
|
||||
if (strpos($path, trailingslashit($rootPath)) === 0) {
|
||||
$newElem = $tree->addChild($path, true, false);
|
||||
if ($newElem) {
|
||||
$newElem->data = $data;
|
||||
}
|
||||
return $newElem;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort child list with callback function of all trees root nodes
|
||||
*
|
||||
* @param callable $callback function to call
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function uasort($callback)
|
||||
{
|
||||
foreach ($this->treeList as $tree) {
|
||||
$tree->uasort($callback);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* traverse tree anche call callback function of all trees root nodes
|
||||
*
|
||||
* @param callable $callback function to call
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function treeTraverseCallback($callback)
|
||||
{
|
||||
foreach ($this->treeList as $tree) {
|
||||
$tree->treeTraverseCallback($callback);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return DUP_PRO_Tree_files_node[]
|
||||
*/
|
||||
public function getTreeList()
|
||||
{
|
||||
return $this->treeList;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tree node data
|
||||
*/
|
||||
class DUP_PRO_Tree_files_node
|
||||
{
|
||||
const MAX_CHILDS_FOR_FOLDER = 250;
|
||||
const MAX_TREE_NODES = 5000;
|
||||
|
||||
/**
|
||||
* All props must be public for json encode
|
||||
*/
|
||||
/**
|
||||
*
|
||||
* @var string unique id l0_l1_l2....
|
||||
*/
|
||||
public $id = '';
|
||||
|
||||
/**
|
||||
*
|
||||
* @var string parent id l0_l1_...
|
||||
*/
|
||||
public $parentId = '';
|
||||
|
||||
/**
|
||||
*
|
||||
* @var string file basename
|
||||
*/
|
||||
public $name = '';
|
||||
|
||||
/**
|
||||
*
|
||||
* @var string full path
|
||||
*/
|
||||
public $fullPath = '';
|
||||
|
||||
/**
|
||||
*
|
||||
* @var bool is directory
|
||||
*/
|
||||
public $isDir = false;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var DUP_PRO_Tree_files_node[] childs nodes
|
||||
*/
|
||||
public $childs = array();
|
||||
|
||||
/**
|
||||
*
|
||||
* @var array<string, mixed> optiona data associated ad node
|
||||
*/
|
||||
public $data = array();
|
||||
|
||||
/**
|
||||
*
|
||||
* @var bool true if folder have a childs
|
||||
*/
|
||||
public $haveChildren = null;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $traversed = false;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var int if can't add a child increment exceeede count
|
||||
*/
|
||||
private $nodesExceeded = 0;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $numTreeNodes = 1;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $path file path
|
||||
* @param string $id current level unique id
|
||||
* @param string $parent_id parent id
|
||||
*/
|
||||
public function __construct($path, $id = '0', $parent_id = '')
|
||||
{
|
||||
$safePath = SnapIO::safePathUntrailingslashit($path);
|
||||
$this->id = (strlen($parent_id) == 0 ? '' : $parent_id . '_') . $id;
|
||||
$this->parentId = $parent_id;
|
||||
$this->name = basename($safePath);
|
||||
$this->fullPath = $safePath;
|
||||
$this->isDir = @is_dir($this->fullPath);
|
||||
$this->haveChildrenCheck();
|
||||
}
|
||||
|
||||
/**
|
||||
* create tree tructure until at basename
|
||||
*
|
||||
* @param string $path file path
|
||||
* @param bool $fullPath if true is considered a full path and must be a child of a current node else is a relative path
|
||||
* @param bool $loadTraverse if true, add the files and folders present at the level of each node
|
||||
*
|
||||
* @return boolean|DUP_PRO_Tree_files_node if fails terurn false ellse return the leaf child added
|
||||
*/
|
||||
public function addChild($path, $fullPath = true, $loadTraverse = false)
|
||||
{
|
||||
if (empty($path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$safePath = SnapIO::safePathUntrailingslashit($path);
|
||||
if ($fullPath) {
|
||||
if (strpos($safePath, trailingslashit($this->fullPath)) !== 0) {
|
||||
throw new Exception('Can\'t add no child on tree; file: "' . $safePath . '" || fullpath: "' . $this->fullPath . '"');
|
||||
}
|
||||
$childPath = substr($safePath, strlen($this->fullPath));
|
||||
} else {
|
||||
$childPath = $safePath;
|
||||
}
|
||||
|
||||
$tree_list = explode('/', $childPath);
|
||||
if (empty($tree_list[0])) {
|
||||
array_shift($tree_list);
|
||||
}
|
||||
|
||||
if (($child = $this->checkAndAddChild($tree_list[0])) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($loadTraverse) {
|
||||
$child->addAllChilds();
|
||||
}
|
||||
|
||||
if (count($tree_list) > 1) {
|
||||
array_shift($tree_list);
|
||||
$nodesBefore = $child->getNumTreeNodes();
|
||||
$result = $child->addChild(implode('/', $tree_list), false, $loadTraverse);
|
||||
$this->numTreeNodes += $child->getNumTreeNodes() - $nodesBefore;
|
||||
return $result;
|
||||
} else {
|
||||
return $child;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If is dir scan all children files and add on childs list
|
||||
*
|
||||
* @param string[] $excludeList child to add
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addAllChilds($excludeList = array())
|
||||
{
|
||||
if ($this->traversed === false) {
|
||||
$this->traversed = true;
|
||||
|
||||
if ($this->isDir) {
|
||||
if ($dh = @opendir($this->fullPath)) {
|
||||
while (($childName = readdir($dh)) !== false) {
|
||||
if ($childName == '.' || $childName == '..' || in_array($childName, $excludeList)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->checkAndAddChild($childName);
|
||||
}
|
||||
closedir($dh);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* check if current dir have children without load nodes
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function haveChildrenCheck()
|
||||
{
|
||||
if ($this->isDir) {
|
||||
$this->haveChildren = false;
|
||||
|
||||
if ($dh = opendir($this->fullPath)) {
|
||||
while (!$this->haveChildren && ($file = readdir($dh)) !== false) {
|
||||
$this->haveChildren = $file !== "." && $file !== "..";
|
||||
}
|
||||
closedir($dh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $name child name
|
||||
*
|
||||
* @return boolean|DUP_PRO_Tree_files_node if notes exceeded return false
|
||||
*/
|
||||
private function checkAndAddChild($name)
|
||||
{
|
||||
if (!array_key_exists($name, $this->childs)) {
|
||||
if ($this->numTreeNodes > self::MAX_TREE_NODES || count($this->childs) >= self::MAX_CHILDS_FOR_FOLDER) {
|
||||
$this->nodesExceeded++;
|
||||
return false;
|
||||
} else {
|
||||
$child = new DUP_PRO_Tree_files_node($this->fullPath . '/' . $name, (string) count($this->childs), $this->id);
|
||||
$this->childs[$name] = $child;
|
||||
$this->numTreeNodes++;
|
||||
}
|
||||
}
|
||||
return $this->childs[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* sort child list with callback function
|
||||
*
|
||||
* @param callable $value_compare_func function to call
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function uasort($value_compare_func)
|
||||
{
|
||||
if (!is_callable($value_compare_func)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($this->childs as $child) {
|
||||
$child->uasort($value_compare_func);
|
||||
}
|
||||
|
||||
uasort($this->childs, $value_compare_func);
|
||||
}
|
||||
|
||||
/**
|
||||
* traverse tree anche call callback function
|
||||
*
|
||||
* @param callable $callback function to call
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function treeTraverseCallback($callback)
|
||||
{
|
||||
if (!is_callable($callback)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($this->childs as $child) {
|
||||
$child->treeTraverseCallback($callback);
|
||||
}
|
||||
|
||||
call_user_func($callback, $this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of nodesExceeded
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getNodesExceeded()
|
||||
{
|
||||
return $this->nodesExceeded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of numTreeNodes
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getNumTreeNodes()
|
||||
{
|
||||
return $this->numTreeNodes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Validate variables
|
||||
*
|
||||
* Standard: PSR-2
|
||||
*
|
||||
* @link http://www.php-fig.org/psr/psr-2
|
||||
*
|
||||
* @package Duplicator
|
||||
* @subpackage classes/utilities
|
||||
* @copyright (c) 2017, Snapcreek LLC
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
defined("ABSPATH") or die("");
|
||||
class DUP_PRO_Validator
|
||||
{
|
||||
/** @var array<string,string> $patterns */
|
||||
private static $patterns = array(
|
||||
'fdir' => '/^([a-zA-Z]:[\\\\\/]|\/|\\\\\\\\|\/\/)[^<>\0]+$/',
|
||||
'fdirwc' => '/^[\s\t]*?(#.*|[a-zA-Z]:[\\\\\/]|\/|\\\\\\\\|\/\/)[^<>\0]*$/',
|
||||
'ffile' => '/^([a-zA-Z]:[\\\\\/]|\/|\\\\\\\\|\/\/)[^<>\0]+$/',
|
||||
'ffilewc' => '/^[\s\t]*?(#.*|[a-zA-Z]:[\\\\\/]|\/|\\\\\\\\|\/\/)[^<>\0]*$/',
|
||||
'fext' => '/^\.?[^\\\\\/*:<>\0?"|\s\.]+$/',
|
||||
'email' => '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_\`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/',
|
||||
'empty' => '/^$/',
|
||||
'nempty' => '/^.+$/',
|
||||
);
|
||||
const FILTER_VALIDATE_IS_EMPTY = 'empty';
|
||||
const FILTER_VALIDATE_NOT_EMPTY = 'nempty';
|
||||
const FILTER_VALIDATE_FILE = 'ffile';
|
||||
const FILTER_VALIDATE_FILE_WITH_COMMENT = 'ffilewc';
|
||||
const FILTER_VALIDATE_FOLDER = 'fdir';
|
||||
const FILTER_VALIDATE_FOLDER_WITH_COMMENT = 'fdirwc';
|
||||
const FILTER_VALIDATE_FILE_EXT = 'fext';
|
||||
const FILTER_VALIDATE_EMAIL = 'email';
|
||||
|
||||
/** @var array<array{key:string,msg:string}> */
|
||||
private $errors = array();
|
||||
|
||||
/**
|
||||
* Class contructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->errors = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->errors = array();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isSuccess()
|
||||
{
|
||||
return empty($this->errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return errors
|
||||
*
|
||||
* @return array<array{key:string,msg:string}>
|
||||
*/
|
||||
public function getErrors()
|
||||
{
|
||||
return $this->errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return errors messages
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getErrorsMsg()
|
||||
{
|
||||
$result = array();
|
||||
foreach ($this->errors as $err) {
|
||||
$result[] = $err['msg'];
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $format printf format message where %s is the variable content default "%s\n"
|
||||
* @param bool $echo if false return string
|
||||
*
|
||||
* @return void|string
|
||||
*/
|
||||
public function getErrorsFormat($format = "%s\n", $echo = true)
|
||||
{
|
||||
$msgs = $this->getErrorsMsg();
|
||||
ob_start();
|
||||
foreach ($msgs as $msg) {
|
||||
printf($format, esc_html($msg)); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
if ($echo) {
|
||||
ob_end_flush();
|
||||
} else {
|
||||
return ob_get_clean();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key field key
|
||||
* @param string $msg error message
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addError($key, $msg)
|
||||
{
|
||||
$this->errors[] = array(
|
||||
'key' => $key,
|
||||
'msg' => $msg,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* filter_var function wrapper see http://php.net/manual/en/function.filter-var.php
|
||||
*
|
||||
* additional options
|
||||
* valkey => key of field
|
||||
* errmsg => error message; % s will be replaced with the contents of the variable es. "<b>%s</b> isn't a valid field"
|
||||
* acc_vals => array of accepted values that skip validation
|
||||
*
|
||||
* @param mixed $variable variable to validate
|
||||
* @param int $filter filter name
|
||||
* @param array<string,mixed> $options additional options for filter_var
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function filter_var($variable, $filter = FILTER_DEFAULT, $options = array())
|
||||
{
|
||||
$success = true;
|
||||
$result = null;
|
||||
if (isset($options['acc_vals']) && in_array($variable, $options['acc_vals'])) {
|
||||
return $variable;
|
||||
}
|
||||
|
||||
if ($filter === FILTER_VALIDATE_BOOLEAN) {
|
||||
$options['flags'] = FILTER_NULL_ON_FAILURE;
|
||||
/** @var null|bool */
|
||||
$result = is_bool($variable) ? $variable : filter_var($variable, $filter, $options);
|
||||
if (is_null($result)) {
|
||||
$success = false;
|
||||
}
|
||||
} else {
|
||||
$result = filter_var($variable, $filter, $options);
|
||||
if ($result === false) {
|
||||
$success = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$success) {
|
||||
$key = isset($options['valkey']) ? $options['valkey'] : '';
|
||||
if (isset($options['errmsg'])) {
|
||||
$msg = sprintf($options['errmsg'], esc_html($variable));
|
||||
} else {
|
||||
$msg = sprintf('%1$s isn\'t a valid value', $variable);
|
||||
}
|
||||
|
||||
$this->addError($key, $msg);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation of predefined regular expressions
|
||||
*
|
||||
* @param mixed $variable variable to validate
|
||||
* @param string $filter filter name
|
||||
* @param array<string, mixed> $options additional options for filter_var
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function filter_custom($variable, $filter, $options = array())
|
||||
{
|
||||
if (!isset(self::$patterns[$filter])) {
|
||||
throw new Exception('Filter not valid');
|
||||
}
|
||||
|
||||
$options = array_merge($options, array(
|
||||
'options' => array(
|
||||
'regexp' => self::$patterns[$filter],
|
||||
),
|
||||
));
|
||||
//$options['regexp'] = self::$patterns[$filter];
|
||||
|
||||
return $this->filter_var($variable, FILTER_VALIDATE_REGEXP, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* it explodes a string with a delimiter and validates every element of the array
|
||||
*
|
||||
* @param string $variable string to explode
|
||||
* @param string $delimiter delimiter
|
||||
* @param string $filter filter name
|
||||
* @param array<string, mixed> $options additional options for filter_var
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function explode_filter_custom($variable, $delimiter, $filter, $options = array())
|
||||
{
|
||||
if (empty($variable)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
if (strlen($delimiter) == 0) {
|
||||
throw new Exception('Delimiter can\'t be empty');
|
||||
}
|
||||
|
||||
$vals = explode($delimiter, trim($variable, $delimiter));
|
||||
$res = array();
|
||||
foreach ($vals as $val) {
|
||||
$res[] = $this->filter_custom($val, $filter, $options);
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Utility class for zipping up content
|
||||
*
|
||||
* Standard: PSR-2
|
||||
*
|
||||
* @link http://www.php-fig.org/psr/psr-2
|
||||
*
|
||||
* @package DUP_PRO
|
||||
* @subpackage classes/utilities
|
||||
* @copyright (c) 2017, Snapcreek LLC
|
||||
* @license https://opensource.org/licenses/GPL-3.0 GNU Public License
|
||||
* @since 3.3.0
|
||||
*/
|
||||
|
||||
use Duplicator\Libs\Shell\Shell;
|
||||
|
||||
/**
|
||||
* Helper class for reporting problems with zipping
|
||||
*
|
||||
* @see DUP_PRO_Zip_U
|
||||
*/
|
||||
class DUP_PRO_Problem_Fix
|
||||
{
|
||||
/** @var string The detected problem */
|
||||
public $problem = '';
|
||||
/** @var string A recommended fix for the problem */
|
||||
public $fix = '';
|
||||
}
|
||||
|
||||
class DUP_PRO_Zip_U
|
||||
{
|
||||
/**
|
||||
* Gets an array of possible ZipArchive problems on the server
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
private static function getPossibleZipPaths()
|
||||
{
|
||||
return array(
|
||||
'/usr/bin/zip',
|
||||
'/opt/local/bin/zip', // RSR TODO put back in when we support shellexec on windows,
|
||||
//'C:/Program\ Files\ (x86)/GnuWin32/bin/zip.exe');
|
||||
'/opt/bin/zip',
|
||||
'/bin/zip',
|
||||
'/usr/local/bin/zip',
|
||||
'/usr/sfw/bin/zip',
|
||||
'/usr/xdg4/bin/zip',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an array of possible ShellExec Zip problems on the server
|
||||
*
|
||||
* @return DUP_PRO_Problem_Fix[]
|
||||
*/
|
||||
public static function getShellExecZipProblems()
|
||||
{
|
||||
$problem_fixes = array();
|
||||
if (!self::getShellExecZipPath()) {
|
||||
$filepath = null;
|
||||
$possible_paths = self::getPossibleZipPaths();
|
||||
foreach ($possible_paths as $path) {
|
||||
if (file_exists($path)) {
|
||||
$filepath = $path;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($filepath == null) {
|
||||
$problem_fix = new DUP_PRO_Problem_Fix();
|
||||
$problem_fix->problem = __('Zip executable not present', 'duplicator-pro');
|
||||
$problem_fix->fix = __('Install the zip executable and make it accessible to PHP.', 'duplicator-pro');
|
||||
$problem_fixes[] = $problem_fix;
|
||||
}
|
||||
|
||||
if (Shell::isSuhosinEnabled()) {
|
||||
$fixDisabled = __(
|
||||
'Remove any of the following from the disable_functions or suhosin.executor.func.blacklist setting in the php.ini files: %1$s',
|
||||
'duplicator-pro'
|
||||
);
|
||||
} else {
|
||||
$fixDisabled = __(
|
||||
'Remove any of the following from the disable_functions setting in the php.ini files: %1$s',
|
||||
'duplicator-pro'
|
||||
);
|
||||
}
|
||||
|
||||
//Function disabled at server level
|
||||
if (Shell::hasDisabledFunctions(array('escapeshellarg', 'escapeshellcmd', 'extension_loaded'))) {
|
||||
$problem_fix = new DUP_PRO_Problem_Fix();
|
||||
$problem_fix->problem = __('Required functions disabled in the php.ini.', 'duplicator-pro');
|
||||
$problem_fix->fix = sprintf($fixDisabled, 'escapeshellarg, escapeshellcmd, extension_loaded.');
|
||||
$problem_fixes[] = $problem_fix;
|
||||
}
|
||||
|
||||
if (Shell::hasDisabledFunctions(array('popen', 'pclose', 'exec', 'shell_exec'))) {
|
||||
$problem_fix = new DUP_PRO_Problem_Fix();
|
||||
$problem_fix->problem = __('Required functions disabled in the php.ini.', 'duplicator-pro');
|
||||
$problem_fix->fix = sprintf($fixDisabled, 'popen, pclose or exec or shell_exec.');
|
||||
$problem_fixes[] = $problem_fix;
|
||||
}
|
||||
}
|
||||
|
||||
return $problem_fixes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to the zip program executable on the server
|
||||
* If wordpress have multiple scan path shell zip archive is disabled
|
||||
*
|
||||
* @return null|string Returns the path to the zip program or null if isn't available
|
||||
*/
|
||||
public static function getShellExecZipPath()
|
||||
{
|
||||
$filepath = null;
|
||||
if (apply_filters('duplicator_pro_is_shellzip_available', Shell::test(Shell::AVAILABLE_COMMANDS))) {
|
||||
$scanPath = DUP_PRO_Archive::getScanPaths();
|
||||
if (count($scanPath) > 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$shellOutput = Shell::runCommand('hash zip 2>&1', Shell::AVAILABLE_COMMANDS);
|
||||
if ($shellOutput !== false && $shellOutput->isEmpty()) {
|
||||
$filepath = 'zip';
|
||||
} else {
|
||||
$possible_paths = self::getPossibleZipPaths();
|
||||
foreach ($possible_paths as $path) {
|
||||
if (file_exists($path)) {
|
||||
$filepath = $path;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $filepath;
|
||||
}
|
||||
|
||||
/**
|
||||
* custom shell arg escape sequence
|
||||
*
|
||||
* @param string $arg argument to escape
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function customShellArgEscapeSequence($arg)
|
||||
{
|
||||
return str_replace(array(' ', '-'), array('\ ', '\-'), $arg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
//silent
|
||||
Reference in New Issue
Block a user