Download all files FTP

This commit is contained in:
2026-04-13 15:50:16 +02:00
parent d8382136b2
commit cb5b386424
6906 changed files with 1956223 additions and 40713 deletions

View File

@@ -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";
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -0,0 +1,3 @@
<?php
//silent