forked from LiveCarta/LiveCartaWP
Changed source root directory
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
|
||||
use Duplicator\Libs\Snap\SnapJson;
|
||||
|
||||
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
||||
/**
|
||||
* Utility class for working with JSON data
|
||||
*
|
||||
* Standard: PSR-2
|
||||
*
|
||||
* @link http://www.php-fig.org/psr/psr-2
|
||||
*
|
||||
* @subpackage classes/utilities
|
||||
* @copyright (c) 2017, Snapcreek LLC
|
||||
* @license https://opensource.org/licenses/GPL-3.0 GNU Public License
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if (! defined('DUPLICATOR_VERSION')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class DUP_JSON
|
||||
{
|
||||
// PHP 5.3 doesn't allow concating of strings within an array initializer so accepting long string
|
||||
protected static $messages = array(
|
||||
JSON_ERROR_NONE => 'No error has occurred',
|
||||
JSON_ERROR_DEPTH => 'The maximum stack depth has been exceeded',
|
||||
JSON_ERROR_STATE_MISMATCH => 'Invalid or malformed JSON',
|
||||
JSON_ERROR_CTRL_CHAR => 'Control character error, possibly incorrectly encoded',
|
||||
JSON_ERROR_SYNTAX => 'Syntax error',
|
||||
JSON_ERROR_UTF8 => 'Malformed UTF-8 characters. To resolve see https://duplicator.com/knowledge-base/ how-to-resolve-malformed-utf-8-characters-possibly-incorrectly-encoded-issues/'
|
||||
);
|
||||
/**
|
||||
* Used on PHP 5.3+ to better handle calling the json_encode method
|
||||
*
|
||||
* Returns a string containing the JSON representation of the supplied value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function customEncode($value, $iteration = 1)
|
||||
{
|
||||
if (DUP_Util::$on_php_53_plus) {
|
||||
$encoded = SnapJson::jsonEncodePPrint($value);
|
||||
switch (json_last_error()) {
|
||||
case JSON_ERROR_NONE:
|
||||
return $encoded;
|
||||
case JSON_ERROR_DEPTH:
|
||||
throw new RuntimeException('Maximum stack depth exceeded');
|
||||
case JSON_ERROR_STATE_MISMATCH:
|
||||
throw new RuntimeException('Underflow or the modes mismatch');
|
||||
case JSON_ERROR_CTRL_CHAR:
|
||||
throw new RuntimeException('Unexpected control character found');
|
||||
case JSON_ERROR_SYNTAX:
|
||||
throw new RuntimeException('Syntax error, malformed JSON');
|
||||
case JSON_ERROR_UTF8:
|
||||
if ($iteration == 1) {
|
||||
$clean = self::makeUTF8($value);
|
||||
return self::customEncode($clean, $iteration + 1);
|
||||
} else {
|
||||
throw new RuntimeException('UTF-8 error loop');
|
||||
}
|
||||
default:
|
||||
throw new RuntimeException('Unknown error');
|
||||
}
|
||||
} else {
|
||||
return self::oldCustomEncode($value);
|
||||
}
|
||||
}
|
||||
|
||||
public static function safeEncode($data, $options = 0, $depth = 512)
|
||||
{
|
||||
try {
|
||||
$jsonString = SnapJson::jsonEncode($data, $options, $depth);
|
||||
} catch (Exception $e) {
|
||||
$jsonString = false;
|
||||
}
|
||||
|
||||
if (($jsonString === false) || trim($jsonString) == '') {
|
||||
$jsonString = self::customEncode($data);
|
||||
if (($jsonString === false) || trim($jsonString) == '') {
|
||||
throw new Exception('Unable to generate JSON from object');
|
||||
}
|
||||
}
|
||||
return $jsonString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to only call the json_decode method directly
|
||||
*
|
||||
* Returns the value encoded in json in appropriate PHP type. Values true, false and null are returned as TRUE, FALSE and NULL respectively.
|
||||
* NULL is returned if the json cannot be decoded or if the encoded data is deeper than the recursion limit.
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public static function decode($json, $assoc = false)
|
||||
{
|
||||
$result = json_decode($json, $assoc);
|
||||
if ($result !== null) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
if (function_exists('json_last_error')) {
|
||||
throw new RuntimeException(self::$messages[json_last_error()]);
|
||||
} else {
|
||||
throw new RuntimeException("DUP_JSON decode error");
|
||||
}
|
||||
}
|
||||
|
||||
private static function makeUTF8($mixed)
|
||||
{
|
||||
if (is_array($mixed)) {
|
||||
foreach ($mixed as $key => $value) {
|
||||
$mixed[$key] = self::makeUTF8($value);
|
||||
}
|
||||
} elseif (is_string($mixed)) {
|
||||
return utf8_encode($mixed);
|
||||
}
|
||||
return $mixed;
|
||||
}
|
||||
|
||||
private static function escapeString($str)
|
||||
{
|
||||
return addcslashes($str, "\v\t\n\r\f\"\\/");
|
||||
}
|
||||
|
||||
private static function oldCustomEncode($in)
|
||||
{
|
||||
$out = "";
|
||||
if (is_object($in)) {
|
||||
$in = get_object_vars($in);
|
||||
}
|
||||
|
||||
if (is_array($in)) {
|
||||
$obj = false;
|
||||
$arr = array();
|
||||
foreach ($in as $key => $val) {
|
||||
if (!is_numeric($key)) {
|
||||
$obj = true;
|
||||
}
|
||||
$arr[$key] = self::oldCustomEncode($val);
|
||||
}
|
||||
|
||||
if ($obj) {
|
||||
foreach ($arr as $key => $val) {
|
||||
$arr[$key] = "\"" . self::escapeString($key) . "\":{$val}";
|
||||
}
|
||||
$val = implode(',', $arr);
|
||||
$out .= "{{$val}}";
|
||||
} else {
|
||||
$val = implode(',', $arr);
|
||||
$out .= "[{$val}]";
|
||||
}
|
||||
} elseif (is_bool($in)) {
|
||||
$out .= $in ? 'true' : 'false';
|
||||
} elseif (is_null($in)) {
|
||||
$out .= 'null';
|
||||
} elseif (is_string($in)) {
|
||||
$out .= "\"" . self::escapeString($in) . "\"";
|
||||
} else {
|
||||
$out .= $in;
|
||||
}
|
||||
|
||||
return "{$out}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
<?php
|
||||
|
||||
use Duplicator\Libs\Snap\SnapDB;
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
use Duplicator\Libs\Snap\SnapURL;
|
||||
use Duplicator\Libs\Snap\SnapWP;
|
||||
|
||||
class DUP_MU
|
||||
{
|
||||
public static function networkMenuPageUrl($menu_slug, $echo = true)
|
||||
{
|
||||
global $_parent_pages;
|
||||
|
||||
if (isset($_parent_pages[$menu_slug])) {
|
||||
$parent_slug = $_parent_pages[$menu_slug];
|
||||
if ($parent_slug && !isset($_parent_pages[$parent_slug])) {
|
||||
$url = network_admin_url(add_query_arg('page', $menu_slug, $parent_slug));
|
||||
} else {
|
||||
$url = network_admin_url('admin.php?page=' . $menu_slug);
|
||||
}
|
||||
} else {
|
||||
$url = '';
|
||||
}
|
||||
|
||||
$url = esc_url($url);
|
||||
|
||||
if ($echo) {
|
||||
echo esc_url($url);
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @deprecated since version 3.8.4
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function getGeneration()
|
||||
{
|
||||
if (self::getMode() == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
$sitesDir = WP_CONTENT_DIR . '/uploads/sites';
|
||||
|
||||
if (file_exists($sitesDir)) {
|
||||
return 2;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param array $filteredSites
|
||||
* @param array $filteredTables
|
||||
* @param array $filteredPaths
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
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_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_Log::trace("Multisite subsite detected. ID={$siteInfo->id} Domain={$siteInfo->domain} Path={$siteInfo->path} Blogname={$siteInfo->blogname}");
|
||||
}
|
||||
|
||||
return $site_array;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param int $subsiteId
|
||||
*
|
||||
* @return stdClass|bool false on failure
|
||||
*/
|
||||
public static function getSubsiteInfoById($subsiteId)
|
||||
{
|
||||
if (!is_multisite()) {
|
||||
$subsiteId = 1;
|
||||
}
|
||||
return self::getSubsiteInfo($subsiteId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get subsite info
|
||||
*
|
||||
* @param int $siteId
|
||||
* @param array $filteredTables
|
||||
* @param array|false $filteredPaths return
|
||||
*
|
||||
* @return stdClass|bool 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_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 stdClass();
|
||||
$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_Archive::getTargetRootPath(), true);
|
||||
$siteInfo->fullUploadPath = untrailingslashit($uploadPath);
|
||||
$siteInfo->fullUploadSafePath = SnapIO::safePathUntrailingslashit($uploadPath);
|
||||
$siteInfo->fullUploadUrl = $uploadData['baseurl'];
|
||||
if (count($filteredPaths)) {
|
||||
$globalDirFilters = apply_filters('duplicator_global_file_filters', $GLOBALS['DUPLICATOR_GLOBAL_FILE_FILTERS']);
|
||||
$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
|
||||
*
|
||||
* @return array 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,80 @@
|
||||
<?php
|
||||
|
||||
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
||||
|
||||
/**
|
||||
* Class used to apply various patches to installer file
|
||||
*
|
||||
* Standard: PSR-2
|
||||
*
|
||||
* @link http://www.php-fig.org/psr/psr-2
|
||||
*
|
||||
* @package Duplicator
|
||||
* @subpackage classes
|
||||
* @copyright (c) 2022, Snapcreek LLC
|
||||
*/
|
||||
class DUP_Patch
|
||||
{
|
||||
/**
|
||||
* The current backup directory path for Duplicator Lite
|
||||
* Possible options are 'wp-snapshots' and 'backups-dup-lite'
|
||||
*/
|
||||
public $DupLiteBackupDir;
|
||||
|
||||
/**
|
||||
* Class construct for init
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->DupLiteBackupDir = DUP_Settings::getSsdirPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply patch code to all installer files
|
||||
*/
|
||||
public function ApplyInstallerPatch_0001()
|
||||
{
|
||||
$backupDir = $this->DupLiteBackupDir;
|
||||
|
||||
foreach (glob("{$backupDir}/*_installer.php") as $file) {
|
||||
if (strstr($file, '_installer.php')) {
|
||||
$content = "<?php \n";
|
||||
$content .= " /** PATCH_MARKER_START:V.0001 **/ \n";
|
||||
$content .= " //TODO ADD PHP CODE HERE";
|
||||
$content .= " /** PATCH_MARKER_END **/ \n";
|
||||
$content .= "?>\n";
|
||||
$this->fwritePrepend($file, $content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prepends data to an existing file
|
||||
*
|
||||
* @param string $file The full file path to the file
|
||||
* @param string $content The content to prepend to the file
|
||||
*
|
||||
* @return TRUE on success or if file does not exist. FALSE on failure
|
||||
*/
|
||||
private function fwritePrepend($file, $prepend)
|
||||
{
|
||||
if (!file_exists($file) || !is_writable($file)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$handle = fopen($file, "r+");
|
||||
$len = strlen($prepend);
|
||||
$final_len = filesize($file) + $len;
|
||||
$cache_old = fread($handle, $len);
|
||||
rewind($handle);
|
||||
$i = 1;
|
||||
while (ftell($handle) < $final_len) {
|
||||
fwrite($handle, $prepend);
|
||||
$prepend = $cache_old;
|
||||
$cache_old = fread($handle, $len);
|
||||
fseek($handle, $i * $len);
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
950
html/wp-content/plugins/duplicator/classes/utilities/class.u.php
Normal file
950
html/wp-content/plugins/duplicator/classes/utilities/class.u.php
Normal file
@@ -0,0 +1,950 @@
|
||||
<?php
|
||||
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
|
||||
/**
|
||||
* Recursivly scans a directory and finds all sym-links and unreadable files
|
||||
*
|
||||
* Standard: PSR-2
|
||||
*
|
||||
* @link http://www.php-fig.org/psr/psr-2
|
||||
*
|
||||
* @package Duplicator
|
||||
* @subpackage classes/utilities
|
||||
* @copyright (c) 2017, Snapcreek LLC
|
||||
*
|
||||
* @todo Refactor out IO methods into class.io.php file
|
||||
*/
|
||||
|
||||
class DUP_Util
|
||||
{
|
||||
/**
|
||||
* Is PHP 5.2.9 or better running
|
||||
*/
|
||||
public static $on_php_529_plus;
|
||||
|
||||
/**
|
||||
* Is PHP 5.3 or better running
|
||||
*/
|
||||
public static $on_php_53_plus;
|
||||
|
||||
/**
|
||||
* Is PHP 5.4 or better running
|
||||
*/
|
||||
public static $on_php_54_plus;
|
||||
|
||||
/**
|
||||
* Is PHP 7 or better running
|
||||
*/
|
||||
public static $PHP7_plus;
|
||||
|
||||
/**
|
||||
* array of ini disable functions
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $iniDisableFuncs = null;
|
||||
|
||||
/**
|
||||
* Initialized on load (see end of file)
|
||||
*/
|
||||
public static function init()
|
||||
{
|
||||
/** @todo Remove the static init method in favor of always consistent values */
|
||||
self::$on_php_529_plus = version_compare(PHP_VERSION, '5.2.9') >= 0;
|
||||
self::$on_php_53_plus = version_compare(PHP_VERSION, '5.3.0') >= 0;
|
||||
self::$on_php_54_plus = version_compare(PHP_VERSION, '5.4.0') >= 0;
|
||||
self::$PHP7_plus = version_compare(PHP_VERSION, '7.0.0', '>=');
|
||||
}
|
||||
|
||||
public static function getArchitectureString()
|
||||
{
|
||||
switch (PHP_INT_SIZE) {
|
||||
case 4:
|
||||
return esc_html__('32-bit', 'duplicator');
|
||||
break;
|
||||
case 8:
|
||||
return esc_html__('64-bit', 'duplicator');
|
||||
break;
|
||||
default:
|
||||
return esc_html__('Unknown', 'duplicator');
|
||||
}
|
||||
}
|
||||
|
||||
public static function objectCopy($srcObject, $destObject, $skipMemberArray = null)
|
||||
{
|
||||
foreach ($srcObject as $member_name => $member_value) {
|
||||
if (!is_object($member_value) && (($skipMemberArray == null) || !in_array($member_name, $skipMemberArray))) {
|
||||
// Skipping all object members
|
||||
$destObject->$member_name = $member_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function getWPCoreDirs()
|
||||
{
|
||||
$wp_core_dirs = array(get_home_path() . 'wp-admin', get_home_path() . 'wp-includes');
|
||||
|
||||
//if wp_content is overrided
|
||||
$wp_path = get_home_path() . "wp-content";
|
||||
if (get_home_path() . 'wp-content' != WP_CONTENT_DIR) {
|
||||
$wp_path = WP_CONTENT_DIR;
|
||||
}
|
||||
$wp_path = str_replace("\\", "/", $wp_path);
|
||||
|
||||
$wp_core_dirs[] = $wp_path;
|
||||
$wp_core_dirs[] = $wp_path . '/plugins';
|
||||
$wp_core_dirs[] = $wp_path . '/themes';
|
||||
|
||||
return $wp_core_dirs;
|
||||
}
|
||||
|
||||
/**
|
||||
* return absolute path for the files that are core directories
|
||||
*
|
||||
* @return string array
|
||||
*/
|
||||
public static function getWPCoreFiles()
|
||||
{
|
||||
$wp_cored_dirs = array(get_home_path() . 'wp-config.php');
|
||||
return $wp_cored_dirs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Groups an array into arrays by a given key, or set of keys, shared between all array members.
|
||||
*
|
||||
* Based on {@author Jake Zatecky}'s {@link https://github.com/jakezatecky/array_group_by array_group_by()} function.
|
||||
* This variant allows $key to be closures.
|
||||
*
|
||||
* @param array $array The array to have grouping performed on.
|
||||
* @param mixed $key,... The key to group or split by. Can be a _string_, an _integer_, a _float_, or a _callable_.
|
||||
* - If the key is a callback, it must return a valid key from the array.
|
||||
* - If the key is _NULL_, the iterated element is skipped.
|
||||
* - string|oink callback ( mixed $item )
|
||||
*
|
||||
* @return array|null Returns a multidimensional array or `null` if `$key` is invalid.
|
||||
*/
|
||||
public static function array_group_by(array $array, $key)
|
||||
{
|
||||
if (!is_string($key) && !is_int($key) && !is_float($key) && !is_callable($key)) {
|
||||
trigger_error('array_group_by(): The key should be a string, an integer, or a callback', E_USER_ERROR);
|
||||
return null;
|
||||
}
|
||||
$func = (!is_string($key) && is_callable($key) ? $key : null);
|
||||
$_key = $key;
|
||||
// Load the new array, splitting by the target key
|
||||
$grouped = array();
|
||||
foreach ($array as $value) {
|
||||
$key = null;
|
||||
if (is_callable($func)) {
|
||||
$key = call_user_func($func, $value);
|
||||
} elseif (is_object($value) && isset($value->{$_key})) {
|
||||
$key = $value->{$_key};
|
||||
} elseif (isset($value[$_key])) {
|
||||
$key = $value[$_key];
|
||||
}
|
||||
if ($key === null) {
|
||||
continue;
|
||||
}
|
||||
$grouped[$key][] = $value;
|
||||
}
|
||||
// Recursively build a nested grouping if more parameters are supplied
|
||||
// Each grouped array value is grouped according to the next sequential key
|
||||
if (func_num_args() > 2) {
|
||||
$args = func_get_args();
|
||||
foreach ($grouped as $key => $value) {
|
||||
$params = array_merge(array($value), array_slice($args, 2, func_num_args()));
|
||||
$grouped[$key] = call_user_func_array('DUP_Util::array_group_by', $params);
|
||||
}
|
||||
}
|
||||
return $grouped;
|
||||
}
|
||||
|
||||
/**
|
||||
* PHP_SAPI for FCGI requires a data flush of at least 256
|
||||
* bytes every 40 seconds or else it forces a script halt
|
||||
*
|
||||
* @return string A series of 256 space characters
|
||||
*/
|
||||
public static function fcgiFlush()
|
||||
{
|
||||
echo(str_repeat(' ', 300));
|
||||
@flush();
|
||||
@ob_flush();
|
||||
}
|
||||
|
||||
public static function isWpDebug()
|
||||
{
|
||||
return defined('WP_DEBUG') && WP_DEBUG;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last N lines of a file. Equivalent 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 string The last N parts of the file
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, $roundBy = 2)
|
||||
{
|
||||
try {
|
||||
$units = array('B', 'KB', 'MB', 'GB', 'TB');
|
||||
for ($i = 0; $size >= 1024 && $i < 4; $i++) {
|
||||
$size /= 1024;
|
||||
}
|
||||
return round($size, $roundBy) . $units[$i];
|
||||
} catch (Exception $e) {
|
||||
return "n/a";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes path safe for any OS
|
||||
* Paths should ALWAYS READ be "/"
|
||||
* uni: /home/path/file.txt
|
||||
* win: D:/home/path/file.txt
|
||||
*
|
||||
* @param string $path The path to make safe
|
||||
*
|
||||
* @return string A path with all slashes facing "/"
|
||||
*/
|
||||
public static function safePath($path)
|
||||
{
|
||||
return str_replace("\\", "/", $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current microtime as a float. Method is used for simple profiling
|
||||
*
|
||||
* @see elapsedTime
|
||||
*
|
||||
* @return string 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a string with the elapsed time
|
||||
*
|
||||
* @see getMicrotime()
|
||||
*
|
||||
* @param mixed number $end The final time in the sequence to measure
|
||||
* @param mixed number $start The start time in the sequence to measure
|
||||
*
|
||||
* @return string The time elapsed from $start to $end
|
||||
*/
|
||||
public static function elapsedTime($end, $start)
|
||||
{
|
||||
return sprintf(
|
||||
esc_html_x(
|
||||
"%.2f sec.",
|
||||
"sec. stands for seconds",
|
||||
"duplicator"
|
||||
),
|
||||
abs($end - $start)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* List all of the files of a path
|
||||
*
|
||||
* @param string $path The full path to a system directory
|
||||
*
|
||||
* @return array of all files in that path
|
||||
*
|
||||
* Notes:
|
||||
* - Avoid using glob() as GLOB_BRACE is not an option on some operating systems
|
||||
* - Pre PHP 5.3 DirectoryIterator will crash on unreadable files
|
||||
* - Scandir will not crash on unreadable items, but will not return results
|
||||
*/
|
||||
public static function listFiles($path = '.')
|
||||
{
|
||||
try {
|
||||
$files = array();
|
||||
if ($dh = opendir($path)) {
|
||||
while (($file = readdir($dh)) !== false) {
|
||||
if ($file == '.' || $file == '..') {
|
||||
continue;
|
||||
}
|
||||
$full_file_path = trailingslashit($path) . $file;
|
||||
$files[] = str_replace("\\", '/', $full_file_path);
|
||||
}
|
||||
@closedir($dh);
|
||||
}
|
||||
return $files;
|
||||
} catch (Exception $exc) {
|
||||
$result = array();
|
||||
$files = @scandir($path);
|
||||
if (is_array($files)) {
|
||||
foreach ($files as $file) {
|
||||
$result[] = str_replace("\\", '/', $path) . $file;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List all of the directories of a path
|
||||
*
|
||||
* @param string $path The full path to a system directory
|
||||
*
|
||||
* @return array of all dirs in the $path
|
||||
*/
|
||||
public static function listDirs($path = '.')
|
||||
{
|
||||
$dirs = array();
|
||||
|
||||
foreach (new DirectoryIterator($path) as $file) {
|
||||
if ($file->isDir() && !$file->isDot()) {
|
||||
$dirs[] = DUP_Util::safePath($file->getPathname());
|
||||
}
|
||||
}
|
||||
return $dirs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the directory have content
|
||||
*
|
||||
* @param string $path The full path to a system directory
|
||||
*
|
||||
* @return bool Returns true if directory is empty
|
||||
*/
|
||||
public static function isDirectoryEmpty($path)
|
||||
{
|
||||
if (!is_readable($path)) {
|
||||
return null;
|
||||
}
|
||||
return (count(scandir($path)) == 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Size of the directory recursively in bytes
|
||||
*
|
||||
* @param string $path The full path to a system directory
|
||||
*
|
||||
* @return int Returns the size of the directory in bytes
|
||||
*/
|
||||
public static function getDirectorySize($path)
|
||||
{
|
||||
if (!file_exists($path)) {
|
||||
return 0;
|
||||
}
|
||||
if (is_file($path)) {
|
||||
return filesize($path);
|
||||
}
|
||||
|
||||
$size = 0;
|
||||
$list = glob($path . "/*");
|
||||
if (!empty($list)) {
|
||||
foreach ($list as $file) {
|
||||
$size += self::getDirectorySize($file);
|
||||
}
|
||||
}
|
||||
return $size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can shell_exec be called on this server
|
||||
*
|
||||
* @return bool Returns true if shell_exec can be called on server
|
||||
*/
|
||||
public static function hasShellExec()
|
||||
{
|
||||
$cmds = array('shell_exec', 'escapeshellarg', 'escapeshellcmd', 'extension_loaded');
|
||||
|
||||
//Function disabled at server level
|
||||
if (array_intersect($cmds, array_map('trim', explode(',', @ini_get('disable_functions'))))) {
|
||||
return apply_filters('duplicator_is_shellzip_available', false);
|
||||
}
|
||||
|
||||
//Suhosin: http://www.hardened-php.net/suhosin/
|
||||
//Will cause PHP to silently fail
|
||||
if (extension_loaded('suhosin')) {
|
||||
$suhosin_ini = @ini_get("suhosin.executor.func.blacklist");
|
||||
if (array_intersect($cmds, array_map('trim', explode(',', $suhosin_ini)))) {
|
||||
return apply_filters('duplicator_is_shellzip_available', false);
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('shell_exec')) {
|
||||
return apply_filters('duplicator_is_shellzip_available', false);
|
||||
}
|
||||
|
||||
// Can we issue a simple echo command?
|
||||
if (!@shell_exec('echo duplicator')) {
|
||||
return apply_filters('duplicator_is_shellzip_available', false);
|
||||
}
|
||||
|
||||
return apply_filters('duplicator_is_shellzip_available', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the server running Windows operating system
|
||||
*
|
||||
* @return bool Returns true if operating system is Windows
|
||||
*/
|
||||
public static function isWindows()
|
||||
{
|
||||
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap to prevent malware scanners from reporting false/positive
|
||||
* Switched from our old method to avoid WordFence reporting a false positive
|
||||
*
|
||||
* @param string $string The string to decrypt i.e. base64_decode
|
||||
*
|
||||
* @return string Returns the string base64 decoded
|
||||
*/
|
||||
public static function installerUnscramble($string)
|
||||
{
|
||||
return base64_decode($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap to prevent malware scanners from reporting false/positive
|
||||
* Switched from our old method to avoid WordFence reporting a false positive
|
||||
*
|
||||
* @param string $string The string to decrypt i.e. base64_encode
|
||||
*
|
||||
* @return string Returns the string base64 encode
|
||||
*/
|
||||
public static function installerScramble($string)
|
||||
{
|
||||
return base64_encode($string);
|
||||
}
|
||||
const SECURE_ISSUE_DIE = 'die';
|
||||
const SECURE_ISSUE_THROW = 'throw';
|
||||
const SECURE_ISSUE_RETURN = 'return';
|
||||
|
||||
/**
|
||||
* Does the current user have the capability
|
||||
*
|
||||
* @param type $permission
|
||||
* @param type $exit // SECURE_ISSUE_DIE die script with die function
|
||||
* SECURE_ISSUE_THROW throw an exception if fail
|
||||
* SECURE_ISSUE_RETURN return false if fail
|
||||
*
|
||||
* @return boolean // return false is fail and $exit is SECURE_ISSUE_THROW
|
||||
* // true if success
|
||||
*
|
||||
* @throws Exception // thow exception if $exit is SECURE_ISSUE_THROW
|
||||
*/
|
||||
public static function hasCapability($permission = 'read', $exit = self::SECURE_ISSUE_DIE)
|
||||
{
|
||||
$capability = apply_filters('wpfront_user_role_editor_duplicator_translate_capability', $permission);
|
||||
|
||||
if (!current_user_can($capability)) {
|
||||
$exitMsg = __('You do not have sufficient permissions to access this page.', 'duplicator');
|
||||
DUP_LOG::Trace('You do not have sufficient permissions to access this page. PERMISSION: ' . $permission);
|
||||
|
||||
switch ($exit) {
|
||||
case self::SECURE_ISSUE_THROW:
|
||||
throw new Exception($exitMsg);
|
||||
case self::SECURE_ISSUE_RETURN:
|
||||
return false;
|
||||
case self::SECURE_ISSUE_DIE:
|
||||
default:
|
||||
wp_die($exitMsg);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the owner of the current PHP script
|
||||
*
|
||||
* @return string The name of the owner of the current PHP script
|
||||
*/
|
||||
public static function getCurrentUser()
|
||||
{
|
||||
$unreadable = 'Undetectable';
|
||||
if (function_exists('get_current_user') && is_callable('get_current_user')) {
|
||||
$user = get_current_user();
|
||||
return strlen($user) ? $user : $unreadable;
|
||||
}
|
||||
return $unreadable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the owner of the PHP process
|
||||
*
|
||||
* @return string Gets the owner of the PHP process
|
||||
*/
|
||||
public static function getProcessOwner()
|
||||
{
|
||||
$unreadable = 'Undetectable';
|
||||
$user = '';
|
||||
try {
|
||||
if (function_exists('exec')) {
|
||||
$user = @exec('whoami');
|
||||
}
|
||||
|
||||
if (!strlen($user) && function_exists('posix_getpwuid') && function_exists('posix_geteuid')) {
|
||||
$user = posix_getpwuid(posix_geteuid());
|
||||
$user = $user['name'];
|
||||
}
|
||||
|
||||
return strlen($user) ? $user : $unreadable;
|
||||
} catch (Exception $ex) {
|
||||
return $unreadable;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the snapshot directory if it doesn't already exist
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function initSnapshotDirectory()
|
||||
{
|
||||
$error = false;
|
||||
|
||||
$path_wproot = duplicator_get_abs_path();
|
||||
$path_ssdir = DUP_Settings::getSsdirPath();
|
||||
|
||||
if (!file_exists($path_ssdir)) {
|
||||
$old_root_perm = @fileperms($path_wproot);
|
||||
|
||||
//--------------------------------
|
||||
//CHMOD DIRECTORY ACCESS
|
||||
//wordpress root directory
|
||||
SnapIO::chmod($path_wproot, 'u+rwx');
|
||||
|
||||
//snapshot directory
|
||||
if (SnapIO::dirWriteCheckOrMkdir($path_ssdir, 'u+rwx,go+rx') == false) {
|
||||
$error = true;
|
||||
}
|
||||
|
||||
// restore original root perms
|
||||
SnapIO::chmod($path_wproot, $old_root_perm);
|
||||
|
||||
if ($error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
SnapIO::chmod($path_ssdir, 'u+rwx,go+rx');
|
||||
SnapIO::dirWriteCheckOrMkdir(DUP_Settings::getSsdirTmpPath(), 'u+rwx');
|
||||
SnapIO::createSilenceIndex(DUP_Settings::getSsdirTmpPath());
|
||||
|
||||
// Logs directory
|
||||
SnapIO::dirWriteCheckOrMkdir(DUP_Settings::getSsdirLogsPath(), 'u+rwx');
|
||||
SnapIO::createSilenceIndex(DUP_Settings::getSsdirLogsPath());
|
||||
|
||||
//--------------------------------
|
||||
//FILE CREATION & HARDEN PROCESS
|
||||
//index.php, .htaccess, robots.txt
|
||||
SnapIO::createSilenceIndex(DUP_Settings::getSsdirPath());
|
||||
self::setupBackupDirRobotsFile();
|
||||
self::setupBackupDirHtaccess();
|
||||
self::performHardenProcesses();
|
||||
// For old installations
|
||||
SnapIO::createSilenceIndex(DUP_Settings::getSsdirInstallerPath());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to create a secure .htaccess file in the download directory
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function setupBackupDirHtaccess()
|
||||
{
|
||||
try {
|
||||
$storageHtaccessOff = DUP_Settings::Get('storage_htaccess_off');
|
||||
$fileName = DUP_Settings::getSsdirPath() . '/.htaccess';
|
||||
|
||||
if ($storageHtaccessOff) {
|
||||
@unlink($fileName);
|
||||
} elseif (!file_exists($fileName)) {
|
||||
$fileContent = <<<HTACCESS
|
||||
# Duplicator config, In case of file downloading problem, you can disable/enable it at Duplicator > Settings > Storage > Apache .htaccess
|
||||
|
||||
Options -Indexes
|
||||
<Files *.php>
|
||||
deny from all
|
||||
</Files>
|
||||
<IfModule mod_headers.c>
|
||||
<FilesMatch "\.(daf)$">
|
||||
ForceType application/octet-stream
|
||||
Header set Content-Disposition attachment
|
||||
</FilesMatch>
|
||||
</IfModule>
|
||||
HTACCESS;
|
||||
if (file_put_contents($fileName, $fileContent) === false) {
|
||||
throw new Exception('Can\'t create .haccess');
|
||||
}
|
||||
}
|
||||
} catch (Exception $ex) {
|
||||
DUP_Log::Trace("Unable create file htaccess {$fileName} msg:" . $ex->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to create a robots.txt file in the backups directory
|
||||
* to prevent search engines
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function setupBackupDirRobotsFile()
|
||||
{
|
||||
try {
|
||||
$fileName = DUP_Settings::getSsdirPath() . '/robots.txt';
|
||||
if (!file_exists($fileName)) {
|
||||
$fileContent = <<<HTACCESS
|
||||
User-agent: *
|
||||
Disallow: /
|
||||
HTACCESS;
|
||||
if (file_put_contents($fileName, $fileContent) === false) {
|
||||
throw new Exception('Can\'t create .haccess');
|
||||
}
|
||||
}
|
||||
} catch (Exception $ex) {
|
||||
DUP_Log::Trace("Unable create robots.txt {$fileName} msg:" . $ex->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Run various secure processes to harden the backups dir
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function performHardenProcesses()
|
||||
{
|
||||
$backupsDir = DUP_Settings::getSsdirPath();
|
||||
|
||||
//Edge Case: Remove any main.installer.php files
|
||||
$dupInstallFile = "{$backupsDir}/dup-installer/main.installer.php";
|
||||
if (file_exists($dupInstallFile)) {
|
||||
SnapIO::chmod($dupInstallFile, "a+w");
|
||||
SnapIO::unlink("{$dupInstallFile}");
|
||||
}
|
||||
|
||||
//Rename installer php files to .bak
|
||||
SnapIO::regexGlobCallback(
|
||||
$backupsDir,
|
||||
function ($path) {
|
||||
$parts = pathinfo($path);
|
||||
$newPath = $parts['dirname'] . '/' . $parts['filename'] . DUP_Installer::INSTALLER_SERVER_EXTENSION;
|
||||
SnapIO::rename($path, $newPath);
|
||||
},
|
||||
array(
|
||||
'regexFile' => '/^.+_installer.*\.php$/',
|
||||
'regexFolder' => false,
|
||||
'recursive' => true,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to get the file zip path on a users system
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public static function getZipPath()
|
||||
{
|
||||
$filepath = null;
|
||||
|
||||
if (self::hasShellExec()) {
|
||||
if (shell_exec('hash zip 2>&1') == null) {
|
||||
$filepath = 'zip';
|
||||
} else {
|
||||
$possible_paths = array(
|
||||
'/usr/bin/zip',
|
||||
'/opt/local/bin/zip'
|
||||
//'C:/Program\ Files\ (x86)/GnuWin32/bin/zip.exe');
|
||||
);
|
||||
|
||||
foreach ($possible_paths as $path) {
|
||||
if (@file_exists($path)) {
|
||||
$filepath = $path;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $filepath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the server PHP 5.3 or better
|
||||
*
|
||||
* @return bool Returns true if the server PHP 5.3 or better
|
||||
*/
|
||||
public static function PHP53()
|
||||
{
|
||||
return version_compare(PHP_VERSION, '5.3.2', '>=');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of the WordPress core tables.
|
||||
*
|
||||
* @return array Returns all WP core tables
|
||||
*/
|
||||
public static function getWPCoreTables()
|
||||
{
|
||||
global $wpdb;
|
||||
$result = array();
|
||||
foreach (self::getWPCoreTablesEnd() as $tend) {
|
||||
$result[] = $wpdb->prefix . $tend;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public static function getWPCoreTablesEnd()
|
||||
{
|
||||
return array(
|
||||
'commentmeta',
|
||||
'comments',
|
||||
'links',
|
||||
'options',
|
||||
'postmeta',
|
||||
'posts',
|
||||
'term_relationships',
|
||||
'term_taxonomy',
|
||||
'termmeta',
|
||||
'terms',
|
||||
'usermeta',
|
||||
'blogs',
|
||||
'blog_versions',
|
||||
'blogmeta',
|
||||
'users',
|
||||
'site',
|
||||
'sitemeta',
|
||||
'signups',
|
||||
'registration_log',
|
||||
'blog_versions');
|
||||
}
|
||||
|
||||
public static function isWPCoreTable($table)
|
||||
{
|
||||
global $wpdb;
|
||||
|
||||
if (strpos($table, $wpdb->prefix) !== 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$subTName = substr($table, strlen($wpdb->prefix));
|
||||
$coreEnds = self::getWPCoreTablesEnd();
|
||||
|
||||
if (in_array($subTName, $coreEnds)) {
|
||||
return true;
|
||||
} elseif (is_multisite()) {
|
||||
$exTable = explode('_', $subTName);
|
||||
if (count($exTable) >= 2 && is_numeric($exTable[0])) {
|
||||
$tChekc = implode('_', array_slice($exTable, 1));
|
||||
if (get_blog_details((int) $exTable[0], false) !== false && in_array($tChekc, $coreEnds)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function getWPBlogIdTable($table)
|
||||
{
|
||||
global $wpdb;
|
||||
|
||||
if (!is_multisite() || strpos($table, $wpdb->prefix) !== 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$subTName = substr($table, strlen($wpdb->prefix));
|
||||
$exTable = explode('_', $subTName);
|
||||
if (count($exTable) >= 2 && is_numeric($exTable[0]) && get_blog_details((int) $exTable[0], false) !== false) {
|
||||
return (int) $exTable[0];
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check given table is exist in real
|
||||
*
|
||||
* @param $table string Table name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isTableExists($table)
|
||||
{
|
||||
global $wpdb;
|
||||
// It will clear the $GLOBALS['wpdb']->last_error var
|
||||
$wpdb->flush();
|
||||
$table = esc_sql($table);
|
||||
$sql = "SELECT 1 FROM `{$table}` LIMIT 1;";
|
||||
|
||||
$wpdb->get_var($sql);
|
||||
if (empty($wpdb->last_error)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds if its a valid executable or not
|
||||
*
|
||||
* @param string $cmd $exe A non zero length executable path to find if that is executable or not.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function isExecutable($cmd)
|
||||
{
|
||||
if (strlen($cmd) < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (@is_executable($cmd)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$output = shell_exec($cmd);
|
||||
if (!is_null($output)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$output = shell_exec($cmd . ' -?');
|
||||
if (!is_null($output)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display human readable byte sizes
|
||||
*
|
||||
* @param string $size The size in bytes
|
||||
*
|
||||
* @return string Human readable bytes such as 50MB, 1GB
|
||||
*/
|
||||
public static function readableByteSize($size)
|
||||
{
|
||||
try {
|
||||
$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";
|
||||
}
|
||||
}
|
||||
|
||||
public static function getTablePrefix()
|
||||
{
|
||||
global $wpdb;
|
||||
$tablePrefix = (is_multisite() && is_plugin_active_for_network('duplicator/duplicator.php')) ? $wpdb->base_prefix : $wpdb->prefix;
|
||||
return $tablePrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* return ini disable functions array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getIniDisableFuncs()
|
||||
{
|
||||
if (is_null(self::$iniDisableFuncs)) {
|
||||
$tmpFuncs = ini_get('disable_functions');
|
||||
$tmpFuncs = explode(',', $tmpFuncs);
|
||||
self::$iniDisableFuncs = array();
|
||||
foreach ($tmpFuncs as $cFunc) {
|
||||
self::$iniDisableFuncs[] = trim($cFunc);
|
||||
}
|
||||
}
|
||||
|
||||
return self::$iniDisableFuncs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if function exists and isn't in ini disable_functions
|
||||
*
|
||||
* @param string $function_name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isIniFunctionEnalbe($function_name)
|
||||
{
|
||||
return function_exists($function_name) && !in_array($function_name, self::getIniDisableFuncs());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
||||
/**
|
||||
* Recursivly scans a directory and finds all sym-links and unreadable files
|
||||
*
|
||||
* 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
|
||||
if (! defined('DUPLICATOR_VERSION')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class DUP_ScanCheck
|
||||
{
|
||||
/**
|
||||
* The number of files scanned
|
||||
*/
|
||||
public $fileCount = 0;
|
||||
/**
|
||||
* The number of directories scanned
|
||||
*/
|
||||
public $dirCount = 0;
|
||||
/**
|
||||
* The maximum count of files before the recursive function stops
|
||||
*/
|
||||
public $maxFiles = 1000000;
|
||||
/**
|
||||
* The maximum count of directories before the recursive function stops
|
||||
*/
|
||||
public $maxDirs = 75000;
|
||||
/**
|
||||
* Recursivly scan the root directory provided
|
||||
*/
|
||||
public $recursion = true;
|
||||
/**
|
||||
* Stores a list of symbolic link files
|
||||
*/
|
||||
public $symLinks = array();
|
||||
/**
|
||||
* Stores a list of files unreadable by PHP
|
||||
*/
|
||||
public $unreadable = array();
|
||||
/**
|
||||
* Stores a list of dirs with utf8 settings
|
||||
*/
|
||||
public $nameTestDirs = array();
|
||||
/**
|
||||
* Stores a list of files with utf8 settings
|
||||
*/
|
||||
public $nameTestFiles = array();
|
||||
/**
|
||||
* If the maxFiles or maxDirs limit is reached then true
|
||||
*/
|
||||
protected $limitReached = false;
|
||||
/**
|
||||
* Is the server running on Windows
|
||||
*/
|
||||
private $isWindows = false;
|
||||
/**
|
||||
* Init this instance of the object
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->isWindows = defined('PHP_WINDOWS_VERSION_BUILD');
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the scan process
|
||||
*
|
||||
* @param string $dir A valid directory path where the scan will run
|
||||
* @param array $results Used for recursion, do not pass in value with calling
|
||||
*
|
||||
* @return obj The scan check object with the results of the scan
|
||||
*/
|
||||
public function run($dir, &$results = array())
|
||||
{
|
||||
//Stop Recursion if Max search is reached
|
||||
if ($this->fileCount > $this->maxFiles || $this->dirCount > $this->maxDirs) {
|
||||
$this->limitReached = true;
|
||||
return $results;
|
||||
}
|
||||
|
||||
$files = @scandir($dir);
|
||||
if (is_array($files)) {
|
||||
foreach ($files as $key => $value) {
|
||||
$path = realpath($dir . DIRECTORY_SEPARATOR . $value);
|
||||
if ($path) {
|
||||
//Files
|
||||
if (!is_dir($path)) {
|
||||
if (!is_readable($path)) {
|
||||
$results[] = $path;
|
||||
$this->unreadable[] = $path;
|
||||
} elseif ($this->isLink($path)) {
|
||||
$results[] = $path;
|
||||
$this->symLinks[] = $path;
|
||||
} else {
|
||||
$name = basename($path);
|
||||
$invalid_test = preg_match('/(\/|\*|\?|\>|\<|\:|\\|\|)/', $name)
|
||||
|| trim($name) == ''
|
||||
|| (strrpos($name, '.') == strlen($name) - 1 && substr($name, -1) == '.')
|
||||
|| preg_match('/[^\x20-\x7f]/', $name);
|
||||
if ($invalid_test) {
|
||||
if (! DUP_Util::$PHP7_plus && DUP_Util::isWindows()) {
|
||||
$this->nameTestFiles[] = utf8_decode($path);
|
||||
} else {
|
||||
$this->nameTestFiles[] = $path;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->fileCount++;
|
||||
} elseif ($value != "." && $value != "..") {
|
||||
//Dirs
|
||||
if (!$this->isLink($path) && $this->recursion) {
|
||||
$this->Run($path, $results);
|
||||
}
|
||||
|
||||
if (!is_readable($path)) {
|
||||
$results[] = $path;
|
||||
$this->unreadable[] = $path;
|
||||
} elseif ($this->isLink($path)) {
|
||||
$results[] = $path;
|
||||
$this->symLinks[] = $path;
|
||||
} else {
|
||||
$invalid_test = strlen($path) > 244
|
||||
|| trim($path) == ''
|
||||
|| preg_match('/[^\x20-\x7f]/', $path);
|
||||
if ($invalid_test) {
|
||||
if (! DUP_Util::$PHP7_plus && DUP_Util::isWindows()) {
|
||||
$this->nameTestDirs[] = utf8_decode($path);
|
||||
} else {
|
||||
$this->nameTestDirs[] = $path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->dirCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Separation logic for supporting how different operating systems work
|
||||
*
|
||||
* @param string $target A valid file path
|
||||
*
|
||||
* @return bool Is the target a sym link
|
||||
*/
|
||||
private function isLink($target)
|
||||
{
|
||||
//Currently Windows does not support sym-link detection
|
||||
if ($this->isWindows) {
|
||||
return false;
|
||||
} elseif (is_link($target)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
||||
// Exit if accessed directly
|
||||
if (! defined('DUPLICATOR_VERSION')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class DUP_Shell_U
|
||||
{
|
||||
/**
|
||||
* Escape a string to be used as a shell argument with bypass support for Windows
|
||||
*
|
||||
* NOTES:
|
||||
* Provides a way to support shell args on Windows OS and allows %,! on Windows command line
|
||||
* Safe if input is know such as a defined constant and not from user input escape shellarg
|
||||
* on Windows with turn %,! into spaces
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function escapeshellargWindowsSupport($string)
|
||||
{
|
||||
if (strncasecmp(PHP_OS, 'WIN', 3) == 0) {
|
||||
if (strstr($string, '%') || strstr($string, '!')) {
|
||||
$result = '"' . str_replace('"', '', $string) . '"';
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
return escapeshellarg($string);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function isPopenEnabled()
|
||||
{
|
||||
|
||||
if (!DUP_Util::isIniFunctionEnalbe('popen') || !DUP_Util::isIniFunctionEnalbe('proc_open')) {
|
||||
$ret = false;
|
||||
} else {
|
||||
$ret = true;
|
||||
}
|
||||
|
||||
$ret = apply_filters('duplicator_is_popen_enabled', $ret);
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
||||
/**
|
||||
* Utility class working with strings
|
||||
*
|
||||
* Standard: PSR-2
|
||||
*
|
||||
* @link http://www.php-fig.org/psr/psr-2
|
||||
*
|
||||
* @package DUP
|
||||
* @subpackage classes/utilities
|
||||
* @copyright (c) 2017, Snapcreek LLC
|
||||
* @license https://opensource.org/licenses/GPL-3.0 GNU Public License
|
||||
*/
|
||||
class DUP_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 null
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,226 @@
|
||||
<?php
|
||||
|
||||
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
||||
/**
|
||||
* 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
|
||||
if (!defined('DUPLICATOR_VERSION')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class DUP_Validator
|
||||
{
|
||||
/**
|
||||
* @var array $patterns
|
||||
*/
|
||||
private static $patterns = array(
|
||||
'fdir' => '/^([a-zA-Z]:[\\\\\/]|\/|\\\\\\\\|\/\/)[^<>\0]+$/',
|
||||
'ffile' => '/^([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_FOLDER = 'fdir';
|
||||
const FILTER_VALIDATE_FILE_EXT = 'fext';
|
||||
const FILTER_VALIDATE_EMAIL = 'email';
|
||||
/**
|
||||
* @var array $errors [ ['key' => string field key,
|
||||
* 'msg' => error message ] , [] ]
|
||||
*/
|
||||
private $errors = array();
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->errors = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->errors = array();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isSuccess()
|
||||
{
|
||||
return empty($this->errors);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getErrors()
|
||||
{
|
||||
return $this->errors;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return array return errors messages
|
||||
*/
|
||||
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 string
|
||||
*/
|
||||
public function getErrorsFormat($format = "%s\n", $echo = true)
|
||||
{
|
||||
$msgs = $this->getErrorsMsg();
|
||||
ob_start();
|
||||
foreach ($msgs as $msg) {
|
||||
printf($format, $msg);
|
||||
}
|
||||
|
||||
if ($echo) {
|
||||
ob_end_flush();
|
||||
return '';
|
||||
} else {
|
||||
return ob_get_clean();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $msg
|
||||
*/
|
||||
protected 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
|
||||
* @param int $filter
|
||||
* @param array $options
|
||||
*
|
||||
* @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;
|
||||
$result = 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'], $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
|
||||
* @param string $filter
|
||||
* @param array $options
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
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
|
||||
* @param string $delimiter
|
||||
* @param string $filter
|
||||
* @param array $options
|
||||
*/
|
||||
public function explode_filter_custom($variable, $delimiter, $filter, $options = array())
|
||||
{
|
||||
if (empty($variable)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$vals = explode($delimiter, trim($variable, $delimiter));
|
||||
$res = array();
|
||||
foreach ($vals as $val) {
|
||||
$res[] = $this->filter_custom($val, $filter, $options);
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
|
||||
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
||||
/**
|
||||
* Utility class for zipping up content
|
||||
*
|
||||
* Standard: PSR-2
|
||||
*
|
||||
* @link http://www.php-fig.org/psr/psr-2
|
||||
*
|
||||
* @subpackage classes/utilities
|
||||
* @copyright (c) 2017, Snapcreek LLC
|
||||
* @license https://opensource.org/licenses/GPL-3.0 GNU Public License
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if (! defined('DUPLICATOR_VERSION')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class DUP_Zip_U
|
||||
{
|
||||
/**
|
||||
* Add a directory to an existing ZipArchive object
|
||||
*
|
||||
* @param ZipArchive $zipArchive An existing ZipArchive object
|
||||
* @param string $directoryPath The full directory path to add to the ZipArchive
|
||||
* @param bool $retainDirectory Should the full directory path be retained in the archive
|
||||
*
|
||||
* @return bool Returns true if the directory was added to the object
|
||||
*/
|
||||
public static function addDirWithZipArchive($zipArchive, $directoryPath, $retainDirectory, $localPrefix, $isCompressed)
|
||||
{
|
||||
$directoryPath = rtrim(str_replace("\\", '/', $directoryPath), '/') . '/';
|
||||
if (!is_dir($directoryPath) || !is_readable($directoryPath)) {
|
||||
$success = false;
|
||||
} elseif (!$fp = @opendir($directoryPath)) {
|
||||
$success = false;
|
||||
} else {
|
||||
$success = true;
|
||||
while (false !== ($file = readdir($fp))) {
|
||||
if ($file === '.' || $file === '..') {
|
||||
continue;
|
||||
}
|
||||
$objectPath = $directoryPath . $file;
|
||||
// Not used SnapIO::safePath(), because I would like to decrease max_nest_level
|
||||
// Otherwise we will get the error:
|
||||
// PHP Fatal error: Uncaught Error: Maximum function nesting level of '512' reached, aborting! in ...
|
||||
// $objectPath = SnapIO::safePath($objectPath);
|
||||
$localName = ltrim(str_replace($directoryPath, '', $objectPath), '/');
|
||||
if ($retainDirectory) {
|
||||
$localName = basename($directoryPath) . "/$localName";
|
||||
}
|
||||
$localName = ltrim($localPrefix . $localName, '/');
|
||||
if (is_readable($objectPath)) {
|
||||
if (is_dir($objectPath)) {
|
||||
$localPrefixArg = substr($localName, 0, strrpos($localName, '/')) . '/';
|
||||
$added = self::addDirWithZipArchive($zipArchive, $objectPath, $retainDirectory, $localPrefixArg, $isCompressed);
|
||||
} else {
|
||||
$added = self::addFileToZipArchive($zipArchive, $objectPath, $localName, $isCompressed);
|
||||
}
|
||||
} else {
|
||||
$added = false;
|
||||
}
|
||||
|
||||
if (!$added) {
|
||||
DUP_Log::error("Couldn't add file $objectPath to archive", '', false);
|
||||
$success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@closedir($fp);
|
||||
}
|
||||
|
||||
if ($success) {
|
||||
return true;
|
||||
} else {
|
||||
DUP_Log::error("Couldn't add folder $directoryPath to archive", '', false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function extractFiles($archiveFilepath, $relativeFilesToExtract, $destinationDirectory, $useShellUnZip)
|
||||
{
|
||||
// TODO: Unzip using either shell unzip or ziparchive
|
||||
if ($useShellUnZip) {
|
||||
$shellExecPath = escapeshellcmd(DUPX_Server::get_unzip_filepath());
|
||||
$escapedFiles = array_map('escapeshellarg', $relativeFilesToExtract);
|
||||
$filenameString = implode(' ', $escapedFiles);
|
||||
$safeArchivePath = escapeshellarg($archiveFilepath);
|
||||
$safeDestination = escapeshellarg($destinationDirectory);
|
||||
$command = "{$shellExecPath} -o -qq {$safeArchivePath} {$filenameString} -d {$safeDestination} 2>&1";
|
||||
$stderr = shell_exec($command);
|
||||
if ($stderr != '') {
|
||||
$errorMessage = __("Error extracting {$archiveFilepath}): {$stderr}", 'duplicator');
|
||||
throw new Exception($errorMessage);
|
||||
}
|
||||
} else {
|
||||
$zipArchive = new ZipArchive();
|
||||
$result = $zipArchive->open($archiveFilepath);
|
||||
if ($result !== true) {
|
||||
throw new Exception("Error opening {$archiveFilepath} when extracting.");
|
||||
}
|
||||
|
||||
$result = $zipArchive->extractTo($destinationDirectory, $relativeFilesToExtract);
|
||||
if ($result === false) {
|
||||
throw new Exception("Error extracting {$archiveFilepath}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a directory to an existing ZipArchive object
|
||||
*
|
||||
* @param string $sourceFilePath The file to add to the zip file
|
||||
* @param string $zipFilePath The zip file to be added to
|
||||
* @param bool $deleteOld Delete the zip file before adding a file
|
||||
* @param string $newName Rename the $sourceFile if needed
|
||||
*
|
||||
* @return bool Returns true if the file was added to the zip file
|
||||
*/
|
||||
public static function zipFile($sourceFilePath, $zipFilePath, $deleteOld, $newName, $isCompressed)
|
||||
{
|
||||
if ($deleteOld && file_exists($zipFilePath)) {
|
||||
SnapIO::unlink($zipFilePath);
|
||||
}
|
||||
|
||||
if (file_exists($sourceFilePath)) {
|
||||
$zip_archive = new ZipArchive();
|
||||
$is_zip_open = ($zip_archive->open($zipFilePath, ZIPARCHIVE::CREATE) === true);
|
||||
if ($is_zip_open === false) {
|
||||
DUP_Log::error("Cannot create zip archive {$zipFilePath}");
|
||||
} else {
|
||||
//ADD SQL
|
||||
if ($newName == null) {
|
||||
$source_filename = basename($sourceFilePath);
|
||||
DUP_Log::Info("adding {$source_filename}");
|
||||
} else {
|
||||
$source_filename = $newName;
|
||||
DUP_Log::Info("new name added {$newName}");
|
||||
}
|
||||
|
||||
$in_zip = DUP_Zip_U::addFileToZipArchive($zip_archive, $sourceFilePath, $source_filename, $isCompressed);
|
||||
if ($in_zip === false) {
|
||||
DUP_Log::error("Unable to add {$sourceFilePath} to $zipFilePath");
|
||||
}
|
||||
|
||||
$zip_archive->close();
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
DUP_Log::error("Trying to add {$sourceFilePath} to a zip but it doesn't exist!");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function addFileToZipArchive($zipArchive, $filepath, $localName, $isCompressed)
|
||||
{
|
||||
$added = $zipArchive->addFile($filepath, $localName);
|
||||
return $added;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user