forked from LiveCarta/LiveCartaWP
Changed source root directory
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
class DUP_Archive_Config
|
||||
{
|
||||
public $dup_type = 'lite';
|
||||
public $created = '';
|
||||
public $version_dup = '';
|
||||
public $version_wp = '';
|
||||
public $version_db = '';
|
||||
public $version_php = '';
|
||||
public $version_os = '';
|
||||
public $blogname = '';
|
||||
public $exportOnlyDB = false;
|
||||
public $secure_on = 0;
|
||||
public $secure_pass = '';
|
||||
public $dbhost = null;
|
||||
public $dbname = null;
|
||||
public $dbuser = null;
|
||||
public $cpnl_host = null;
|
||||
public $cpnl_user = null;
|
||||
public $cpnl_pass = null;
|
||||
public $cpnl_enable = null;
|
||||
public $wp_tableprefix = '';
|
||||
public $mu_mode = 0;
|
||||
public $mu_generation = 0;
|
||||
public $mu_siteadmins = array();
|
||||
public $subsites = array();
|
||||
public $main_site_id = 1;
|
||||
public $mu_is_filtered = false;
|
||||
public $license_limit = 0;
|
||||
public $license_type = 0;
|
||||
public $dbInfo = array();
|
||||
public $packInfo = array();
|
||||
public $fileInfo = array();
|
||||
public $wpInfo = array();
|
||||
/** @var int<-1,max> */
|
||||
public $defaultStorageId = -1;
|
||||
/** @var string[] */
|
||||
public $components = array();
|
||||
/** @var string[] */
|
||||
public $opts_delete = array();
|
||||
/** @var array<string, mixed> */
|
||||
public $brand = array();
|
||||
/** @var array<string, mixed> */
|
||||
public $overwriteInstallerParams = array();
|
||||
/** @var string */
|
||||
public $installer_base_name = '';
|
||||
/** @var string */
|
||||
public $installer_backup_name = '';
|
||||
/** @var string */
|
||||
public $package_name = '';
|
||||
/** @var string */
|
||||
public $package_hash = '';
|
||||
/** @var string */
|
||||
public $package_notes = '';
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
345
html/wp-content/plugins/duplicator/classes/class.db.php
Normal file
345
html/wp-content/plugins/duplicator/classes/class.db.php
Normal file
@@ -0,0 +1,345 @@
|
||||
<?php
|
||||
|
||||
use Duplicator\Libs\Snap\SnapOS;
|
||||
|
||||
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
||||
/**
|
||||
* Lightweight abstraction layer for common simple database routines
|
||||
*
|
||||
* 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_DB extends wpdb
|
||||
{
|
||||
const BUILD_MODE_MYSQLDUMP = 'MYSQLDUMP';
|
||||
const BUILD_MODE_PHP_SINGLE_THREAD = 'PHP';
|
||||
|
||||
const MAX_TABLE_COUNT_IN_PACKET = 100;
|
||||
public static $remove_placeholder_escape_exists = null;
|
||||
|
||||
public static function init()
|
||||
{
|
||||
global $wpdb;
|
||||
self::$remove_placeholder_escape_exists = method_exists($wpdb, 'remove_placeholder_escape');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the requested MySQL system variable
|
||||
*
|
||||
* @param string $name The database variable name to lookup
|
||||
*
|
||||
* @return string the server variable to query for
|
||||
*/
|
||||
public static function getVariable($name)
|
||||
{
|
||||
global $wpdb;
|
||||
if (strlen($name)) {
|
||||
$query = $wpdb->prepare("SHOW VARIABLES LIKE %s", $name);
|
||||
$row = $wpdb->get_row($query, ARRAY_N);
|
||||
return isset($row[1]) ? $row[1] : null;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value of lower_case_table_names
|
||||
*
|
||||
* @see https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_lower_case_table_names
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function getLowerCaseTableNames()
|
||||
{
|
||||
if (($result = self::getVariable("lower_case_table_names")) === null) {
|
||||
if (SnapOS::isOSX()) {
|
||||
return 2;
|
||||
} elseif (SnapOS::isWindows()) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return (int) $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the MySQL database version number
|
||||
*
|
||||
* @param bool $full True: Gets the full version
|
||||
* False: Gets only the numeric portion i.e. 5.5.6 or 10.1.2 (for MariaDB)
|
||||
*
|
||||
* @return false|string 0 on failure, version number on success
|
||||
*/
|
||||
public static function getVersion($full = false)
|
||||
{
|
||||
global $wpdb;
|
||||
if ($full) {
|
||||
$version = self::getVariable('version');
|
||||
} else {
|
||||
$version = preg_replace('/[^0-9.].*/', '', self::getVariable('version'));
|
||||
}
|
||||
|
||||
//Fall-back for servers that have restricted SQL for SHOW statement
|
||||
if (empty($version)) {
|
||||
$version = $wpdb->db_version();
|
||||
}
|
||||
|
||||
return empty($version) ? 0 : $version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to return the mysqldump path on Windows servers
|
||||
*
|
||||
* @return boolean|string
|
||||
*/
|
||||
public static function getWindowsMySqlDumpRealPath()
|
||||
{
|
||||
if (function_exists('php_ini_loaded_file')) {
|
||||
$get_php_ini_path = php_ini_loaded_file();
|
||||
if (file_exists($get_php_ini_path)) {
|
||||
$search = array(
|
||||
dirname(dirname($get_php_ini_path)) . '/mysql/bin/mysqldump.exe',
|
||||
dirname(dirname(dirname($get_php_ini_path))) . '/mysql/bin/mysqldump.exe',
|
||||
dirname(dirname($get_php_ini_path)) . '/mysql/bin/mysqldump',
|
||||
dirname(dirname(dirname($get_php_ini_path))) . '/mysql/bin/mysqldump',
|
||||
);
|
||||
foreach ($search as $mysqldump) {
|
||||
if (file_exists($mysqldump)) {
|
||||
return str_replace("\\", "/", $mysqldump);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unset($search);
|
||||
unset($get_php_ini_path);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the correct database build mode PHP, MYSQLDUMP, PHPCHUNKING
|
||||
*
|
||||
* @return string Returns a string with one of theses three values PHP, MYSQLDUMP, PHPCHUNKING
|
||||
*/
|
||||
public static function getBuildMode()
|
||||
{
|
||||
$package_mysqldump = DUP_Settings::Get('package_mysqldump');
|
||||
$mysqlDumpPath = DUP_DB::getMySqlDumpPath();
|
||||
return ($mysqlDumpPath && $package_mysqldump) ? self::BUILD_MODE_MYSQLDUMP : self::BUILD_MODE_PHP_SINGLE_THREAD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mysqldump path if the server is enabled to execute it otherwise false
|
||||
*
|
||||
* @return boolean|string
|
||||
*/
|
||||
public static function getMySqlDumpPath()
|
||||
{
|
||||
//Is shell_exec possible
|
||||
if (!DUP_Util::hasShellExec()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$custom_mysqldump_path = DUP_Settings::Get('package_mysqldump_path');
|
||||
$custom_mysqldump_path = (strlen($custom_mysqldump_path)) ? $custom_mysqldump_path : '';
|
||||
$custom_mysqldump_path = escapeshellcmd($custom_mysqldump_path);
|
||||
|
||||
// Common Windows Paths
|
||||
if (DUP_Util::isWindows()) {
|
||||
$paths = array(
|
||||
$custom_mysqldump_path,
|
||||
'mysqldump.exe',
|
||||
self::getWindowsMySqlDumpRealPath(),
|
||||
'C:/xampp/mysql/bin/mysqldump.exe',
|
||||
'C:/Program Files/xampp/mysql/bin/mysqldump',
|
||||
'C:/Program Files/MySQL/MySQL Server 6.0/bin/mysqldump',
|
||||
'C:/Program Files/MySQL/MySQL Server 5.5/bin/mysqldump',
|
||||
'C:/Program Files/MySQL/MySQL Server 5.4/bin/mysqldump'
|
||||
);
|
||||
// Common Linux Paths
|
||||
} else {
|
||||
$paths = array(
|
||||
$custom_mysqldump_path,
|
||||
'mysqldump',
|
||||
'/usr/local/bin/mysqldump',
|
||||
'/usr/local/mysql/bin/mysqldump',
|
||||
'/usr/mysql/bin/mysqldump',
|
||||
'/usr/bin/mysqldump',
|
||||
'/opt/local/lib/mysql6/bin/mysqldump',
|
||||
'/opt/local/lib/mysql5/bin/mysqldump',
|
||||
'/usr/bin/mysqldump',
|
||||
);
|
||||
}
|
||||
|
||||
$exec_available = function_exists('exec');
|
||||
foreach ($paths as $path) {
|
||||
if (@file_exists($path)) {
|
||||
if (DUP_Util::isExecutable($path)) {
|
||||
return $path;
|
||||
}
|
||||
} elseif ($exec_available) {
|
||||
$out = array();
|
||||
$rc = -1;
|
||||
$cmd = $path . ' --help';
|
||||
@exec($cmd, $out, $rc);
|
||||
if ($rc === 0) {
|
||||
return $path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Sql query to create table which is given.
|
||||
*
|
||||
* @param string $table Table name
|
||||
*
|
||||
* @return string mysql query create table
|
||||
*/
|
||||
private static function getCreateTableQuery($table)
|
||||
{
|
||||
$row = $GLOBALS['wpdb']->get_row('SHOW CREATE TABLE `' . esc_sql($table) . '`', ARRAY_N);
|
||||
return $row[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all collation types that are assigned to the tables in
|
||||
* the current database. Each element in the array is unique
|
||||
*
|
||||
* @param array $tables A list of tables to include from the search
|
||||
*
|
||||
* @return array Returns an array with all the character set being used
|
||||
*/
|
||||
public static function getTableCharSetList($tables)
|
||||
{
|
||||
$charSets = array();
|
||||
try {
|
||||
foreach ($tables as $table) {
|
||||
$createTableQuery = self::getCreateTableQuery($table);
|
||||
if (preg_match('/ CHARSET=([^\s;]+)/i', $createTableQuery, $charsetMatch)) {
|
||||
if (!in_array($charsetMatch[1], $charSets)) {
|
||||
$charSets[] = $charsetMatch[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $charSets;
|
||||
} catch (Exception $ex) {
|
||||
return $charSets;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all collation types that are assigned to the tables and columns table in
|
||||
* the current database. Each element in the array is unique
|
||||
*
|
||||
* @param string[] $tablesToInclude A list of tables to include in the search.
|
||||
*
|
||||
* @return string[] Returns an array with all the collation types being used
|
||||
*/
|
||||
public static function getTableCollationList($tablesToInclude)
|
||||
{
|
||||
global $wpdb;
|
||||
static $collations = null;
|
||||
if (is_null($collations)) {
|
||||
$collations = array();
|
||||
//use half the number of tables since we are using them twice
|
||||
foreach (array_chunk($tablesToInclude, self::MAX_TABLE_COUNT_IN_PACKET) as $tablesChunk) {
|
||||
$sqlTables = implode(",", array_map(array(__CLASS__, 'escValueToQueryString'), $tablesChunk));
|
||||
//UNION is by default DISTINCT
|
||||
$query = "SELECT `COLLATION_NAME` FROM `information_schema`.`columns` WHERE `COLLATION_NAME` IS NOT NULL AND `table_schema` = '{$wpdb->dbname}' "
|
||||
. "AND `table_name` in (" . $sqlTables . ")"
|
||||
. "UNION SELECT `TABLE_COLLATION` FROM `information_schema`.`tables` WHERE `TABLE_COLLATION` IS NOT NULL AND `table_schema` = '{$wpdb->dbname}' "
|
||||
. "AND `table_name` in (" . $sqlTables . ")";
|
||||
if (!$wpdb->query($query)) {
|
||||
DUP_Log::Info("GET TABLE COLLATION ERROR: " . $wpdb->last_error);
|
||||
continue;
|
||||
}
|
||||
|
||||
$collations = array_unique(array_merge($collations, $wpdb->get_col()));
|
||||
}
|
||||
$collations = array_values($collations);
|
||||
sort($collations);
|
||||
}
|
||||
return $collations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of MySQL engines used by $tablesToInclude in the current DB
|
||||
*
|
||||
* @param string[] $tablesToInclude tables to check the engines for
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public static function getTableEngineList($tablesToInclude)
|
||||
{
|
||||
global $wpdb;
|
||||
static $engines = null;
|
||||
if (is_null($engines)) {
|
||||
$engines = array();
|
||||
foreach (array_chunk($tablesToInclude, self::MAX_TABLE_COUNT_IN_PACKET) as $tablesChunk) {
|
||||
$query = "SELECT DISTINCT `ENGINE` FROM `information_schema`.`tables` WHERE `ENGINE` IS NOT NULL AND `table_schema` = '{$wpdb->dbname}' "
|
||||
. "AND `table_name` in (" . implode(",", array_map(array(__CLASS__, 'escValueToQueryString'), $tablesChunk)) . ")";
|
||||
if (!$wpdb->query($query)) {
|
||||
DUP_Log::info("GET TABLE ENGINES ERROR: " . $wpdb->last_error);
|
||||
}
|
||||
|
||||
$engines = array_merge($engines, $wpdb->get_col($query));
|
||||
}
|
||||
$engines = array_values(array_unique($engines));
|
||||
}
|
||||
|
||||
return $engines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an escaped SQL string
|
||||
*
|
||||
* @param string $sql The SQL to escape
|
||||
* @param bool $removePlaceholderEscape Patch for how the default WP function works.
|
||||
*
|
||||
* @return boolean|string
|
||||
* @also see: https://make.wordpress.org/core/2017/10/31/changed-behaviour-of-esc_sql-in-wordpress-4-8-3/
|
||||
*/
|
||||
public static function escSQL($sql, $removePlaceholderEscape = false)
|
||||
{
|
||||
global $wpdb;
|
||||
$removePlaceholderEscape = $removePlaceholderEscape && self::$remove_placeholder_escape_exists;
|
||||
if ($removePlaceholderEscape) {
|
||||
return $wpdb->remove_placeholder_escape(@esc_sql($sql));
|
||||
} else {
|
||||
return @esc_sql($sql);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function escape sql string without add and remove remove_placeholder_escape
|
||||
* Don't use esc_sql wordpress function
|
||||
*
|
||||
* @param null|scalar $value input value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function escValueToQueryString($value)
|
||||
{
|
||||
if (is_null($value)) {
|
||||
return 'NULL';
|
||||
}
|
||||
global $wpdb;
|
||||
return '"' . mysqli_real_escape_string($wpdb->dbh, (string) $value) . '"';
|
||||
}
|
||||
}
|
||||
20
html/wp-content/plugins/duplicator/classes/class.io.php
Normal file
20
html/wp-content/plugins/duplicator/classes/class.io.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @copyright 2022 Snap Creek LLC
|
||||
* Class for all IO operations
|
||||
*/
|
||||
|
||||
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
||||
if (! defined('DUPLICATOR_VERSION')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is deprecated. Please use:
|
||||
* Duplicator\Libs\Snap\SnapIO
|
||||
*/
|
||||
class DUP_IO
|
||||
{
|
||||
/* !!DO NOT IMPLMENT HERE!! */
|
||||
}
|
||||
558
html/wp-content/plugins/duplicator/classes/class.logging.php
Normal file
558
html/wp-content/plugins/duplicator/classes/class.logging.php
Normal file
@@ -0,0 +1,558 @@
|
||||
<?php
|
||||
|
||||
use Duplicator\Libs\Snap\SnapUtil;
|
||||
|
||||
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
||||
// Exit if accessed directly
|
||||
if (!defined('DUPLICATOR_VERSION')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper Class for logging
|
||||
*
|
||||
* @package Duplicator\classes
|
||||
*/
|
||||
abstract class Dup_ErrorBehavior
|
||||
{
|
||||
const LogOnly = 0;
|
||||
const ThrowException = 1;
|
||||
const Quit = 2;
|
||||
}
|
||||
|
||||
class DUP_Log
|
||||
{
|
||||
/**
|
||||
* The file handle used to write to the package log file
|
||||
*/
|
||||
private static $logFileHandle = null;
|
||||
|
||||
/**
|
||||
* Get the setting which indicates if tracing is enabled
|
||||
*/
|
||||
private static $traceEnabled = false;
|
||||
|
||||
public static $profileLogs = null;
|
||||
|
||||
/**
|
||||
* Init this static object
|
||||
*/
|
||||
public static function Init()
|
||||
{
|
||||
self::$traceEnabled = (DUP_Settings::Get('trace_log_enabled') == 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a log file connection for writing to the package log file
|
||||
*
|
||||
* @param string $nameHas The Name of the log file to create
|
||||
*
|
||||
* @return nul
|
||||
*/
|
||||
public static function Open($nameHash)
|
||||
{
|
||||
if (!isset($nameHash)) {
|
||||
throw new Exception("A name value is required to open a file log.");
|
||||
}
|
||||
self::Close();
|
||||
if ((self::$logFileHandle = @fopen(DUP_Settings::getSsdirLogsPath() . "/{$nameHash}.log", "a+")) === false) {
|
||||
self::$logFileHandle = null;
|
||||
return false;
|
||||
} else {
|
||||
/**
|
||||
* By initializing the error_handler on opening the log, I am sure that when a package is processed, the handler is active.
|
||||
*/
|
||||
DUP_Handler::init_error_handler();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the package log file connection if is opened
|
||||
*
|
||||
* @return bool Returns TRUE on success or FALSE on failure.
|
||||
*/
|
||||
public static function Close()
|
||||
{
|
||||
$result = true;
|
||||
|
||||
if (!is_null(self::$logFileHandle)) {
|
||||
$result = @fclose(self::$logFileHandle);
|
||||
self::$logFileHandle = null;
|
||||
} else {
|
||||
$result = true;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* General information send to the package log if opened
|
||||
*
|
||||
* REPLACE TO DEBUG: Memory consumption as script runs
|
||||
* $results = DUP_Util::byteSize(memory_get_peak_usage(true)) . "\t" . $msg;
|
||||
*
|
||||
* @fwrite(self::$logFileHandle, "{$results} \n");
|
||||
*
|
||||
* @param string $msg The message to log
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public static function Info($msg)
|
||||
{
|
||||
if (self::$traceEnabled) {
|
||||
self::Trace($msg);
|
||||
}
|
||||
if (!is_null(self::$logFileHandle)) {
|
||||
@fwrite(self::$logFileHandle, $msg . "\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* General information send to the package log and trace log
|
||||
*
|
||||
* @param string $msg The message to log
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public static function infoTrace($msg, $calling_function_override = null)
|
||||
{
|
||||
self::Info($msg);
|
||||
self::Trace($msg, $calling_function_override);
|
||||
}
|
||||
|
||||
public static function print_r_info($val, $name = '')
|
||||
{
|
||||
$msg = empty($name) ? '' : 'VALUE ' . $name . ': ';
|
||||
$msg .= print_r($val, true);
|
||||
self::info($msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the trace file exists
|
||||
*
|
||||
* @return bool Returns true if an active trace file exists
|
||||
*/
|
||||
public static function TraceFileExists()
|
||||
{
|
||||
$file_path = self::getTraceFilepath();
|
||||
return file_exists($file_path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current file size of the active trace file
|
||||
*
|
||||
* @return string Returns a human readable file size of the active trace file
|
||||
*/
|
||||
public static function getTraceStatus()
|
||||
{
|
||||
$file_path = DUP_Log::getTraceFilepath();
|
||||
$backup_path = DUP_Log::getBackupTraceFilepath();
|
||||
|
||||
if (file_exists($file_path)) {
|
||||
$filesize = is_file($file_path) ? @filesize($file_path) : 0;
|
||||
|
||||
//Its possible mulitple trace log files exist due to size
|
||||
if (is_file($backup_path)) {
|
||||
$filesize += @filesize($backup_path);
|
||||
}
|
||||
|
||||
$message = sprintf('%1$s', DUP_Util::byteSize($filesize));
|
||||
} else {
|
||||
$message = esc_html__('No Log', 'duplicator');
|
||||
}
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
// RSR TODO: Swap trace logic out for real trace later
|
||||
public static function Trace($message, $calling_function_override = null)
|
||||
{
|
||||
|
||||
if (self::$traceEnabled) {
|
||||
$unique_id = sprintf("%08x", abs(crc32($_SERVER['REMOTE_ADDR'] . $_SERVER['REQUEST_TIME'] . $_SERVER['REMOTE_PORT'])));
|
||||
|
||||
if ($calling_function_override == null) {
|
||||
$calling_function = SnapUtil::getCallingFunctionName();
|
||||
} else {
|
||||
$calling_function = $calling_function_override;
|
||||
}
|
||||
|
||||
if (is_object($message)) {
|
||||
$ov = get_object_vars($message);
|
||||
$message = print_r($ov, true);
|
||||
} elseif (is_array($message)) {
|
||||
$message = print_r($message, true);
|
||||
}
|
||||
|
||||
$logging_message = "{$unique_id}|{$calling_function} | {$message}";
|
||||
$ticks = time() + ((int) get_option('gmt_offset') * 3600);
|
||||
$formatted_time = date('d-m-H:i:s', $ticks);
|
||||
$formatted_logging_message = "{$formatted_time}|DUP|{$logging_message} \r\n";
|
||||
|
||||
// Always write to error log - if they don't want the info they can turn off WordPress error logging or tracing
|
||||
self::ErrLog($logging_message);
|
||||
|
||||
// Everything goes to the plugin log, whether it's part of package generation or not.
|
||||
self::WriteToTrace($formatted_logging_message);
|
||||
}
|
||||
}
|
||||
|
||||
public static function print_r_trace($val, $name = '', $calling_function_override = null)
|
||||
{
|
||||
$msg = empty($name) ? '' : 'VALUE ' . $name . ': ';
|
||||
$msg .= print_r($val, true);
|
||||
self::trace($msg, $calling_function_override);
|
||||
}
|
||||
|
||||
public static function errLog($message)
|
||||
{
|
||||
$message = 'DUP:' . $message;
|
||||
SnapUtil::errorLog($message);
|
||||
}
|
||||
|
||||
public static function TraceObject($msg, $o, $log_private_members = true)
|
||||
{
|
||||
if (self::$traceEnabled) {
|
||||
if (!$log_private_members) {
|
||||
$o = get_object_vars($o);
|
||||
}
|
||||
self::Trace($msg . ':' . print_r($o, true));
|
||||
}
|
||||
}
|
||||
|
||||
public static function GetDefaultKey()
|
||||
{
|
||||
$auth_key = defined('AUTH_KEY') ? AUTH_KEY : 'atk';
|
||||
$auth_key .= defined('DB_HOST') ? DB_HOST : 'dbh';
|
||||
$auth_key .= defined('DB_NAME') ? DB_NAME : 'dbn';
|
||||
$auth_key .= defined('DB_USER') ? DB_USER : 'dbu';
|
||||
return hash('md5', $auth_key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current file size of the old trace file "1"
|
||||
*
|
||||
* @return string Returns a human readable file size of the active trace file
|
||||
*/
|
||||
public static function GetBackupTraceFilepath()
|
||||
{
|
||||
$default_key = self::getDefaultKey();
|
||||
$backup_log_filename = "dup_$default_key.log1";
|
||||
$backup_path = DUP_Settings::getSsdirLogsPath() . "/" . $backup_log_filename;
|
||||
return $backup_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the active trace file path
|
||||
*
|
||||
* @return string Returns the full path to the active trace file
|
||||
*/
|
||||
public static function GetTraceFilepath()
|
||||
{
|
||||
$default_key = self::getDefaultKey();
|
||||
$log_filename = "dup_$default_key.log";
|
||||
$file_path = DUP_Settings::getSsdirLogsPath() . "/" . $log_filename;
|
||||
return $file_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the trace log and backup trace log files
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public static function DeleteTraceLog()
|
||||
{
|
||||
$file_path = self::GetTraceFilepath();
|
||||
$backup_path = self::GetBackupTraceFilepath();
|
||||
self::trace("deleting $file_path");
|
||||
@unlink($file_path);
|
||||
self::trace("deleting $backup_path");
|
||||
@unlink($backup_path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an error is detected and no further processing should occur
|
||||
*
|
||||
* @param string $msg The message to log
|
||||
* @param string $detail Additional details to help resolve the issue if possible
|
||||
* @param int $behavior
|
||||
*/
|
||||
public static function error($msg, $detail = '', $behavior = Dup_ErrorBehavior::Quit)
|
||||
{
|
||||
|
||||
SnapUtil::errorLog($msg . ' DETAIL:' . $detail);
|
||||
$source = self::getStack(debug_backtrace());
|
||||
|
||||
$err_msg = "\n==================================================================================\n";
|
||||
$err_msg .= "DUPLICATOR ERROR\n";
|
||||
$err_msg .= "Please try again! If the error persists see the Duplicator 'Help' menu.\n";
|
||||
$err_msg .= "---------------------------------------------------------------------------------\n";
|
||||
$err_msg .= "MESSAGE:\n\t{$msg}\n";
|
||||
if (strlen($detail)) {
|
||||
$err_msg .= "DETAILS:\n\t{$detail}\n";
|
||||
}
|
||||
$err_msg .= "TRACE:\n{$source}";
|
||||
$err_msg .= "==================================================================================\n\n";
|
||||
@fwrite(self::$logFileHandle, "{$err_msg}");
|
||||
|
||||
switch ($behavior) {
|
||||
case Dup_ErrorBehavior::ThrowException:
|
||||
DUP_LOG::trace("throwing exception");
|
||||
throw new Exception($msg);
|
||||
break;
|
||||
|
||||
case Dup_ErrorBehavior::Quit:
|
||||
DUP_LOG::trace("quitting");
|
||||
die("DUPLICATOR ERROR: Please see the 'Backup Log' file link below.");
|
||||
break;
|
||||
|
||||
default:
|
||||
// Nothing
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The current stack trace of a PHP call
|
||||
*
|
||||
* @param $stacktrace The current debug stack
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getStack($stacktrace)
|
||||
{
|
||||
$output = "";
|
||||
$i = 1;
|
||||
foreach ($stacktrace as $node) {
|
||||
$output .= "\t $i. " . basename($node['file']) . " : " . $node['function'] . " (" . $node['line'] . ")\n";
|
||||
$i++;
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages writing the active or backup log based on the size setting
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
private static function WriteToTrace($formatted_logging_message)
|
||||
{
|
||||
$log_filepath = self::GetTraceFilepath();
|
||||
|
||||
if (@file_exists($log_filepath) && @filesize($log_filepath) > DUPLICATOR_MAX_LOG_SIZE) {
|
||||
$backup_log_filepath = self::GetBackupTraceFilepath();
|
||||
|
||||
if (file_exists($backup_log_filepath)) {
|
||||
if (@unlink($backup_log_filepath) === false) {
|
||||
self::errLog("Couldn't delete backup log $backup_log_filepath");
|
||||
}
|
||||
}
|
||||
|
||||
if (@rename($log_filepath, $backup_log_filepath) === false) {
|
||||
self::errLog("Couldn't rename log $log_filepath to $backup_log_filepath");
|
||||
}
|
||||
}
|
||||
|
||||
if (@file_put_contents($log_filepath, $formatted_logging_message, FILE_APPEND) === false) {
|
||||
// Not en error worth reporting
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DUP_Handler
|
||||
{
|
||||
const MODE_OFF = 0; // don't write in log
|
||||
const MODE_LOG = 1; // write errors in log file
|
||||
const MODE_VAR = 2; // put php errors in $varModeLog static var
|
||||
const SHUTDOWN_TIMEOUT = 'tm';
|
||||
|
||||
/**
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private static $initialized = false;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $shutdownReturns = array(
|
||||
'tm' => 'timeout'
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private static $handlerMode = self::MODE_LOG;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var bool // print code reference and errno at end of php error line [CODE:10|FILE:test.php|LINE:100]
|
||||
*/
|
||||
private static $codeReference = true;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var bool // print prefix in php error line [PHP ERR][WARN] MSG: .....
|
||||
*/
|
||||
private static $errPrefix = true;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var string // php errors in MODE_VAR
|
||||
*/
|
||||
private static $varModeLog = '';
|
||||
|
||||
/**
|
||||
* This function only initializes the error handler the first time it is called
|
||||
*/
|
||||
public static function init_error_handler()
|
||||
{
|
||||
if (!self::$initialized) {
|
||||
@set_error_handler(array(__CLASS__, 'error'));
|
||||
@register_shutdown_function(array(__CLASS__, 'shutdown'));
|
||||
self::$initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Error handler
|
||||
*
|
||||
* @param integer $errno Error level
|
||||
* @param string $errstr Error message
|
||||
* @param string $errfile Error file
|
||||
* @param integer $errline Error line
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function error($errno, $errstr, $errfile, $errline)
|
||||
{
|
||||
switch (self::$handlerMode) {
|
||||
case self::MODE_OFF:
|
||||
if ($errno == E_ERROR) {
|
||||
$log_message = self::getMessage($errno, $errstr, $errfile, $errline);
|
||||
DUP_Log::error($log_message);
|
||||
}
|
||||
break;
|
||||
case self::MODE_VAR:
|
||||
self::$varModeLog .= self::getMessage($errno, $errstr, $errfile, $errline) . "\n";
|
||||
break;
|
||||
case self::MODE_LOG:
|
||||
default:
|
||||
switch ($errno) {
|
||||
case E_ERROR:
|
||||
$log_message = self::getMessage($errno, $errstr, $errfile, $errline);
|
||||
DUP_Log::error($log_message);
|
||||
break;
|
||||
case E_NOTICE:
|
||||
case E_WARNING:
|
||||
default:
|
||||
$log_message = self::getMessage($errno, $errstr, $errfile, $errline);
|
||||
DUP_Log::Info($log_message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static function getMessage($errno, $errstr, $errfile, $errline)
|
||||
{
|
||||
$result = '';
|
||||
|
||||
if (self::$errPrefix) {
|
||||
$result = '[PHP ERR]';
|
||||
switch ($errno) {
|
||||
case E_ERROR:
|
||||
$result .= '[FATAL]';
|
||||
break;
|
||||
case E_WARNING:
|
||||
$result .= '[WARN]';
|
||||
break;
|
||||
case E_NOTICE:
|
||||
$result .= '[NOTICE]';
|
||||
break;
|
||||
default:
|
||||
$result .= '[ISSUE]';
|
||||
break;
|
||||
}
|
||||
$result .= ' MSG:';
|
||||
}
|
||||
|
||||
$result .= $errstr;
|
||||
|
||||
if (self::$codeReference) {
|
||||
$result .= ' [CODE:' . $errno . '|FILE:' . $errfile . '|LINE:' . $errline . ']';
|
||||
$result .= "\n" . wp_debug_backtrace_summary();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* if setMode is called without params set as default
|
||||
*
|
||||
* @param int $mode
|
||||
* @param bool $errPrefix // print prefix in php error line [PHP ERR][WARN] MSG: .....
|
||||
* @param bool $codeReference // print code reference and errno at end of php error line [CODE:10|FILE:test.php|LINE:100]
|
||||
*/
|
||||
public static function setMode($mode = self::MODE_LOG, $errPrefix = true, $codeReference = true)
|
||||
{
|
||||
switch ($mode) {
|
||||
case self::MODE_OFF:
|
||||
case self::MODE_VAR:
|
||||
self::$handlerMode = $mode;
|
||||
break;
|
||||
case self::MODE_LOG:
|
||||
default:
|
||||
self::$handlerMode = self::MODE_LOG;
|
||||
}
|
||||
|
||||
self::$varModeLog = '';
|
||||
self::$errPrefix = $errPrefix;
|
||||
self::$codeReference = $codeReference;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return string // return var log string in MODE_VAR
|
||||
*/
|
||||
public static function getVarLog()
|
||||
{
|
||||
return self::$varModeLog;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return string // return var log string in MODE_VAR and clean var
|
||||
*/
|
||||
public static function getVarLogClean()
|
||||
{
|
||||
$result = self::$varModeLog;
|
||||
self::$varModeLog = '';
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $status // timeout
|
||||
* @param string
|
||||
*/
|
||||
public static function setShutdownReturn($status, $str)
|
||||
{
|
||||
self::$shutdownReturns[$status] = $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown handler
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function shutdown()
|
||||
{
|
||||
if (($error = error_get_last())) {
|
||||
if (preg_match('/^Maximum execution time (?:.+) exceeded$/i', $error['message'])) {
|
||||
echo self::$shutdownReturns[self::SHUTDOWN_TIMEOUT];
|
||||
}
|
||||
self::error($error['type'], $error['message'], $error['file'], $error['line']);
|
||||
}
|
||||
}
|
||||
}
|
||||
235
html/wp-content/plugins/duplicator/classes/class.password.php
Normal file
235
html/wp-content/plugins/duplicator/classes/class.password.php
Normal file
@@ -0,0 +1,235 @@
|
||||
<?php
|
||||
|
||||
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
||||
#
|
||||
# Portable PHP password hashing framework.
|
||||
#
|
||||
# Version 0.5 / genuine.
|
||||
#
|
||||
# Written by Solar Designer <solar at openwall.com> in 2004-2006 and placed in
|
||||
# the public domain. Revised in subsequent years, still public domain.
|
||||
#
|
||||
# There's absolutely no warranty.
|
||||
#
|
||||
# The homepage URL for this framework is:
|
||||
#
|
||||
# http://www.openwall.com/phpass/
|
||||
#
|
||||
# Please be sure to update the Version line if you edit this file in any way.
|
||||
# It is suggested that you leave the main version number intact, but indicate
|
||||
# your project name (after the slash) and add your own revision information.
|
||||
#
|
||||
# Please do not change the "private" password hashing method implemented in
|
||||
# here, thereby making your hashes incompatible. However, if you must, please
|
||||
# change the hash type identifier (the "$P$") to something different.
|
||||
#
|
||||
# Obviously, since this code is in the public domain, the above are not
|
||||
# requirements (there can be none), but merely suggestions.
|
||||
#
|
||||
|
||||
// Exit if accessed directly
|
||||
if (! defined('DUPLICATOR_VERSION')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class DUP_PasswordHash
|
||||
{
|
||||
public $itoa64;
|
||||
public $iteration_count_log2;
|
||||
public $portable_hashes;
|
||||
public $random_state;
|
||||
|
||||
public function __construct($iteration_count_log2, $portable_hashes)
|
||||
{
|
||||
$this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
||||
if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31) {
|
||||
$iteration_count_log2 = 8;
|
||||
}
|
||||
$this->iteration_count_log2 = $iteration_count_log2;
|
||||
$this->portable_hashes = $portable_hashes;
|
||||
$this->random_state = microtime();
|
||||
if (function_exists('getmypid')) {
|
||||
$this->random_state .= getmypid();
|
||||
}
|
||||
}
|
||||
|
||||
public function PasswordHash($iteration_count_log2, $portable_hashes)
|
||||
{
|
||||
self::__construct($iteration_count_log2, $portable_hashes);
|
||||
}
|
||||
|
||||
public function get_random_bytes($count)
|
||||
{
|
||||
$output = '';
|
||||
if (
|
||||
@is_readable('/dev/urandom') &&
|
||||
($fh = @fopen('/dev/urandom', 'rb'))
|
||||
) {
|
||||
$output = fread($fh, $count);
|
||||
fclose($fh);
|
||||
}
|
||||
|
||||
if (strlen($output) < $count) {
|
||||
$output = '';
|
||||
for ($i = 0; $i < $count; $i += 16) {
|
||||
$this->random_state =
|
||||
md5(microtime() . $this->random_state);
|
||||
$output .= md5($this->random_state, true);
|
||||
}
|
||||
$output = substr($output, 0, $count);
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
public function encode64($input, $count)
|
||||
{
|
||||
$output = '';
|
||||
$i = 0;
|
||||
do {
|
||||
$value = ord($input[$i++]);
|
||||
$output .= $this->itoa64[$value & 0x3f];
|
||||
if ($i < $count) {
|
||||
$value |= ord($input[$i]) << 8;
|
||||
}
|
||||
$output .= $this->itoa64[($value >> 6) & 0x3f];
|
||||
if ($i++ >= $count) {
|
||||
break;
|
||||
}
|
||||
if ($i < $count) {
|
||||
$value |= ord($input[$i]) << 16;
|
||||
}
|
||||
$output .= $this->itoa64[($value >> 12) & 0x3f];
|
||||
if ($i++ >= $count) {
|
||||
break;
|
||||
}
|
||||
$output .= $this->itoa64[($value >> 18) & 0x3f];
|
||||
} while ($i < $count);
|
||||
return $output;
|
||||
}
|
||||
|
||||
public function gensalt_private($input)
|
||||
{
|
||||
$output = '$P$';
|
||||
$output .= $this->itoa64[min($this->iteration_count_log2 +
|
||||
((PHP_VERSION >= '5') ? 5 : 3), 30)];
|
||||
$output .= $this->encode64($input, 6);
|
||||
return $output;
|
||||
}
|
||||
|
||||
public function crypt_private($password, $setting)
|
||||
{
|
||||
$output = '*0';
|
||||
if (substr($setting, 0, 2) === $output) {
|
||||
$output = '*1';
|
||||
}
|
||||
|
||||
$id = substr($setting, 0, 3);
|
||||
# We use "$P$", phpBB3 uses "$H$" for the same thing
|
||||
if ($id !== '$P$' && $id !== '$H$') {
|
||||
return $output;
|
||||
}
|
||||
|
||||
$count_log2 = strpos($this->itoa64, $setting[3]);
|
||||
if ($count_log2 < 7 || $count_log2 > 30) {
|
||||
return $output;
|
||||
}
|
||||
|
||||
$count = 1 << $count_log2;
|
||||
$salt = substr($setting, 4, 8);
|
||||
if (strlen($salt) !== 8) {
|
||||
return $output;
|
||||
}
|
||||
|
||||
# We were kind of forced to use MD5 here since it's the only
|
||||
# cryptographic primitive that was available in all versions
|
||||
# of PHP in use. To implement our own low-level crypto in PHP
|
||||
# would have resulted in much worse performance and
|
||||
# consequently in lower iteration counts and hashes that are
|
||||
# quicker to crack (by non-PHP code).
|
||||
$hash = md5($salt . $password, true);
|
||||
do {
|
||||
$hash = md5($hash . $password, true);
|
||||
} while (--$count);
|
||||
$output = substr($setting, 0, 12);
|
||||
$output .= $this->encode64($hash, 16);
|
||||
return $output;
|
||||
}
|
||||
|
||||
public function gensalt_blowfish($input)
|
||||
{
|
||||
# This one needs to use a different order of characters and a
|
||||
# different encoding scheme from the one in encode64() above.
|
||||
# We care because the last character in our encoded string will
|
||||
# only represent 2 bits. While two known implementations of
|
||||
# bcrypt will happily accept and correct a salt string which
|
||||
# has the 4 unused bits set to non-zero, we do not want to take
|
||||
# chances and we also do not want to waste an additional byte
|
||||
# of entropy.
|
||||
$itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
$output = '$2a$';
|
||||
$output .= chr(ord('0') + (int)($this->iteration_count_log2 / 10));
|
||||
$output .= chr(ord('0') + $this->iteration_count_log2 % 10);
|
||||
$output .= '$';
|
||||
$i = 0;
|
||||
do {
|
||||
$c1 = ord($input[$i++]);
|
||||
$output .= $itoa64[$c1 >> 2];
|
||||
$c1 = ($c1 & 0x03) << 4;
|
||||
if ($i >= 16) {
|
||||
$output .= $itoa64[$c1];
|
||||
break;
|
||||
}
|
||||
|
||||
$c2 = ord($input[$i++]);
|
||||
$c1 |= $c2 >> 4;
|
||||
$output .= $itoa64[$c1];
|
||||
$c1 = ($c2 & 0x0f) << 2;
|
||||
$c2 = ord($input[$i++]);
|
||||
$c1 |= $c2 >> 6;
|
||||
$output .= $itoa64[$c1];
|
||||
$output .= $itoa64[$c2 & 0x3f];
|
||||
} while (1);
|
||||
return $output;
|
||||
}
|
||||
|
||||
public function HashPassword($password)
|
||||
{
|
||||
$random = '';
|
||||
if (CRYPT_BLOWFISH === 1 && !$this->portable_hashes) {
|
||||
$random = $this->get_random_bytes(16);
|
||||
$hash =
|
||||
crypt($password, $this->gensalt_blowfish($random));
|
||||
if (strlen($hash) === 60) {
|
||||
return $hash;
|
||||
}
|
||||
}
|
||||
|
||||
if (strlen($random) < 6) {
|
||||
$random = $this->get_random_bytes(6);
|
||||
}
|
||||
$hash =
|
||||
$this->crypt_private(
|
||||
$password,
|
||||
$this->gensalt_private($random)
|
||||
);
|
||||
if (strlen($hash) === 34) {
|
||||
return $hash;
|
||||
}
|
||||
|
||||
# Returning '*' on error is safe here, but would _not_ be safe
|
||||
# in a crypt(3)-like function used _both_ for generating new
|
||||
# hashes and for validating passwords against existing hashes.
|
||||
return '*';
|
||||
}
|
||||
|
||||
public function CheckPassword($password, $stored_hash)
|
||||
{
|
||||
$hash = $this->crypt_private($password, $stored_hash);
|
||||
if ($hash[0] === '*') {
|
||||
$hash = crypt($password, $stored_hash);
|
||||
}
|
||||
|
||||
return $hash === $stored_hash;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
*
|
||||
* @package Duplicator
|
||||
* @copyright (c) 2023, Snap Creek LLC
|
||||
*/
|
||||
|
||||
use Duplicator\Controllers\WelcomeController;
|
||||
use Duplicator\Core\Upgrade\UpgradeFunctions;
|
||||
|
||||
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
||||
|
||||
/**
|
||||
* Upgrade/Install logic of plugin resides here
|
||||
*/
|
||||
class DUP_LITE_Plugin_Upgrade
|
||||
{
|
||||
const DUP_VERSION_OPT_KEY = 'duplicator_version_plugin';
|
||||
const PLUGIN_INSTALL_INFO_OPT_KEY = 'duplicator_install_info';
|
||||
|
||||
/**
|
||||
* version starting from which the welcome page is shown
|
||||
*/
|
||||
const DUP_WELCOME_PAGE_VERSION = '1.5.3';
|
||||
|
||||
/**
|
||||
* wp_options key containing info about when the plugin was activated
|
||||
*/
|
||||
const DUP_ACTIVATED_OPT_KEY = 'duplicator_activated';
|
||||
|
||||
/**
|
||||
* Called as part of WordPress register_activation_hook
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function onActivationAction()
|
||||
{
|
||||
//NEW VS UPDATE
|
||||
if (($oldDupVersion = get_option(self::DUP_VERSION_OPT_KEY, false)) === false) {
|
||||
self::newInstallation();
|
||||
} else {
|
||||
self::updateInstallation($oldDupVersion);
|
||||
}
|
||||
|
||||
DUP_Settings::Save();
|
||||
|
||||
//Init Database & Backup Directories
|
||||
self::updateDatabase();
|
||||
DUP_Util::initSnapshotDirectory();
|
||||
|
||||
do_action('duplicator_after_activation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Update install info.
|
||||
*
|
||||
* @param string $oldVersion The last/previous installed version, is empty for new installs
|
||||
*
|
||||
* @return array{version:string,time:int,updateTime:int}
|
||||
*/
|
||||
protected static function setInstallInfo($oldVersion = '')
|
||||
{
|
||||
if (empty($oldVersion) || ($installInfo = get_option(self::PLUGIN_INSTALL_INFO_OPT_KEY, false)) === false) {
|
||||
// If is new installation or install info is not set generate new install info
|
||||
$installInfo = array(
|
||||
'version' => DUPLICATOR_VERSION,
|
||||
'time' => time(),
|
||||
'updateTime' => time(),
|
||||
);
|
||||
} else {
|
||||
$installInfo['updateTime'] = time();
|
||||
}
|
||||
|
||||
if (($oldInfos = get_option(self::DUP_ACTIVATED_OPT_KEY, false)) !== false) {
|
||||
// Migrate the previously used option to install info and remove old option if exists
|
||||
$installInfo['version'] = $oldVersion;
|
||||
$installInfo['time'] = $oldInfos['lite'];
|
||||
delete_option(self::DUP_ACTIVATED_OPT_KEY);
|
||||
}
|
||||
|
||||
delete_option(self::PLUGIN_INSTALL_INFO_OPT_KEY);
|
||||
update_option(self::PLUGIN_INSTALL_INFO_OPT_KEY, $installInfo, false);
|
||||
return $installInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get install info.
|
||||
*
|
||||
* @return array{version:string,time:int,updateTime:int}
|
||||
*/
|
||||
public static function getInstallInfo()
|
||||
{
|
||||
if (($installInfo = get_option(self::PLUGIN_INSTALL_INFO_OPT_KEY, false)) === false) {
|
||||
$installInfo = self::setInstallInfo();
|
||||
}
|
||||
return $installInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs only on new installs
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function newInstallation()
|
||||
{
|
||||
UpgradeFunctions::performUpgrade(false, DUPLICATOR_VERSION);
|
||||
|
||||
//WordPress Options Hooks
|
||||
update_option(self::DUP_VERSION_OPT_KEY, DUPLICATOR_VERSION);
|
||||
update_option(WelcomeController::REDIRECT_OPT_KEY, true);
|
||||
self::setInstallInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run only on update installs
|
||||
*
|
||||
* @param string $oldVersion The last/previous installed version
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function updateInstallation($oldVersion)
|
||||
{
|
||||
UpgradeFunctions::performUpgrade($oldVersion, DUPLICATOR_VERSION);
|
||||
|
||||
//WordPress Options Hooks
|
||||
update_option(self::DUP_VERSION_OPT_KEY, DUPLICATOR_VERSION);
|
||||
self::setInstallInfo($oldVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs for both new and update installs and creates the database tables
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function updateDatabase()
|
||||
{
|
||||
global $wpdb;
|
||||
|
||||
$charset_collate = $wpdb->get_charset_collate();
|
||||
$table_name = $wpdb->prefix . "duplicator_packages";
|
||||
|
||||
//PRIMARY KEY must have 2 spaces before for dbDelta to work
|
||||
//see: https://codex.wordpress.org/Creating_Tables_with_Plugins
|
||||
$sql = "CREATE TABLE `{$table_name}` (
|
||||
id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
name VARCHAR(250) NOT NULL,
|
||||
hash VARCHAR(50) NOT NULL,
|
||||
status INT(11) NOT NULL,
|
||||
created DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
|
||||
owner VARCHAR(60) NOT NULL,
|
||||
package LONGTEXT NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
KEY hash (hash)) {$charset_collate}";
|
||||
|
||||
$abs_path = duplicator_get_abs_path();
|
||||
require_once($abs_path . '/wp-admin/includes/upgrade.php');
|
||||
@dbDelta($sql);
|
||||
}
|
||||
}
|
||||
732
html/wp-content/plugins/duplicator/classes/class.server.php
Normal file
732
html/wp-content/plugins/duplicator/classes/class.server.php
Normal file
@@ -0,0 +1,732 @@
|
||||
<?php
|
||||
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
use Duplicator\Libs\Snap\SnapUtil;
|
||||
use Duplicator\Libs\Snap\SnapWP;
|
||||
|
||||
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
||||
require_once(DUPLICATOR_PLUGIN_PATH . 'classes/utilities/class.u.php');
|
||||
|
||||
/**
|
||||
* Used to get various pieces of information about the server environment
|
||||
*
|
||||
* Standard: PSR-2
|
||||
*
|
||||
* @link http://www.php-fig.org/psr/psr-2
|
||||
*
|
||||
* @package Duplicator
|
||||
* @subpackage classes/utilities
|
||||
* @copyright (c) 2017, Snapcreek LLC
|
||||
*/
|
||||
|
||||
class DUP_Server
|
||||
{
|
||||
const LockFileName = 'lockfile.txt';
|
||||
|
||||
// Possibly use in the future if we want to prevent double building
|
||||
public static function isEngineLocked()
|
||||
{
|
||||
if (self::setEngineLock(true)) {
|
||||
self::setEngineLock(false);
|
||||
$locked = false;
|
||||
} else {
|
||||
$locked = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Possibly use in the future if we want to prevent double building
|
||||
public static function setEngineLock($shouldLock)
|
||||
{
|
||||
$success = false;
|
||||
$locking_file = @fopen(self::LockFileName, 'c+');
|
||||
if ($locking_file != false) {
|
||||
if ($shouldLock) {
|
||||
$success = @flock($locking_file, LOCK_EX | LOCK_NB);
|
||||
} else {
|
||||
$success = @flock($locking_file, LOCK_UN);
|
||||
}
|
||||
|
||||
@fclose($locking_file);
|
||||
}
|
||||
return $success;
|
||||
}
|
||||
|
||||
public static function mysqlEscapeIsOk()
|
||||
{
|
||||
$escape_test_string = chr(0) . chr(26) . "\r\n'\"\\";
|
||||
$escape_expected_result = "\"\\0\Z\\r\\n\\'\\\"\\\\\"";
|
||||
$escape_actual_result = DUP_DB::escValueToQueryString($escape_test_string);
|
||||
$result = $escape_expected_result === $escape_actual_result;
|
||||
|
||||
if (!$result) {
|
||||
$msg = "mysqli_real_escape_string test results\n" .
|
||||
"Expected escape result: " . $escape_expected_result . "\n" .
|
||||
"Actual escape result: " . $escape_actual_result;
|
||||
DUP_Log::trace($msg);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the system requirements which must pass to build a package
|
||||
*
|
||||
* @return array An array of requirements
|
||||
*/
|
||||
public static function getRequirements()
|
||||
{
|
||||
$dup_tests = array();
|
||||
|
||||
//PHP SUPPORT
|
||||
$safe_ini = strtolower(ini_get('safe_mode'));
|
||||
$dup_tests['PHP']['SAFE_MODE'] = $safe_ini != 'on' || $safe_ini != 'yes' || $safe_ini != 'true' || ini_get("safe_mode") != 1 ? 'Pass' : 'Fail';
|
||||
self::logRequirementFail($dup_tests['PHP']['SAFE_MODE'], 'SAFE_MODE is on.');
|
||||
|
||||
$dup_tests['PHP']['VERSION'] = DUP_Util::$on_php_529_plus ? 'Pass' : 'Fail';
|
||||
$phpversion = phpversion();
|
||||
self::logRequirementFail($dup_tests['PHP']['VERSION'], 'PHP version(' . $phpversion . ') is lower than 5.2.9');
|
||||
|
||||
if (DUP_Settings::Get('archive_build_mode') == DUP_Archive_Build_Mode::ZipArchive) {
|
||||
$dup_tests['PHP']['ZIP'] = class_exists('ZipArchive') ? 'Pass' : 'Fail';
|
||||
self::logRequirementFail($dup_tests['PHP']['ZIP'], 'ZipArchive class doesn\'t exist.');
|
||||
}
|
||||
|
||||
$dup_tests['PHP']['FUNC_1'] = function_exists("file_get_contents") ? 'Pass' : 'Fail';
|
||||
self::logRequirementFail($dup_tests['PHP']['FUNC_1'], 'file_get_contents function doesn\'t exist.');
|
||||
|
||||
$dup_tests['PHP']['FUNC_2'] = function_exists("file_put_contents") ? 'Pass' : 'Fail';
|
||||
self::logRequirementFail($dup_tests['PHP']['FUNC_2'], 'file_put_contents function doesn\'t exist.');
|
||||
|
||||
$dup_tests['PHP']['FUNC_3'] = function_exists("mb_strlen") ? 'Pass' : 'Fail';
|
||||
self::logRequirementFail($dup_tests['PHP']['FUNC_3'], 'mb_strlen function doesn\'t exist.');
|
||||
|
||||
$dup_tests['PHP']['ALL'] = !in_array('Fail', $dup_tests['PHP']) ? 'Pass' : 'Fail';
|
||||
|
||||
//REQUIRED PATHS
|
||||
$abs_path = duplicator_get_abs_path();
|
||||
$handle_test = @opendir($abs_path);
|
||||
$dup_tests['IO']['WPROOT'] = is_writeable($abs_path) && $handle_test ? 'Pass' : 'Warn';
|
||||
@closedir($handle_test);
|
||||
self::logRequirementFail($dup_tests['IO']['WPROOT'], $abs_path . ' (abs path) can\'t be opened.');
|
||||
|
||||
$dup_tests['IO']['SSDIR'] = is_writeable(DUP_Settings::getSsdirPath()) ? 'Pass' : 'Fail';
|
||||
self::logRequirementFail($dup_tests['IO']['SSDIR'], DUP_Settings::getSsdirPath() . ' (DUPLICATOR_SSDIR_PATH) can\'t be writeable.');
|
||||
|
||||
$dup_tests['IO']['SSTMP'] = is_writeable(DUP_Settings::getSsdirTmpPath()) ? 'Pass' : 'Fail';
|
||||
self::logRequirementFail($dup_tests['IO']['SSTMP'], DUP_Settings::getSsdirTmpPath() . ' (DUPLICATOR_SSDIR_PATH_TMP) can\'t be writeable.');
|
||||
|
||||
$dup_tests['IO']['ALL'] = !in_array('Fail', $dup_tests['IO']) ? 'Pass' : 'Fail';
|
||||
|
||||
//SERVER SUPPORT
|
||||
$dup_tests['SRV']['MYSQLi'] = function_exists('mysqli_connect') ? 'Pass' : 'Fail';
|
||||
self::logRequirementFail($dup_tests['SRV']['MYSQLi'], 'mysqli_connect function doesn\'t exist.');
|
||||
|
||||
//mysqli_real_escape_string test
|
||||
$dup_tests['SRV']['MYSQL_ESC'] = self::mysqlEscapeIsOk() ? 'Pass' : 'Fail';
|
||||
self::logRequirementFail($dup_tests['SRV']['MYSQL_ESC'], "The function mysqli_real_escape_string is not escaping strings as expected.");
|
||||
|
||||
$db_version = DUP_DB::getVersion();
|
||||
$dup_tests['SRV']['MYSQL_VER'] = version_compare($db_version, '5.0', '>=') ? 'Pass' : 'Fail';
|
||||
self::logRequirementFail($dup_tests['SRV']['MYSQL_VER'], 'MySQL version ' . $db_version . ' is lower than 5.0.');
|
||||
|
||||
$dup_tests['SRV']['ALL'] = !in_array('Fail', $dup_tests['SRV']) ? 'Pass' : 'Fail';
|
||||
|
||||
//RESERVED FILES
|
||||
$dup_tests['RES']['INSTALL'] = !(self::hasInstallerFiles()) ? 'Pass' : 'Fail';
|
||||
self::logRequirementFail($dup_tests['RES']['INSTALL'], 'Installer file(s) are exist on the server.');
|
||||
$dup_tests['Success'] = $dup_tests['PHP']['ALL'] == 'Pass' && $dup_tests['IO']['ALL'] == 'Pass' && $dup_tests['SRV']['ALL'] == 'Pass' && $dup_tests['RES']['INSTALL'] == 'Pass';
|
||||
|
||||
$dup_tests['Warning'] = $dup_tests['IO']['WPROOT'] == 'Warn';
|
||||
|
||||
return $dup_tests;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs requirement fail status informative message
|
||||
*
|
||||
* @param string $testStatus Either it is Pass or Fail
|
||||
* @param string $errorMessage Error message which should be logged
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function logRequirementFail($testStatus, $errorMessage)
|
||||
{
|
||||
if (empty($testStatus)) {
|
||||
throw new Exception('Exception: Empty $testStatus [File: ' . __FILE__ . ', Ln: ' . __LINE__);
|
||||
}
|
||||
|
||||
if (empty($errorMessage)) {
|
||||
throw new Exception('Exception: Empty $errorMessage [File: ' . __FILE__ . ', Ln: ' . __LINE__);
|
||||
}
|
||||
|
||||
$validTestStatuses = array('Pass', 'Fail', 'Warn');
|
||||
|
||||
if (!in_array($testStatus, $validTestStatuses)) {
|
||||
throw new Exception('Exception: Invalid $testStatus value: ' . $testStatus . ' [File: ' . __FILE__ . ', Ln: ' . __LINE__);
|
||||
}
|
||||
|
||||
if ('Fail' == $testStatus) {
|
||||
DUP_LOG::trace($errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the system checks which are not required
|
||||
*
|
||||
* @return array An array of system checks
|
||||
*/
|
||||
public static function getChecks()
|
||||
{
|
||||
$checks = array();
|
||||
|
||||
//PHP/SYSTEM SETTINGS
|
||||
//Web Server
|
||||
$php_test0 = false;
|
||||
foreach ($GLOBALS['DUPLICATOR_SERVER_LIST'] as $value) {
|
||||
if (stristr($_SERVER['SERVER_SOFTWARE'], $value)) {
|
||||
$php_test0 = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
self::logCheckFalse($php_test0, 'Any out of server software (' . implode(', ', $GLOBALS['DUPLICATOR_SERVER_LIST']) . ') doesn\'t exist.');
|
||||
|
||||
$php_test1 = ini_get("open_basedir");
|
||||
$php_test1 = empty($php_test1) ? true : false;
|
||||
self::logCheckFalse($php_test1, 'open_basedir is enabled.');
|
||||
|
||||
$max_execution_time = ini_get("max_execution_time");
|
||||
$php_test2 = ($max_execution_time > DUPLICATOR_SCAN_TIMEOUT) || (strcmp($max_execution_time, 'Off') == 0 || $max_execution_time == 0) ? true : false;
|
||||
if (strcmp($max_execution_time, 'Off') == 0) {
|
||||
$max_execution_time_error_message = '$max_execution_time should not be' . $max_execution_time;
|
||||
} else {
|
||||
$max_execution_time_error_message = '$max_execution_time (' . $max_execution_time . ') should not be lower than the DUPLICATOR_SCAN_TIMEOUT' . DUPLICATOR_SCAN_TIMEOUT;
|
||||
}
|
||||
self::logCheckFalse($php_test2, $max_execution_time_error_message);
|
||||
|
||||
$php_test3 = function_exists('mysqli_connect');
|
||||
self::logCheckFalse($php_test3, 'mysqli_connect function doesn\'t exist.');
|
||||
|
||||
$php_test4 = DUP_Util::$on_php_53_plus ? true : false;
|
||||
self::logCheckFalse($php_test4, 'PHP Version is lower than 5.3.');
|
||||
|
||||
$checks['SRV']['PHP']['websrv'] = $php_test0;
|
||||
$checks['SRV']['PHP']['openbase'] = $php_test1;
|
||||
$checks['SRV']['PHP']['maxtime'] = $php_test2;
|
||||
$checks['SRV']['PHP']['mysqli'] = $php_test3;
|
||||
$checks['SRV']['PHP']['version'] = $php_test4;
|
||||
//MANAGED HOST
|
||||
$checks['SRV']['SYS']['managedHost'] = !DUP_Custom_Host_Manager::getInstance()->isManaged();
|
||||
$checks['SRV']['SYS']['ALL'] = ($php_test0 && $php_test1 && $php_test2 && $php_test3 && $php_test4 && $checks['SRV']['SYS']['managedHost']) ? 'Good' : 'Warn';
|
||||
|
||||
//WORDPRESS SETTINGS
|
||||
global $wp_version;
|
||||
$wp_test1 = version_compare($wp_version, DUPLICATOR_SCAN_MIN_WP) >= 0 ? true : false;
|
||||
self::logCheckFalse($wp_test1, 'WP version (' . $wp_version . ') is lower than the DUPLICATOR_SCAN_MIN_WP (' . DUPLICATOR_SCAN_MIN_WP . ').');
|
||||
|
||||
//Core Files
|
||||
$files = array();
|
||||
$proper_wp_config_file_path = SnapWP::getWPConfigPath();
|
||||
$files['wp-config.php'] = file_exists($proper_wp_config_file_path);
|
||||
self::logCheckFalse($files['wp-config.php'], 'The wp-config.php file doesn\'t exist on the ' . $proper_wp_config_file_path);
|
||||
|
||||
/* searching wp-config in working word press is not worthy
|
||||
* if this script is executing that means wp-config.php exists :)
|
||||
* we need to know the core folders and files added by the user at this point
|
||||
* retaining old logic as else for the case if its used some where else
|
||||
*/
|
||||
//Core dir and files logic
|
||||
if (isset($_POST['file_notice']) && isset($_POST['dir_notice'])) {
|
||||
//means if there are core directories excluded or core files excluded return false
|
||||
if ((bool) $_POST['file_notice'] || (bool) $_POST['dir_notice']) {
|
||||
$wp_test2 = false;
|
||||
} else {
|
||||
$wp_test2 = true;
|
||||
}
|
||||
} else {
|
||||
$wp_test2 = $files['wp-config.php'];
|
||||
}
|
||||
|
||||
//Cache
|
||||
/*
|
||||
$Package = DUP_Package::getActive();
|
||||
$cache_path = DUP_Util::safePath(WP_CONTENT_DIR) . '/cache';
|
||||
$dirEmpty = DUP_Util::isDirectoryEmpty($cache_path);
|
||||
$dirSize = DUP_Util::getDirectorySize($cache_path);
|
||||
$cach_filtered = in_array($cache_path, explode(';', $Package->Archive->FilterDirs));
|
||||
$wp_test3 = ($cach_filtered || $dirEmpty || $dirSize < DUPLICATOR_SCAN_CACHESIZE ) ? true : false;
|
||||
*/
|
||||
$wp_test3 = is_multisite();
|
||||
self::logCheckFalse($wp_test3, 'WP is multi-site setup.');
|
||||
|
||||
$checks['SRV']['WP']['version'] = $wp_test1;
|
||||
$checks['SRV']['WP']['core'] = $wp_test2;
|
||||
$checks['SRV']['WP']['ismu'] = $wp_test3;
|
||||
$checks['SRV']['WP']['ALL'] = $wp_test1 && $wp_test2 && !$wp_test3 ? 'Good' : 'Warn';
|
||||
|
||||
return $checks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs checks false informative message
|
||||
*
|
||||
* @param boolean $check Either it is true or false
|
||||
* @param string $errorMessage Error message which should be logged when check is false
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function logCheckFalse($check, $errorMessage)
|
||||
{
|
||||
if (empty($errorMessage)) {
|
||||
throw new Exception('Exception: Empty $errorMessage variable [File: ' . __FILE__ . ', Ln: ' . __LINE__);
|
||||
}
|
||||
|
||||
if (filter_var($check, FILTER_VALIDATE_BOOLEAN) === false) {
|
||||
DUP_LOG::trace($errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if duplicator installer files are present
|
||||
*
|
||||
* @return bool True if any reserved files are found
|
||||
*/
|
||||
public static function hasInstallerFiles()
|
||||
{
|
||||
$files = self::getInstallerFiles();
|
||||
foreach ($files as $file => $path) {
|
||||
if (false !== strpos($path, '*')) {
|
||||
$glob_files = glob($path);
|
||||
if (!empty($glob_files)) {
|
||||
return true;
|
||||
}
|
||||
} elseif (file_exists($path)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of all the installer files by name and full path
|
||||
*
|
||||
* @remarks
|
||||
* FILES: installer.php, installer-backup.php, dup-installer-bootlog__[HASH].txt
|
||||
* DIRS: dup-installer
|
||||
* DEV FILES: wp-config.orig
|
||||
* Last set is for lazy developer cleanup files that a developer may have
|
||||
* accidently left around lets be proactive for the user just in case.
|
||||
*
|
||||
* @return array [file_name, file_path]
|
||||
*/
|
||||
public static function getInstallerFiles()
|
||||
{
|
||||
// alphanumeric 7 time, then -(dash), then 8 digits
|
||||
$abs_path = duplicator_get_abs_path();
|
||||
$four_digit_glob_pattern = '[0-9][0-9][0-9][0-9]';
|
||||
$retArr = array(
|
||||
basename(DUPLICATOR_INSTALLER_DIRECTORY) . ' ' . esc_html__('(directory)', 'duplicator') => DUPLICATOR_INSTALLER_DIRECTORY,
|
||||
DUPLICATOR_INSTALL_PHP => $abs_path . '/' . DUPLICATOR_INSTALL_PHP,
|
||||
'[HASH]' . '_' . DUPLICATOR_INSTALL_PHP => $abs_path . '/*_*' . $four_digit_glob_pattern . '_' . DUPLICATOR_INSTALL_PHP,
|
||||
DUPLICATOR_INSTALL_BAK => $abs_path . '/' . DUPLICATOR_INSTALL_BAK,
|
||||
'[HASH]' . '_' . DUPLICATOR_INSTALL_BAK => $abs_path . '/*_*' . $four_digit_glob_pattern . '_' . DUPLICATOR_INSTALL_BAK,
|
||||
'[HASH]_archive.zip|daf' => $abs_path . '/*_*' . $four_digit_glob_pattern . '_archive.[zd][ia][pf]',
|
||||
'dup-installer-bootlog__[HASH].txt' => $abs_path . '/dup-installer-bootlog__' . DUPLICATOR_INSTALLER_HASH_PATTERN . '.txt',
|
||||
);
|
||||
|
||||
// legacy package
|
||||
$retArr['dup-wp-config-arc__[HASH].txt'] = $abs_path . '/dup-wp-config-arc__' . DUPLICATOR_INSTALLER_HASH_PATTERN . '.txt';
|
||||
return $retArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the IP of a client machine
|
||||
*
|
||||
* @return string IP of the client machine
|
||||
*/
|
||||
public static function getClientIP()
|
||||
{
|
||||
if (array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER)) {
|
||||
return $_SERVER["HTTP_X_FORWARDED_FOR"];
|
||||
} elseif (array_key_exists('REMOTE_ADDR', $_SERVER)) {
|
||||
return $_SERVER["REMOTE_ADDR"];
|
||||
} elseif (array_key_exists('HTTP_CLIENT_IP', $_SERVER)) {
|
||||
return $_SERVER["HTTP_CLIENT_IP"];
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get PHP memory usage
|
||||
*
|
||||
* @return string Returns human readable memory usage.
|
||||
*/
|
||||
public static function getPHPMemory($peak = false)
|
||||
{
|
||||
if ($peak) {
|
||||
$result = 'Unable to read PHP peak memory usage';
|
||||
if (function_exists('memory_get_peak_usage')) {
|
||||
$result = DUP_Util::byteSize(memory_get_peak_usage(true));
|
||||
}
|
||||
} else {
|
||||
$result = 'Unable to read PHP memory usage';
|
||||
if (function_exists('memory_get_usage')) {
|
||||
$result = DUP_Util::byteSize(memory_get_usage(true));
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the server settings data
|
||||
*
|
||||
* @return array<mixed>
|
||||
*/
|
||||
public static function getServerSettingsData()
|
||||
{
|
||||
$serverSettings = [];
|
||||
|
||||
//GENERAL SETTINGS
|
||||
$serverSettings[] = [
|
||||
'title' => __('General', 'duplicator'),
|
||||
'settings' => self::getGeneralServerSettings(),
|
||||
];
|
||||
|
||||
//WORDPRESS SETTINGS
|
||||
$serverSettings[] = [
|
||||
'title' => __('WordPress', 'duplicator'),
|
||||
'settings' => self::getWordPressServerSettings(),
|
||||
];
|
||||
|
||||
//PHP SETTINGS
|
||||
$serverSettings[] = [
|
||||
'title' => __('PHP', 'duplicator'),
|
||||
'settings' => self::getPHPServerSettings(),
|
||||
];
|
||||
|
||||
//MYSQL SETTINGS
|
||||
$serverSettings[] = [
|
||||
'title' => __('MySQL', 'duplicator'),
|
||||
'settings' => self::getMysqlServerSettings(),
|
||||
];
|
||||
|
||||
// Paths Info
|
||||
$serverSettings[] = [
|
||||
'title' => __('Paths Info', 'duplicator'),
|
||||
'settings' => self::getPathsSettings(),
|
||||
];
|
||||
|
||||
//URLs info
|
||||
$urlsSettings = [];
|
||||
foreach (DUP_Archive::getOriginalURLs() as $key => $url) {
|
||||
$urlsSettings[] = [
|
||||
'label' => __('URL ', 'duplicator') . $key,
|
||||
'logLabel' => 'URL ' . $key,
|
||||
'value' => $url,
|
||||
];
|
||||
}
|
||||
|
||||
$serverSettings[] = [
|
||||
'title' => __('URLs Info', 'duplicator'),
|
||||
'settings' => $urlsSettings,
|
||||
];
|
||||
|
||||
//Disk Space
|
||||
$home_path = duplicator_get_home_path();
|
||||
$space = SnapIO::diskTotalSpace($home_path);
|
||||
$space_free = SnapIO::diskFreeSpace($home_path);
|
||||
$serverDiskSettings = [
|
||||
[
|
||||
'label' => __('Free Space', 'duplicator'),
|
||||
'logLabel' => 'Free Space',
|
||||
'value' => sprintf(
|
||||
__('%1$s%% -- %2$s from %3$s', 'duplicator'),
|
||||
round($space_free / $space * 100, 2),
|
||||
DUP_Util::byteSize($space_free),
|
||||
DUP_Util::byteSize($space)
|
||||
),
|
||||
'valueNoteBottom' => __(
|
||||
'Note: This value is the physical servers hard-drive allocation.
|
||||
On shared hosts check your control panel for the "TRUE" disk space quota value.',
|
||||
'duplicator'
|
||||
),
|
||||
],
|
||||
];
|
||||
|
||||
$serverSettings[] = [
|
||||
'title' => __('Server Disk', 'duplicator'),
|
||||
'settings' => $serverDiskSettings,
|
||||
];
|
||||
|
||||
return $serverSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the geleral server settings
|
||||
*
|
||||
* @return array<mixed>
|
||||
*/
|
||||
private static function getGeneralServerSettings()
|
||||
{
|
||||
$ip = __("Can't detect", 'duplicator');
|
||||
if (isset($_SERVER['SERVER_ADDR'])) {
|
||||
$ip = $_SERVER['SERVER_ADDR'];
|
||||
} elseif (isset($_SERVER['SERVER_NAME']) && function_exists('gethostbyname')) {
|
||||
$ip = gethostbyname($_SERVER['SERVER_NAME']);
|
||||
}
|
||||
|
||||
return [
|
||||
[
|
||||
'label' => __('Duplicator Version', 'duplicator'),
|
||||
'logLabel' => 'Duplicator Version',
|
||||
'value' => DUPLICATOR_VERSION,
|
||||
],
|
||||
[
|
||||
'label' => __('Operating System', 'duplicator'),
|
||||
'logLabel' => 'Operating System',
|
||||
'value' => PHP_OS,
|
||||
],
|
||||
[
|
||||
'label' => __('Timezone', 'duplicator'),
|
||||
'logLabel' => 'Timezone',
|
||||
'value' => function_exists('wp_timezone_string') ? wp_timezone_string() : __('Unknown', 'duplicator'),
|
||||
'valueNote' => sprintf(
|
||||
_x(
|
||||
'This is a %1$sWordPress Setting%2$s',
|
||||
'%1$s and %2$s are the opening and closing anchor tags',
|
||||
'duplicator'
|
||||
),
|
||||
'<a href="options-general.php">',
|
||||
'</a>'
|
||||
),
|
||||
],
|
||||
|
||||
[
|
||||
'label' => __('Server Time', 'duplicator'),
|
||||
'logLabel' => 'Server Time',
|
||||
'value' => current_time('Y-m-d H:i:s'),
|
||||
],
|
||||
[
|
||||
'label' => __('Web Server', 'duplicator'),
|
||||
'logLabel' => 'Web Server',
|
||||
'value' => SnapUtil::sanitizeTextInput(INPUT_SERVER, 'SERVER_SOFTWARE'),
|
||||
],
|
||||
[
|
||||
'label' => __('Loaded PHP INI', 'duplicator'),
|
||||
'logLabel' => 'Loaded PHP INI',
|
||||
'value' => php_ini_loaded_file(),
|
||||
],
|
||||
[
|
||||
'label' => __('Server IP', 'duplicator'),
|
||||
'logLabel' => 'Server IP',
|
||||
'value' => $ip,
|
||||
],
|
||||
[
|
||||
'label' => __('Client IP', 'duplicator'),
|
||||
'logLabel' => 'Client IP',
|
||||
'value' => self::getClientIP(),
|
||||
],
|
||||
[
|
||||
'label' => __('Host', 'duplicator'),
|
||||
'logLabel' => 'Host',
|
||||
'value' => parse_url(get_site_url(), PHP_URL_HOST),
|
||||
],
|
||||
[
|
||||
'label' => __('Duplicator Version', 'duplicator'),
|
||||
'logLabel' => 'Duplicator Version',
|
||||
'value' => DUPLICATOR_VERSION,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the WP server settings
|
||||
*
|
||||
* @return array<mixed>
|
||||
*/
|
||||
private static function getWordPressServerSettings()
|
||||
{
|
||||
global $wp_version;
|
||||
|
||||
return [
|
||||
[
|
||||
'label' => __('WordPress Version', 'duplicator'),
|
||||
'logLabel' => 'WordPress Version',
|
||||
'value' => $wp_version,
|
||||
],
|
||||
[
|
||||
'label' => __('Language', 'duplicator'),
|
||||
'logLabel' => 'Language',
|
||||
'value' => get_bloginfo('language'),
|
||||
],
|
||||
[
|
||||
'label' => __('Charset', 'duplicator'),
|
||||
'logLabel' => 'Charset',
|
||||
'value' => get_bloginfo('charset'),
|
||||
],
|
||||
[
|
||||
'label' => __('Memory Limit', 'duplicator'),
|
||||
'logLabel' => 'Memory Limit',
|
||||
'value' => WP_MEMORY_LIMIT,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the PHP server settings
|
||||
*
|
||||
* @return array<mixed>
|
||||
*/
|
||||
private static function getPHPServerSettings()
|
||||
{
|
||||
return [
|
||||
[
|
||||
'label' => __('PHP Version', 'duplicator'),
|
||||
'logLabel' => 'PHP Version',
|
||||
'value' => phpversion(),
|
||||
],
|
||||
[
|
||||
'label' => __('PHP SAPI', 'duplicator'),
|
||||
'logLabel' => 'PHP SAPI',
|
||||
'value' => PHP_SAPI,
|
||||
],
|
||||
[
|
||||
'label' => __('User', 'duplicator'),
|
||||
'logLabel' => 'User',
|
||||
'value' => DUP_Util::getCurrentUser(),
|
||||
],
|
||||
[
|
||||
'label' => __('Memory Limit', 'duplicator'),
|
||||
'logLabel' => 'Memory Limit',
|
||||
'labelLink' => 'http://www.php.net/manual/en/ini.core.php#ini.memory-limit',
|
||||
'value' => @ini_get('memory_limit'),
|
||||
],
|
||||
[
|
||||
'label' => __('Memory In Use', 'duplicator'),
|
||||
'logLabel' => 'Memory In Use',
|
||||
'value' => size_format(memory_get_usage(true)),
|
||||
],
|
||||
[
|
||||
'label' => __('Max Execution Time', 'duplicator'),
|
||||
'logLabel' => 'Max Execution Time',
|
||||
'labelLink' => 'http://www.php.net/manual/en/info.configuration.php#ini.max-execution-time',
|
||||
'value' => @ini_get('max_execution_time'),
|
||||
'valueNote' => sprintf(
|
||||
_x('(default) - %1$s', '%1$s = "is dynamic" or "value is fixed" based on settings', 'duplicator'),
|
||||
set_time_limit(0) ? __('is dynamic', 'duplicator') : __('value is fixed', 'duplicator')
|
||||
),
|
||||
'valueTooltip' =>
|
||||
__(
|
||||
'If the value shows dynamic then this means its possible for PHP to run longer than the default.
|
||||
If the value is fixed then PHP will not be allowed to run longer than the default.',
|
||||
'duplicator'
|
||||
),
|
||||
],
|
||||
[
|
||||
'label' => __('open_basedir', 'duplicator'),
|
||||
'logLabel' => 'open_basedir',
|
||||
'labelLink' => 'http://php.net/manual/en/ini.core.php#ini.open-basedir',
|
||||
'value' => empty(@ini_get('open_basedir')) ? __('Off', 'duplicator') : @ini_get('open_basedir'),
|
||||
],
|
||||
[
|
||||
'label' => __('Shell (exec)', 'duplicator'),
|
||||
'logLabel' => 'Shell (exec)',
|
||||
'labelLink' => 'https://www.php.net/manual/en/function.exec.php',
|
||||
'value' => DUP_Util::hasShellExec() ? __('Is Supported', 'duplicator') : __('Not Supported', 'duplicator'),
|
||||
],
|
||||
[
|
||||
'label' => __('Shell Exec Zip', 'duplicator'),
|
||||
'logLabel' => 'Shell Exec Zip',
|
||||
'value' => (DUP_Util::getZipPath() != null) ? __('Is Supported', 'duplicator') : __('Not Supported', 'duplicator'),
|
||||
],
|
||||
[
|
||||
'label' => __('Suhosin Extension', 'duplicator'),
|
||||
'logLabel' => 'Suhosin Extension',
|
||||
'labelLink' => 'https://suhosin.org/stories/index.html',
|
||||
'value' => extension_loaded('suhosin') ? __('Enabled', 'duplicator') : __('Disabled', 'duplicator'),
|
||||
],
|
||||
[
|
||||
'label' => __('Architecture', 'duplicator'),
|
||||
'logLabel' => 'Architecture',
|
||||
'value' => SnapUtil::getArchitectureString(),
|
||||
],
|
||||
[
|
||||
'label' => __('Error Log File', 'duplicator'),
|
||||
'logLabel' => 'Error Log File',
|
||||
'value' => @ini_get('error_log'),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the MySQL server settings
|
||||
*
|
||||
* @return array<mixed>
|
||||
*/
|
||||
public static function getMysqlServerSettings()
|
||||
{
|
||||
return [
|
||||
[
|
||||
'label' => __('Version', 'duplicator'),
|
||||
'logLabel' => 'Version',
|
||||
'value' => DUP_DB::getVersion(),
|
||||
],
|
||||
[
|
||||
'label' => __('Charset', 'duplicator'),
|
||||
'logLabel' => 'Charset',
|
||||
'value' => DB_CHARSET,
|
||||
],
|
||||
[
|
||||
'label' => __('Wait Timeout', 'duplicator'),
|
||||
'logLabel' => 'Wait Timeout',
|
||||
'labelLink' => 'http://dev.mysql.com/doc/refman/5.0/en/server-system-variables.html#sysvar_wait_timeout',
|
||||
'value' => DUP_DB::getVariable('wait_timeout'),
|
||||
],
|
||||
[
|
||||
'label' => __('Max Allowed Packets', 'duplicator'),
|
||||
'logLabel' => 'Max Allowed Packets',
|
||||
'labelLink' => 'http://dev.mysql.com/doc/refman/5.0/en/server-system-variables.html#sysvar_max_allowed_packet',
|
||||
'value' => DUP_DB::getVariable('max_allowed_packet'),
|
||||
],
|
||||
[
|
||||
'label' => __('mysqldump Path', 'duplicator'),
|
||||
'logLabel' => 'mysqldump Path',
|
||||
'labelLink' => 'http://dev.mysql.com/doc/refman/5.0/en/mysqldump.html',
|
||||
'value' => DUP_DB::getMySqlDumpPath() !== false ? DUP_DB::getMySqlDumpPath() : __('Path Not Found', 'duplicator'),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the paths settings
|
||||
*
|
||||
* @return array<mixed>
|
||||
*/
|
||||
public static function getPathsSettings()
|
||||
{
|
||||
$pathsSettings = [
|
||||
[
|
||||
'label' => __('Target root path', 'duplicator'),
|
||||
'logLabel' => 'Target root path',
|
||||
'value' => DUP_Archive::getTargetRootPath(),
|
||||
],
|
||||
];
|
||||
|
||||
foreach (DUP_Archive::getOriginalPaths() as $key => $origPath) {
|
||||
$pathsSettings[] = [
|
||||
'label' => __('Original ', 'duplicator') . $key,
|
||||
'logLabel' => 'Original ' . $key,
|
||||
'value' => $origPath,
|
||||
];
|
||||
}
|
||||
|
||||
foreach (DUP_Archive::getArchiveListPaths() as $key => $archivePath) {
|
||||
$pathsSettings[] = [
|
||||
'label' => __('Archive ', 'duplicator') . $key,
|
||||
'logLabel' => 'Archive ' . $key,
|
||||
'value' => $archivePath,
|
||||
];
|
||||
}
|
||||
|
||||
return $pathsSettings;
|
||||
}
|
||||
}
|
||||
429
html/wp-content/plugins/duplicator/classes/class.settings.php
Normal file
429
html/wp-content/plugins/duplicator/classes/class.settings.php
Normal file
@@ -0,0 +1,429 @@
|
||||
<?php
|
||||
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
use Duplicator\Utils\Email\EmailSummary;
|
||||
use Duplicator\Utils\Email\EmailSummaryBootstrap;
|
||||
use Duplicator\Utils\UsageStatistics\CommStats;
|
||||
|
||||
abstract class DUP_Archive_Build_Mode
|
||||
{
|
||||
const Unconfigured = -1;
|
||||
const Auto = 0;
|
||||
// should no longer be used
|
||||
// const Shell_Exec = 1;
|
||||
const ZipArchive = 2;
|
||||
const DupArchive = 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maintains the settings for the Duplicator plugin.
|
||||
*/
|
||||
class DUP_Settings
|
||||
{
|
||||
const UNINSTALL_PACKAGE_OPTION_KEY = 'duplicator_uninstall_package';
|
||||
const UNINSTALL_SETTINGS_OPTION_KEY = 'duplicator_uninstall_settings';
|
||||
|
||||
const OPT_SETTINGS = 'duplicator_settings';
|
||||
const INSTALLER_NAME_MODE_WITH_HASH = 'withhash';
|
||||
const INSTALLER_NAME_MODE_SIMPLE = 'simple';
|
||||
const STORAGE_POSITION_WP_CONTENT = 'wpcont';
|
||||
const SSDIR_NAME_NEW = 'backups-dup-lite';
|
||||
|
||||
/**
|
||||
* @deprecated since 1.5.14. Use STORAGE_POSITION_WP_CONTENT instead.
|
||||
*/
|
||||
const STORAGE_POSITION_LEGACY = 'legacy';
|
||||
|
||||
/**
|
||||
* @deprecated since 1.5.14. Use SSDIR_NAME_NEW instead.
|
||||
*/
|
||||
const SSDIR_NAME_LEGACY = 'wp-snapshots';
|
||||
|
||||
protected static $Data = array();
|
||||
protected static $ssDirPath = null;
|
||||
protected static $ssDirUrl = null;
|
||||
|
||||
/**
|
||||
* Class used to manage all the settings for the plugin
|
||||
*/
|
||||
public static function init(): void
|
||||
{
|
||||
static $initialized = null;
|
||||
if ($initialized == null) {
|
||||
$initialized = true; // Must be at the top of the function to prvent infinite loop
|
||||
$defaults = (array) self::GetAllDefaults();
|
||||
$data = (array) get_option(self::OPT_SETTINGS);
|
||||
self::$Data = array_merge($defaults, $data);
|
||||
// when the plugin updated, this will be true
|
||||
if (empty(self::$Data) || empty(self::$Data['version']) || version_compare(DUPLICATOR_VERSION, self::$Data['version'], '>')) {
|
||||
self::SetDefaults();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the setting value
|
||||
*
|
||||
* @param string $key The name of the key to find
|
||||
*
|
||||
* @return ?scalar value stored in the key returns null if key does not exist
|
||||
*/
|
||||
public static function Get($key = '')
|
||||
{
|
||||
self::init();
|
||||
$result = null;
|
||||
if (isset(self::$Data[$key])) {
|
||||
$result = self::$Data[$key];
|
||||
} else {
|
||||
$defaults = self::GetAllDefaults();
|
||||
if (isset($defaults[$key])) {
|
||||
$result = $defaults[$key];
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the settings value in memory only
|
||||
*
|
||||
* @param string $key The name of the key to find
|
||||
* @param string $value The value to set
|
||||
* remarks: The Save() method must be called to write the Settings object to the DB
|
||||
*/
|
||||
public static function Set($key, $value): void
|
||||
{
|
||||
self::init();
|
||||
if ($key == 'usage_tracking') {
|
||||
self::setUsageTracking($value);
|
||||
return;
|
||||
}
|
||||
if (isset(self::$Data[$key])) {
|
||||
self::$Data[$key] = ($value == null) ? '' : $value;
|
||||
} elseif (!empty($key)) {
|
||||
self::$Data[$key] = ($value == null) ? '' : $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set usage tracking
|
||||
*
|
||||
* @param bool $value value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function setUsageTracking($value): void
|
||||
{
|
||||
if (DUPLICATOR_USTATS_DISALLOW) { // @phpstan-ignore-line
|
||||
// If usagfe tracking is hardcoded disabled, don't change the setting value
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isset(self::$Data['usage_tracking'])) {
|
||||
self::$Data['usage_tracking'] = false;
|
||||
}
|
||||
|
||||
$value = (bool) $value;
|
||||
$oldValue = self::$Data['usage_tracking'];
|
||||
|
||||
self::$Data['usage_tracking'] = $value;
|
||||
|
||||
if ($value == false && $oldValue != $value) {
|
||||
CommStats::disableUsageTracking();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the storage position
|
||||
*
|
||||
* @deprecated since 1.5.14. Storage is now always in wp-content.
|
||||
*
|
||||
* @param string $newPosition The new storage position
|
||||
*
|
||||
* @return bool True if the position was set, false otherwise
|
||||
*/
|
||||
public static function setStoragePosition($newPosition)
|
||||
{
|
||||
self::init();
|
||||
|
||||
$legacyPath = self::getSsdirPathLegacy();
|
||||
$wpContPath = self::getSsdirPathWpCont();
|
||||
$oldStoragePost = self::Get('storage_position');
|
||||
self::resetPositionVars();
|
||||
switch ($newPosition) {
|
||||
case self::STORAGE_POSITION_LEGACY:
|
||||
self::$Data['storage_position'] = self::STORAGE_POSITION_LEGACY;
|
||||
if (!DUP_Util::initSnapshotDirectory()) {
|
||||
self::resetPositionVars();
|
||||
self::$Data['storage_position'] = $oldStoragePost;
|
||||
return false;
|
||||
}
|
||||
if (is_dir($wpContPath)) {
|
||||
if (SnapIO::rcopy($wpContPath, $legacyPath)) {
|
||||
SnapIO::rrmdir($wpContPath);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case self::STORAGE_POSITION_WP_CONTENT:
|
||||
self::$Data['storage_position'] = self::STORAGE_POSITION_WP_CONTENT;
|
||||
if (!DUP_Util::initSnapshotDirectory()) {
|
||||
self::resetPositionVars();
|
||||
self::$Data['storage_position'] = $oldStoragePost;
|
||||
return false;
|
||||
}
|
||||
if (is_dir($legacyPath)) {
|
||||
if (SnapIO::rcopy($legacyPath, $wpContPath)) {
|
||||
SnapIO::rrmdir($legacyPath);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Exception('Unknown storage position');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the position variables
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function resetPositionVars(): void
|
||||
{
|
||||
self::$ssDirPath = null;
|
||||
self::$ssDirUrl = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves all the setting values to the database
|
||||
*
|
||||
* @return bool True if option value has changed, false if not or if update failed.
|
||||
*/
|
||||
public static function Save()
|
||||
{
|
||||
self::init();
|
||||
|
||||
update_option(self::UNINSTALL_PACKAGE_OPTION_KEY, self::$Data['uninstall_files']);
|
||||
update_option(self::UNINSTALL_SETTINGS_OPTION_KEY, self::$Data['uninstall_settings']);
|
||||
return update_option(self::OPT_SETTINGS, self::$Data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all the setting values to the database
|
||||
*
|
||||
* @return bool true if option value has changed, false if not or if update failed.
|
||||
*/
|
||||
public static function Delete()
|
||||
{
|
||||
$defaults = self::GetAllDefaults();
|
||||
self::$Data = apply_filters('duplicator_defaults_settings', $defaults);
|
||||
return delete_option(self::OPT_SETTINGS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the defaults if they have not been set
|
||||
*
|
||||
* @return bool True if option value has changed, false if not or if update failed.
|
||||
*/
|
||||
public static function SetDefaults()
|
||||
{
|
||||
$defaults = self::GetAllDefaults();
|
||||
self::$Data = apply_filters('duplicator_defaults_settings', $defaults);
|
||||
return self::Save();
|
||||
}
|
||||
|
||||
/**
|
||||
* DeleteWPOption: Cleans up legacy data
|
||||
*/
|
||||
public static function DeleteWPOption($optionName)
|
||||
{
|
||||
if (in_array($optionName, $GLOBALS['DUPLICATOR_OPTS_DELETE'])) {
|
||||
return delete_option($optionName);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all defaults
|
||||
*
|
||||
* @return array The defaults
|
||||
*/
|
||||
public static function GetAllDefaults()
|
||||
{
|
||||
$default = array();
|
||||
$default['version'] = DUPLICATOR_VERSION;
|
||||
//Flag used to remove the wp_options value duplicator_settings which are all the settings in this class
|
||||
$default['uninstall_settings'] = isset(self::$Data['uninstall_settings']) ? self::$Data['uninstall_settings'] : true;
|
||||
//Flag used to remove entire wp-snapshot directory
|
||||
$default['uninstall_files'] = isset(self::$Data['uninstall_files']) ? self::$Data['uninstall_files'] : true;
|
||||
//Flag used to show debug info
|
||||
$default['package_debug'] = isset(self::$Data['package_debug']) ? self::$Data['package_debug'] : false;
|
||||
//Frequency of email summary
|
||||
$default['email_summary_frequency'] = isset(self::$Data['email_summary_frequency'])
|
||||
? self::$Data['email_summary_frequency'] : EmailSummary::SEND_FREQ_WEEKLY;
|
||||
//Setting to enable AM notifications
|
||||
$default['amNotices'] = isset(self::$Data['amNotices']) ? self::$Data['amNotices'] : true;
|
||||
//Flag used to enable mysqldump
|
||||
$default['package_mysqldump'] = isset(self::$Data['package_mysqldump']) ? self::$Data['package_mysqldump'] : true;
|
||||
//Optional mysqldump search path
|
||||
$default['package_mysqldump_path'] = isset(self::$Data['package_mysqldump_path']) ? self::$Data['package_mysqldump_path'] : '';
|
||||
//Optional mysql limit size
|
||||
$default['package_phpdump_qrylimit'] = isset(self::$Data['package_phpdump_qrylimit']) ? self::$Data['package_phpdump_qrylimit'] : "100";
|
||||
//Optional mysqldump search path
|
||||
$default['package_zip_flush'] = isset(self::$Data['package_zip_flush']) ? self::$Data['package_zip_flush'] : false;
|
||||
//Optional mysqldump search path
|
||||
$default['installer_name_mode'] = isset(self::$Data['installer_name_mode']) ? self::$Data['installer_name_mode'] : self::INSTALLER_NAME_MODE_SIMPLE;
|
||||
// storage position
|
||||
$default['storage_position'] = isset(self::$Data['storage_position']) ? self::$Data['storage_position'] : self::STORAGE_POSITION_WP_CONTENT;
|
||||
//Flag for .htaccess file
|
||||
$default['storage_htaccess_off'] = isset(self::$Data['storage_htaccess_off']) ? self::$Data['storage_htaccess_off'] : false;
|
||||
// Initial archive build mode
|
||||
if (isset(self::$Data['archive_build_mode'])) {
|
||||
$default['archive_build_mode'] = self::$Data['archive_build_mode'];
|
||||
} else {
|
||||
$is_ziparchive_available = apply_filters('duplicator_is_ziparchive_available', class_exists('ZipArchive'));
|
||||
$default['archive_build_mode'] = $is_ziparchive_available ? DUP_Archive_Build_Mode::ZipArchive : DUP_Archive_Build_Mode::DupArchive;
|
||||
}
|
||||
|
||||
// $default['package_zip_flush'] = apply_filters('duplicator_package_zip_flush_default_setting', '0');
|
||||
//Skip scan archive
|
||||
$default['skip_archive_scan'] = isset(self::$Data['skip_archive_scan']) ? self::$Data['skip_archive_scan'] : false;
|
||||
$default['unhook_third_party_js'] = isset(self::$Data['unhook_third_party_js']) ? self::$Data['unhook_third_party_js'] : false;
|
||||
$default['unhook_third_party_css'] = isset(self::$Data['unhook_third_party_css']) ? self::$Data['unhook_third_party_css'] : false;
|
||||
$default['active_package_id'] = -1;
|
||||
$default['usage_tracking'] = isset(self::$Data['usage_tracking']) ? self::$Data['usage_tracking'] : false;
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the emaul summary frequency
|
||||
*
|
||||
* @param string $frequency The new frequency
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function setEmailSummaryFrequency($frequency): void
|
||||
{
|
||||
$oldFrequency = self::Get('email_summary_frequency');
|
||||
if (EmailSummaryBootstrap::updateFrequency($oldFrequency, $frequency) === false) {
|
||||
DUP_Log::Trace("Invalide email summary frequency: {$frequency}");
|
||||
return;
|
||||
}
|
||||
|
||||
self::Set('email_summary_frequency', $frequency);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the create date format
|
||||
*
|
||||
* @return int The create date format
|
||||
*/
|
||||
public static function get_create_date_format()
|
||||
{
|
||||
static $ui_create_frmt = null;
|
||||
if (is_null($ui_create_frmt)) {
|
||||
$ui_create_frmt = is_numeric(self::Get('package_ui_created')) ? self::Get('package_ui_created') : 1;
|
||||
}
|
||||
return $ui_create_frmt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get legacy storage directory path (wp-snapshots)
|
||||
*
|
||||
* @deprecated since 1.5.14. Use getSsdirPathWpCont() instead.
|
||||
*
|
||||
* @return string The legacy storage path
|
||||
*/
|
||||
public static function getSsdirPathLegacy()
|
||||
{
|
||||
return SnapIO::safePathTrailingslashit(duplicator_get_abs_path()) . self::SSDIR_NAME_LEGACY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get wp-content storage directory path (backups-dup-lite)
|
||||
*
|
||||
* @return string The wp-content storage path
|
||||
*/
|
||||
public static function getSsdirPathWpCont()
|
||||
{
|
||||
return SnapIO::safePathTrailingslashit(WP_CONTENT_DIR) . self::SSDIR_NAME_NEW;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the storage directory path
|
||||
*
|
||||
* @return string The storage directory path
|
||||
*
|
||||
* @todo: This function should be removed in future versions as the storage position is now always in wp-content
|
||||
*/
|
||||
public static function getSsdirPath()
|
||||
{
|
||||
if (is_null(self::$ssDirPath)) {
|
||||
if (self::Get('storage_position') === self::STORAGE_POSITION_LEGACY) {
|
||||
self::$ssDirPath = self::getSsdirPathLegacy();
|
||||
} else {
|
||||
self::$ssDirPath = self::getSsdirPathWpCont();
|
||||
}
|
||||
}
|
||||
return self::$ssDirPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the storage directory URL
|
||||
*
|
||||
* @return string The storage directory URL
|
||||
*
|
||||
* @todo: This function should be removed in future versions as the storage position is now always in wp-content
|
||||
*/
|
||||
public static function getSsdirUrl()
|
||||
{
|
||||
if (is_null(self::$ssDirUrl)) {
|
||||
if (self::Get('storage_position') === self::STORAGE_POSITION_LEGACY) {
|
||||
self::$ssDirUrl = SnapIO::trailingslashit(DUPLICATOR_SITE_URL) . self::SSDIR_NAME_LEGACY;
|
||||
} else {
|
||||
self::$ssDirUrl = SnapIO::trailingslashit(content_url()) . self::SSDIR_NAME_NEW;
|
||||
}
|
||||
}
|
||||
return self::$ssDirUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the temporary directory path
|
||||
*
|
||||
* @return string The temporary directory path
|
||||
*/
|
||||
public static function getSsdirTmpPath()
|
||||
{
|
||||
return self::getSsdirPath() . '/tmp';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the installer directory path
|
||||
*
|
||||
* @return string The installer directory path
|
||||
*/
|
||||
public static function getSsdirInstallerPath()
|
||||
{
|
||||
return self::getSsdirPath() . '/installer';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the logs directory path
|
||||
*
|
||||
* @return string The logs directory path
|
||||
*/
|
||||
public static function getSsdirLogsPath()
|
||||
{
|
||||
return self::getSsdirPath() . '/' . DUPLICATOR_LOGS_DIR_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the logs directory URL
|
||||
*
|
||||
* @return string The logs directory URL
|
||||
*/
|
||||
public static function getSsdirLogsUrl()
|
||||
{
|
||||
return self::getSsdirUrl() . '/' . DUPLICATOR_LOGS_DIR_NAME;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* custom hosting manager
|
||||
* singleton class
|
||||
*
|
||||
* Standard: PSR-2
|
||||
*
|
||||
* @package SC\DUPX\HOST
|
||||
* @link http://www.php-fig.org/psr/psr-2/
|
||||
*/
|
||||
|
||||
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
||||
|
||||
require_once(DUPLICATOR_PLUGIN_PATH . '/classes/host/interface.host.php');
|
||||
require_once(DUPLICATOR_PLUGIN_PATH . '/classes/host/class.godaddy.host.php');
|
||||
require_once(DUPLICATOR_PLUGIN_PATH . '/classes/host/class.wpengine.host.php');
|
||||
require_once(DUPLICATOR_PLUGIN_PATH . '/classes/host/class.wordpresscom.host.php');
|
||||
require_once(DUPLICATOR_PLUGIN_PATH . '/classes/host/class.liquidweb.host.php');
|
||||
require_once(DUPLICATOR_PLUGIN_PATH . '/classes/host/class.pantheon.host.php');
|
||||
require_once(DUPLICATOR_PLUGIN_PATH . '/classes/host/class.flywheel.host.php');
|
||||
|
||||
class DUP_Custom_Host_Manager
|
||||
{
|
||||
const HOST_GODADDY = 'godaddy';
|
||||
const HOST_WPENGINE = 'wpengine';
|
||||
const HOST_WORDPRESSCOM = 'wordpresscom';
|
||||
const HOST_LIQUIDWEB = 'liquidweb';
|
||||
const HOST_PANTHEON = 'pantheon';
|
||||
const HOST_FLYWHEEL = 'flywheel';
|
||||
|
||||
/**
|
||||
*
|
||||
* @var DUP_Custom_Host_Manager
|
||||
*/
|
||||
protected static $instance = null;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $initialized = false;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var DUP_Host_interface[]
|
||||
*/
|
||||
private $customHostings = array();
|
||||
|
||||
/**
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
private $activeHostings = array();
|
||||
|
||||
/**
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function getInstance()
|
||||
{
|
||||
if (is_null(self::$instance)) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
private function __construct()
|
||||
{
|
||||
$this->customHostings[DUP_WPEngine_Host::getIdentifier()] = new DUP_WPEngine_Host();
|
||||
$this->customHostings[DUP_GoDaddy_Host::getIdentifier()] = new DUP_GoDaddy_Host();
|
||||
$this->customHostings[DUP_WordpressCom_Host::getIdentifier()] = new DUP_WordpressCom_Host();
|
||||
$this->customHostings[DUP_Liquidweb_Host::getIdentifier()] = new DUP_Liquidweb_Host();
|
||||
$this->customHostings[DUP_Pantheon_Host::getIdentifier()] = new DUP_Pantheon_Host();
|
||||
$this->customHostings[DUP_Flywheel_Host::getIdentifier()] = new DUP_Flywheel_Host();
|
||||
}
|
||||
|
||||
public function init()
|
||||
{
|
||||
if ($this->initialized) {
|
||||
return true;
|
||||
}
|
||||
foreach ($this->customHostings as $cHost) {
|
||||
if (!($cHost instanceof DUP_Host_interface)) {
|
||||
throw new Exception('Host must implement DUP_Host_interface');
|
||||
}
|
||||
if ($cHost->isHosting()) {
|
||||
$this->activeHostings[] = $cHost->getIdentifier();
|
||||
$cHost->init();
|
||||
}
|
||||
}
|
||||
$this->initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getActiveHostings()
|
||||
{
|
||||
return $this->activeHostings;
|
||||
}
|
||||
|
||||
public function isHosting($identifier)
|
||||
{
|
||||
return in_array($identifier, $this->activeHostings);
|
||||
}
|
||||
|
||||
public function isManaged()
|
||||
{
|
||||
if ($this->isHosting(self::HOST_WORDPRESSCOM)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->isHosting(self::HOST_GODADDY)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->isHosting(self::HOST_WPENGINE)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->isHosting(self::HOST_LIQUIDWEB)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->isHosting(self::HOST_PANTHEON)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->isHosting(self::HOST_FLYWHEEL)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->WPConfigIsNotWriteable()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->notAccessibleCoreDirPresent()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function WPConfigIsNotWriteable()
|
||||
{
|
||||
$wpConfigPath = duplicator_get_abs_path() . "/wp-config.php";
|
||||
|
||||
return file_exists($wpConfigPath) && !is_writeable($wpConfigPath);
|
||||
}
|
||||
|
||||
public function notAccessibleCoreDirPresent()
|
||||
{
|
||||
$absPath = duplicator_get_abs_path();
|
||||
$coreDirs = array(
|
||||
$absPath . '/wp-admin',
|
||||
$absPath . '/wp-includes',
|
||||
WP_CONTENT_DIR
|
||||
);
|
||||
|
||||
foreach ($coreDirs as $coreDir) {
|
||||
if (file_exists($coreDir) && !is_writeable($coreDir)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getHosting($identifier)
|
||||
{
|
||||
if ($this->isHosting($identifier)) {
|
||||
return $this->activeHostings[$identifier];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Flywheel custom hosting class
|
||||
*
|
||||
* Standard: PSR-2
|
||||
*
|
||||
* @package SC\DUPX\HOST
|
||||
* @link http://www.php-fig.org/psr/psr-2/
|
||||
*/
|
||||
|
||||
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
||||
|
||||
class DUP_Flywheel_Host implements DUP_Host_interface
|
||||
{
|
||||
public static function getIdentifier()
|
||||
{
|
||||
return DUP_Custom_Host_Manager::HOST_FLYWHEEL;
|
||||
}
|
||||
|
||||
public function isHosting()
|
||||
{
|
||||
$path = duplicator_get_home_path() . '/.fw-config.php';
|
||||
return apply_filters('duplicator_host_check', file_exists($path), self::getIdentifier());
|
||||
}
|
||||
|
||||
public function init()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
|
||||
class DUP_GoDaddy_Host implements DUP_Host_interface
|
||||
{
|
||||
public static function getIdentifier()
|
||||
{
|
||||
return DUP_Custom_Host_Manager::HOST_GODADDY;
|
||||
}
|
||||
|
||||
public function isHosting()
|
||||
{
|
||||
return apply_filters(
|
||||
'duplicator_godaddy_host_check',
|
||||
file_exists(SnapIO::safePathUntrailingslashit(WPMU_PLUGIN_DIR) . '/gd-system-plugin.php')
|
||||
);
|
||||
}
|
||||
|
||||
public function init()
|
||||
{
|
||||
add_filter('duplicator_defaults_settings', array(__CLASS__, 'defaultsSettings'));
|
||||
}
|
||||
|
||||
public static function defaultsSettings($defaults)
|
||||
{
|
||||
$defaults['archive_build_mode'] = DUP_Archive_Build_Mode::DupArchive;
|
||||
return $defaults;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* wpengine custom hosting class
|
||||
*
|
||||
* Standard: PSR-2
|
||||
*
|
||||
* @package SC\DUPX\HOST
|
||||
* @link http://www.php-fig.org/psr/psr-2/
|
||||
*/
|
||||
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
|
||||
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
||||
|
||||
class DUP_Liquidweb_Host implements DUP_Host_interface
|
||||
{
|
||||
const TEST = 0;
|
||||
|
||||
public static function getIdentifier()
|
||||
{
|
||||
return DUP_Custom_Host_Manager::HOST_LIQUIDWEB;
|
||||
}
|
||||
|
||||
public function isHosting()
|
||||
{
|
||||
return apply_filters(
|
||||
'duplicator_liquidweb_host_check',
|
||||
file_exists(SnapIO::safePathUntrailingslashit(WPMU_PLUGIN_DIR) . '/liquid-web.php')
|
||||
);
|
||||
}
|
||||
|
||||
public function init()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* godaddy custom hosting class
|
||||
*
|
||||
* Standard: PSR-2
|
||||
*
|
||||
* @package SC\DUPX\HOST
|
||||
* @link http://www.php-fig.org/psr/psr-2/
|
||||
*/
|
||||
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
|
||||
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
||||
|
||||
class DUP_Pantheon_Host implements DUP_Host_interface
|
||||
{
|
||||
public static function getIdentifier()
|
||||
{
|
||||
return DUP_Custom_Host_Manager::HOST_PANTHEON;
|
||||
}
|
||||
|
||||
public function isHosting()
|
||||
{
|
||||
return apply_filters(
|
||||
'duplicator_pantheon_host_check',
|
||||
file_exists(SnapIO::safePathUntrailingslashit(WPMU_PLUGIN_DIR) . '/pantheon.php')
|
||||
);
|
||||
}
|
||||
|
||||
public function init()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* godaddy custom hosting class
|
||||
*
|
||||
* Standard: PSR-2
|
||||
*
|
||||
* @package SC\DUPX\HOST
|
||||
* @link http://www.php-fig.org/psr/psr-2/
|
||||
*/
|
||||
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
|
||||
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
||||
|
||||
class DUP_WordpressCom_Host implements DUP_Host_interface
|
||||
{
|
||||
public static function getIdentifier()
|
||||
{
|
||||
return DUP_Custom_Host_Manager::HOST_WORDPRESSCOM;
|
||||
}
|
||||
|
||||
public function isHosting()
|
||||
{
|
||||
return apply_filters(
|
||||
'duplicator_pro_wordpress_host_check',
|
||||
file_exists(SnapIO::safePathUntrailingslashit(WPMU_PLUGIN_DIR) . '/wpcomsh-loader.php')
|
||||
);
|
||||
}
|
||||
|
||||
public function init()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
|
||||
// New encryption class
|
||||
|
||||
class DUP_WPEngine_Host implements DUP_Host_interface
|
||||
{
|
||||
public function init()
|
||||
{
|
||||
add_filter('duplicator_installer_file_path', array(__CLASS__, 'installerFilePath'), 10, 1);
|
||||
add_filter('duplicator_global_file_filters_on', '__return_true');
|
||||
add_filter('duplicator_global_file_filters', array(__CLASS__, 'globalFileFilters'), 10, 1);
|
||||
add_filter('duplicator_defaults_settings', array(__CLASS__, 'defaultsSettings'));
|
||||
}
|
||||
|
||||
public static function getIdentifier()
|
||||
{
|
||||
return DUP_Custom_Host_Manager::HOST_WPENGINE;
|
||||
}
|
||||
|
||||
|
||||
public function isHosting()
|
||||
{
|
||||
return apply_filters(
|
||||
'duplicator_wp_engine_host_check',
|
||||
file_exists(SnapIO::safePathUntrailingslashit(WPMU_PLUGIN_DIR) . '/wpengine-security-auditor.php')
|
||||
);
|
||||
}
|
||||
|
||||
public static function installerFilePath($path)
|
||||
{
|
||||
$path_info = pathinfo($path);
|
||||
$newPath = $path;
|
||||
if ('php' == $path_info['extension']) {
|
||||
$newPath = substr_replace($path, '.txt', -4);
|
||||
}
|
||||
return $newPath;
|
||||
}
|
||||
|
||||
public static function globalFileFilters($files)
|
||||
{
|
||||
$files[] = wp_normalize_path(WP_CONTENT_DIR) . '/mysql.sql';
|
||||
return $files;
|
||||
}
|
||||
|
||||
public static function defaultsSettings($defaults)
|
||||
{
|
||||
$defaults['package_zip_flush'] = '1';
|
||||
return $defaults;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* interface for specific hostings class
|
||||
*
|
||||
* Standard: PSR-2
|
||||
*
|
||||
* @package SC\DUPX\HOST
|
||||
* @link http://www.php-fig.org/psr/psr-2/
|
||||
*/
|
||||
|
||||
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
||||
|
||||
interface DUP_Host_interface
|
||||
{
|
||||
/**
|
||||
* return the current host itentifier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getIdentifier();
|
||||
|
||||
/**
|
||||
* @return bool true if is current host
|
||||
*/
|
||||
public function isHosting();
|
||||
|
||||
/**
|
||||
* the init function.
|
||||
* is called only if isHosting is true
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init();
|
||||
}
|
||||
3
html/wp-content/plugins/duplicator/classes/index.php
Normal file
3
html/wp-content/plugins/duplicator/classes/index.php
Normal file
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
//silent
|
||||
@@ -0,0 +1,203 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* archive path file list object
|
||||
*
|
||||
* Standard: PSR-2
|
||||
*
|
||||
* @link http://www.php-fig.org/psr/psr-2
|
||||
*
|
||||
* @package name
|
||||
* @copyright (c) 2019, Snapcreek LLC
|
||||
* @license https://opensource.org/licenses/GPL-3.0 GNU Public License
|
||||
*/
|
||||
|
||||
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
||||
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
use Duplicator\Libs\Snap\SnapJson;
|
||||
|
||||
class DUP_Archive_File_List
|
||||
{
|
||||
protected $path = null;
|
||||
protected $handle = null;
|
||||
protected $cache = null;
|
||||
|
||||
public function __construct($path)
|
||||
{
|
||||
if (empty($path)) {
|
||||
throw new Exception('path can\'t be empty');
|
||||
}
|
||||
|
||||
$this->path = SnapIO::safePath($path);
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
$this->close();
|
||||
}
|
||||
|
||||
public function getPath()
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
public function open($truncate = false)
|
||||
{
|
||||
if (is_null($this->handle)) {
|
||||
if (($this->handle = fopen($this->path, 'a+')) === false) {
|
||||
DUP_Log::trace('Can\'t open ' . $this->path);
|
||||
$this->handle = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if ($truncate) {
|
||||
$this->emptyFile();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function emptyFile()
|
||||
{
|
||||
if (!$this->open(false)) {
|
||||
return false;
|
||||
}
|
||||
if (($res = ftruncate($this->handle, 0)) === false) {
|
||||
DUP_Log::trace('Can\'t truncate file ' . $this->path);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function close()
|
||||
{
|
||||
if (!is_null($this->handle)) {
|
||||
if (($res = @fclose($this->handle)) === false) {
|
||||
DUP_Log::trace('Can\'t close ' . $this->path);
|
||||
return false;
|
||||
}
|
||||
$this->handle = null;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function addEntry($path, $size, $nodes)
|
||||
{
|
||||
|
||||
if (is_null($this->handle)) { // check to generate less overhead
|
||||
if (!$this->open()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$entry = array('p' => $path, 's' => $size, 'n' => $nodes);
|
||||
fwrite($this->handle, SnapJson::jsonEncode($entry) . "\n");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param bool $pathOnly if true return only payth
|
||||
*
|
||||
* @return boolean|array|string return false if is end of filer.
|
||||
*/
|
||||
public function getEntry($pathOnly = false)
|
||||
{
|
||||
if (is_null($this->handle)) { // check to generate less overhead
|
||||
if (!$this->open()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (($json = fgets($this->handle, 4196)) === false) {
|
||||
// end of file return false
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = json_decode($json, true);
|
||||
if ($pathOnly) {
|
||||
return $result['p'];
|
||||
} else {
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
protected function cleanCache()
|
||||
{
|
||||
$this->cache = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function loadCache($refreshCache = false)
|
||||
{
|
||||
if ($refreshCache || is_null($this->cache)) {
|
||||
if (!$this->open()) {
|
||||
return false;
|
||||
}
|
||||
$this->cache = array();
|
||||
if (@fseek($this->handle, 0) === -1) {
|
||||
DUP_Log::trace('Can\'t seek at 0 pos for file ' . $this->path);
|
||||
$this->cleanCache();
|
||||
return false;
|
||||
}
|
||||
while (($entry = $this->getEntry()) !== false) {
|
||||
$this->cache[$entry['p']] = $entry;
|
||||
}
|
||||
if (!feof($this->handle)) {
|
||||
DUP_Log::trace('Error: unexpected fgets() fail', '', false);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getEntryFromPath($path, $refreshCache = false)
|
||||
{
|
||||
if (!$this->loadCache($refreshCache)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (array_key_exists($path, $this->cache)) {
|
||||
return $this->cache[$path];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function getEntriesFormPath($path, $refreshCache = false)
|
||||
{
|
||||
if (!$this->loadCache($refreshCache)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (array_key_exists($path, $this->cache)) {
|
||||
$result = array();
|
||||
foreach ($this->cache as $current => $entry) {
|
||||
if (preg_match('/^' . preg_quote($path, '/') . '\/[^\/]+$/', $current)) {
|
||||
$result[] = $entry;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function getArrayPaths($pathPrefix = '')
|
||||
{
|
||||
if (!$this->open()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = array();
|
||||
if (@fseek($this->handle, 0) === -1) {
|
||||
DUP_Log::trace('Can\'t seek at 0 pos for file ' . $this->path);
|
||||
return false;
|
||||
}
|
||||
$safePrefix = SnapIO::safePathUntrailingslashit($pathPrefix);
|
||||
while (($path = $this->getEntry(true)) !== false) {
|
||||
$result[] = $safePrefix . '/' . $path;
|
||||
}
|
||||
if (!feof($this->handle)) {
|
||||
DUP_Log::trace('Error: unexpected fgets() fail', '', false);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
||||
/**
|
||||
* The base class for all filter types Directories/Files/Extentions
|
||||
*
|
||||
* @package Duplicator
|
||||
* @subpackage classes/package
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if (! defined('DUPLICATOR_VERSION')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class DUP_Archive_Filter_Scope_Base
|
||||
{
|
||||
//All internal storage items that duplicator decides to filter
|
||||
public $Core = array();
|
||||
//Global filter items added from settings
|
||||
public $Global = array();
|
||||
//Items when creating a package or template that a user decides to filter
|
||||
public $Instance = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* The filter types that belong to directories
|
||||
*
|
||||
* @package Duplicator
|
||||
* @subpackage classes/package
|
||||
*/
|
||||
class DUP_Archive_Filter_Scope_Directory extends DUP_Archive_Filter_Scope_Base
|
||||
{
|
||||
//Items that are not readable
|
||||
public $Warning = array();
|
||||
|
||||
// Items that are not readable
|
||||
public $Unreadable = array();
|
||||
public $AddonSites = array();
|
||||
|
||||
// Items that exceed size threshold
|
||||
public $Size = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* The filter types that belong to files
|
||||
*
|
||||
* @package Duplicator
|
||||
* @subpackage classes/package
|
||||
*/
|
||||
class DUP_Archive_Filter_Scope_File extends DUP_Archive_Filter_Scope_Directory
|
||||
{
|
||||
//Items that are too large
|
||||
public $Size = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* The filter information object which store all information about the filtered
|
||||
* data that is gathered to the execution of a scan process
|
||||
*
|
||||
* @package Duplicator
|
||||
* @subpackage classes/package
|
||||
*/
|
||||
class DUP_Archive_Filter_Info
|
||||
{
|
||||
/** @var DUP_Archive_Filter_Scope_Directory Contains all folder filter info */
|
||||
public $Dirs = null;
|
||||
/** @var DUP_Archive_Filter_Scope_File Contains all file filter info */
|
||||
public $Files = null;
|
||||
/** @var DUP_Archive_Filter_Scope_Base Contains all extensions filter info */
|
||||
public $Exts = null;
|
||||
public $UDirCount = 0;
|
||||
public $UFileCount = 0;
|
||||
public $UExtCount = 0;
|
||||
public $TreeSize;
|
||||
public $TreeWarning;
|
||||
/**
|
||||
* Init this object
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* reset and clean all object
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->Dirs = new DUP_Archive_Filter_Scope_Directory();
|
||||
$this->Files = new DUP_Archive_Filter_Scope_File();
|
||||
$this->Exts = new DUP_Archive_Filter_Scope_Base();
|
||||
$this->TreeSize = array();
|
||||
$this->TreeWarning = array();
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,251 @@
|
||||
<?php
|
||||
|
||||
use Duplicator\Libs\Snap\SnapUtil;
|
||||
|
||||
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
||||
// Exit if accessed directly
|
||||
if (! defined('DUPLICATOR_VERSION')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
require_once DUPLICATOR_PLUGIN_PATH . 'classes/package/class.pack.archive.php';
|
||||
|
||||
/**
|
||||
* Creates a zip file using the built in PHP ZipArchive class
|
||||
*/
|
||||
class DUP_Zip extends DUP_Archive
|
||||
{
|
||||
//PRIVATE
|
||||
private static $compressDir;
|
||||
private static $countDirs = 0;
|
||||
private static $countFiles = 0;
|
||||
private static $sqlPath;
|
||||
private static $zipPath;
|
||||
private static $zipFileSize;
|
||||
private static $zipArchive;
|
||||
private static $limitItems = 0;
|
||||
private static $networkFlush = false;
|
||||
private static $scanReport;
|
||||
|
||||
/**
|
||||
* Creates the zip file and adds the SQL file to the archive
|
||||
*/
|
||||
public static function create(DUP_Archive $archive, $buildProgress)
|
||||
{
|
||||
try {
|
||||
$timerAllStart = DUP_Util::getMicrotime();
|
||||
$package_zip_flush = DUP_Settings::Get('package_zip_flush');
|
||||
|
||||
self::$compressDir = rtrim(wp_normalize_path(DUP_Util::safePath($archive->PackDir)), '/');
|
||||
self::$sqlPath = DUP_Settings::getSsdirTmpPath() . "/{$archive->Package->Database->File}";
|
||||
self::$zipPath = DUP_Settings::getSsdirTmpPath() . "/{$archive->File}";
|
||||
self::$zipArchive = new ZipArchive();
|
||||
self::$networkFlush = empty($package_zip_flush) ? false : $package_zip_flush;
|
||||
|
||||
$filterDirs = empty($archive->FilterDirs) ? 'not set' : $archive->FilterDirs;
|
||||
$filterExts = empty($archive->FilterExts) ? 'not set' : $archive->FilterExts;
|
||||
$filterFiles = empty($archive->FilterFiles) ? 'not set' : $archive->FilterFiles;
|
||||
$filterOn = ($archive->FilterOn) ? 'ON' : 'OFF';
|
||||
$filterDirsFormat = rtrim(str_replace(';', "\n\t", $filterDirs));
|
||||
$filterFilesFormat = rtrim(str_replace(';', "\n\t", $filterFiles));
|
||||
$lastDirSuccess = self::$compressDir;
|
||||
|
||||
//LOAD SCAN REPORT
|
||||
$json = file_get_contents(DUP_Settings::getSsdirTmpPath() . "/{$archive->Package->NameHash}_scan.json");
|
||||
self::$scanReport = json_decode($json);
|
||||
|
||||
DUP_Log::Info("\n********************************************************************************");
|
||||
DUP_Log::Info("ARCHIVE (ZIP):");
|
||||
DUP_Log::Info("********************************************************************************");
|
||||
$isZipOpen = (self::$zipArchive->open(self::$zipPath, ZIPARCHIVE::CREATE) === true);
|
||||
if (!$isZipOpen) {
|
||||
$error_message = "Cannot open zip file with PHP ZipArchive.";
|
||||
$buildProgress->set_failed($error_message);
|
||||
DUP_Log::error($error_message, "Path location [" . self::$zipPath . "]", Dup_ErrorBehavior::LogOnly);
|
||||
$archive->Package->setStatus(DUP_PackageStatus::ERROR);
|
||||
return;
|
||||
}
|
||||
DUP_Log::Info("ARCHIVE DIR: " . self::$compressDir);
|
||||
DUP_Log::Info("ARCHIVE FILE: " . basename(self::$zipPath));
|
||||
DUP_Log::Info("FILTERS: *{$filterOn}*");
|
||||
DUP_Log::Info("DIRS:\n\t{$filterDirsFormat}");
|
||||
DUP_Log::Info("FILES:\n\t{$filterFilesFormat}");
|
||||
DUP_Log::Info("EXTS: {$filterExts}");
|
||||
|
||||
DUP_Log::Info("----------------------------------------");
|
||||
DUP_Log::Info("COMPRESSING");
|
||||
DUP_Log::Info("SIZE:\t" . self::$scanReport->ARC->Size);
|
||||
DUP_Log::Info("STATS:\tDirs " . self::$scanReport->ARC->DirCount . " | Files " . self::$scanReport->ARC->FileCount);
|
||||
|
||||
//ADD SQL
|
||||
$sql_ark_file_path = $archive->Package->getSqlArkFilePath();
|
||||
$isSQLInZip = self::$zipArchive->addFile(self::$sqlPath, $sql_ark_file_path);
|
||||
|
||||
if ($isSQLInZip) {
|
||||
DUP_Log::Info("SQL ADDED: " . basename(self::$sqlPath));
|
||||
} else {
|
||||
$error_message = "Unable to add database.sql to archive.";
|
||||
DUP_Log::error($error_message, "SQL File Path [" . self::$sqlPath . "]", Dup_ErrorBehavior::LogOnly);
|
||||
$buildProgress->set_failed($error_message);
|
||||
$archive->Package->setStatus(DUP_PackageStatus::ERROR);
|
||||
return;
|
||||
}
|
||||
self::$zipArchive->close();
|
||||
self::$zipArchive->open(self::$zipPath, ZipArchive::CREATE);
|
||||
|
||||
//ZIP DIRECTORIES
|
||||
$info = '';
|
||||
foreach (self::$scanReport->ARC->Dirs as $dir) {
|
||||
$emptyDir = $archive->getLocalDirPath($dir);
|
||||
|
||||
if (is_readable($dir) && self::$zipArchive->addEmptyDir($emptyDir)) {
|
||||
self::$countDirs++;
|
||||
$lastDirSuccess = $dir;
|
||||
} else {
|
||||
//Don't warn when dirtory is the root path
|
||||
if (strcmp($dir, rtrim(self::$compressDir, '/')) != 0) {
|
||||
$dir_path = strlen($dir) ? "[{$dir}]" : "[Read Error] - last successful read was: [{$lastDirSuccess}]";
|
||||
$info .= "DIR: {$dir_path}\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//LOG Unreadable DIR info
|
||||
if (strlen($info)) {
|
||||
DUP_Log::Info("\nWARNING: Unable to zip directories:");
|
||||
DUP_Log::Info($info);
|
||||
}
|
||||
|
||||
/**
|
||||
* count update for integrity check
|
||||
*/
|
||||
$sumItems = (self::$countDirs + self::$countFiles);
|
||||
|
||||
/* ZIP FILES: Network Flush
|
||||
* This allows the process to not timeout on fcgi
|
||||
* setups that need a response every X seconds */
|
||||
$totalFileCount = count(self::$scanReport->ARC->Files);
|
||||
$info = '';
|
||||
if (self::$networkFlush) {
|
||||
foreach (self::$scanReport->ARC->Files as $file) {
|
||||
$file_size = @filesize($file);
|
||||
$localFileName = $archive->getLocalFilePath($file);
|
||||
|
||||
if (is_readable($file)) {
|
||||
if (
|
||||
defined('DUPLICATOR_ZIP_ARCHIVE_ADD_FROM_STR') &&
|
||||
DUPLICATOR_ZIP_ARCHIVE_ADD_FROM_STR &&
|
||||
$file_size < DUP_Constants::ZIP_STRING_LIMIT &&
|
||||
self::$zipArchive->addFromString($localFileName, file_get_contents($file))
|
||||
) {
|
||||
Dup_Log::Info("Adding {$file} to zip");
|
||||
self::$limitItems++;
|
||||
self::$countFiles++;
|
||||
} elseif (self::$zipArchive->addFile($file, $localFileName)) {
|
||||
Dup_Log::Info("Adding {$file} to zip");
|
||||
self::$limitItems++;
|
||||
self::$countFiles++;
|
||||
} else {
|
||||
$info .= "FILE: [{$file}]\n";
|
||||
}
|
||||
} else {
|
||||
$info .= "FILE: [{$file}]\n";
|
||||
}
|
||||
//Trigger a flush to the web server after so many files have been loaded.
|
||||
if (self::$limitItems > DUPLICATOR_ZIP_FLUSH_TRIGGER) {
|
||||
self::$zipArchive->close();
|
||||
self::$zipArchive->open(self::$zipPath);
|
||||
self::$limitItems = 0;
|
||||
DUP_Util::fcgiFlush();
|
||||
DUP_Log::Info("Items archived [{$sumItems}] flushing response.");
|
||||
}
|
||||
|
||||
if (self::$countFiles % 500 == 0) {
|
||||
// Every so many files update the status so the UI can display
|
||||
$archive->Package->Status = SnapUtil::getWorkPercent(DUP_PackageStatus::ARCSTART, DUP_PackageStatus::ARCVALIDATION, $totalFileCount, self::$countFiles);
|
||||
$archive->Package->update();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//Normal
|
||||
foreach (self::$scanReport->ARC->Files as $file) {
|
||||
$file_size = @filesize($file);
|
||||
$localFileName = $archive->getLocalFilePath($file);
|
||||
|
||||
if (is_readable($file)) {
|
||||
if (
|
||||
defined('DUPLICATOR_ZIP_ARCHIVE_ADD_FROM_STR') &&
|
||||
DUPLICATOR_ZIP_ARCHIVE_ADD_FROM_STR &&
|
||||
$file_size < DUP_Constants::ZIP_STRING_LIMIT &&
|
||||
self::$zipArchive->addFromString($localFileName, file_get_contents($file))
|
||||
) {
|
||||
self::$countFiles++;
|
||||
} elseif (self::$zipArchive->addFile($file, $localFileName)) {
|
||||
self::$countFiles++;
|
||||
} else {
|
||||
$info .= "FILE: [{$file}]\n";
|
||||
}
|
||||
} else {
|
||||
$info .= "FILE: [{$file}]\n";
|
||||
}
|
||||
|
||||
if (self::$countFiles % 500 == 0) {
|
||||
// Every so many files update the status so the UI can display
|
||||
$archive->Package->Status = SnapUtil::getWorkPercent(DUP_PackageStatus::ARCSTART, DUP_PackageStatus::ARCVALIDATION, $totalFileCount, self::$countFiles);
|
||||
$archive->Package->update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//LOG Unreadable FILE info
|
||||
if (strlen($info)) {
|
||||
DUP_Log::Info("\nWARNING: Unable to zip files:");
|
||||
DUP_Log::Info($info);
|
||||
unset($info);
|
||||
}
|
||||
|
||||
DUP_Log::Info(print_r(self::$zipArchive, true));
|
||||
|
||||
/**
|
||||
* count update for integrity check
|
||||
*/
|
||||
$archive->file_count = self::$countDirs + self::$countFiles;
|
||||
DUP_Log::Info("FILE ADDED TO ZIP: " . $archive->file_count);
|
||||
|
||||
|
||||
//--------------------------------
|
||||
//LOG FINAL RESULTS
|
||||
DUP_Util::fcgiFlush();
|
||||
$zipCloseResult = self::$zipArchive->close();
|
||||
if ($zipCloseResult) {
|
||||
DUP_Log::Info("COMPRESSION RESULT: '{$zipCloseResult}'");
|
||||
} else {
|
||||
$error_message = "ZipArchive close failure.";
|
||||
DUP_Log::error(
|
||||
$error_message,
|
||||
"The ZipArchive engine is having issues zipping up the files on this server. For more details visit the FAQ\n"
|
||||
. "I'm getting a ZipArchive close failure when building. How can I resolve this?\n"
|
||||
. "[" . DUPLICATOR_DOCS_URL . "how-to-resolve-zip-format-related-build-issues/]",
|
||||
Dup_ErrorBehavior::LogOnly
|
||||
);
|
||||
$buildProgress->set_failed($error_message);
|
||||
$archive->Package->setStatus(DUP_PackageStatus::ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
$timerAllEnd = DUP_Util::getMicrotime();
|
||||
$timerAllSum = DUP_Util::elapsedTime($timerAllEnd, $timerAllStart);
|
||||
|
||||
self::$zipFileSize = @filesize(self::$zipPath);
|
||||
DUP_Log::Info("COMPRESSED SIZE: " . DUP_Util::byteSize(self::$zipFileSize));
|
||||
DUP_Log::Info("ARCHIVE RUNTIME: {$timerAllSum}");
|
||||
DUP_Log::Info("MEMORY STACK: " . DUP_Server::getPHPMemory());
|
||||
} catch (Exception $e) {
|
||||
$error_message = "Runtime error in class.pack.archive.zip.php constructor.";
|
||||
DUP_Log::error($error_message, "Exception: {$e}", Dup_ErrorBehavior::LogOnly);
|
||||
$buildProgress->set_failed($error_message);
|
||||
$archive->Package->setStatus(DUP_PackageStatus::ERROR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,879 @@
|
||||
<?php
|
||||
|
||||
use Duplicator\Libs\Snap\SnapDB;
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
use Duplicator\Libs\Snap\SnapURL;
|
||||
use Duplicator\Libs\Snap\SnapUtil;
|
||||
use Duplicator\Libs\Snap\SnapWP;
|
||||
|
||||
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
||||
// Exit if accessed directly
|
||||
if (!defined('DUPLICATOR_VERSION')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for gathering system information about a database
|
||||
*
|
||||
* Standard: PSR-2
|
||||
*
|
||||
* @link http://www.php-fig.org/psr/psr-2
|
||||
*/
|
||||
class DUP_DatabaseInfo
|
||||
{
|
||||
/**
|
||||
* The SQL file was built with mysqldump or PHP
|
||||
*/
|
||||
public $buildMode = 'PHP';
|
||||
/** @var string[] A unique list of all charsets table types used in the database */
|
||||
public $charSetList = array();
|
||||
/** @var string[] A unique list of all the collation table types used in the database */
|
||||
public $collationList = array();
|
||||
/** @var string[] engile list used in database tables */
|
||||
public $engineList = array();
|
||||
/**
|
||||
* Does any filtered table have an upper case character in it
|
||||
*/
|
||||
public $isTablesUpperCase = false;
|
||||
/**
|
||||
* Does the database name have any filtered characters in it
|
||||
*/
|
||||
public $isNameUpperCase = false;
|
||||
/**
|
||||
* The real name of the database
|
||||
*/
|
||||
public $name = '';
|
||||
/** @var int The full count of all tables in the database */
|
||||
public $tablesBaseCount = 0;
|
||||
/** @var int The count of tables after the tables filter has been applied */
|
||||
public $tablesFinalCount = 0;
|
||||
/** @var int multisite tables filtered count */
|
||||
public $muFilteredTableCount = 0;
|
||||
/** @var int The number of rows from all filtered tables in the database */
|
||||
public $tablesRowCount = 0;
|
||||
/** @var int The estimated data size on disk from all filtered tables in the database */
|
||||
public $tablesSizeOnDisk = 0;
|
||||
/** @var array */
|
||||
public $tablesList = array();
|
||||
|
||||
/**
|
||||
* Gets the server variable lower_case_table_names
|
||||
*
|
||||
* 0 store=lowercase; compare=sensitive (works only on case sensitive file systems )
|
||||
* 1 store=lowercase; compare=insensitive
|
||||
* 2 store=exact; compare=insensitive (works only on case INsensitive file systems )
|
||||
* default is 0/Linux ; 1/Windows
|
||||
*/
|
||||
public $lowerCaseTableNames = 0;
|
||||
/**
|
||||
* The database engine (MySQL/MariaDB/Percona)
|
||||
*
|
||||
* @var string
|
||||
* @example MariaDB
|
||||
*/
|
||||
public $dbEngine = '';
|
||||
/**
|
||||
* The simple numeric version number of the database server
|
||||
*
|
||||
* @exmaple: 5.5
|
||||
*/
|
||||
public $version = 0;
|
||||
/**
|
||||
* The full text version number of the database server
|
||||
*
|
||||
* @exmaple: 10.2 mariadb.org binary distribution
|
||||
*/
|
||||
public $versionComment = 0;
|
||||
|
||||
/**
|
||||
* @var int Number of VIEWs in the database
|
||||
*/
|
||||
public $viewCount = 0;
|
||||
|
||||
/**
|
||||
* @var int Number of PROCEDUREs in the database
|
||||
*/
|
||||
public $procCount = 0;
|
||||
|
||||
/**
|
||||
* @var int Number of PROCEDUREs in the database
|
||||
*/
|
||||
public $funcCount = 0;
|
||||
|
||||
/**
|
||||
* @var array List of triggers included in the database
|
||||
*/
|
||||
public $triggerList = array();
|
||||
/**
|
||||
* Integer field file structure of table, table name as key
|
||||
*/
|
||||
private $intFieldsStruct = array();
|
||||
/**
|
||||
* $currentIndex => processedSchemaSize
|
||||
*/
|
||||
private $indexProcessedSchemaSize = array();
|
||||
//CONSTRUCTOR
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public function addTriggers()
|
||||
{
|
||||
global $wpdb;
|
||||
if (!is_array($triggers = $wpdb->get_results("SHOW TRIGGERS", ARRAY_A))) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($triggers as $trigger) {
|
||||
$name = $trigger["Trigger"];
|
||||
$create = $wpdb->get_row("SHOW CREATE TRIGGER `{$name}`", ARRAY_N);
|
||||
$this->triggerList[$name] = array(
|
||||
"create" => "DELIMITER ;;\n" . $create[2] . ";;\nDELIMITER ;"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param stirng $name // table name
|
||||
* @param int $inaccurateRows // This data is intended as a preliminary count and therefore not necessarily accurate
|
||||
* @param int $size // This data is intended as a preliminary count and therefore not necessarily accurate
|
||||
* @param int|bool $insertedRows // This value, if other than false, is the exact line value inserted into the dump file
|
||||
*/
|
||||
public function addTableInList($name, $inaccurateRows, $size, $insertedRows = false)
|
||||
{
|
||||
$this->tablesList[$name] = array(
|
||||
'inaccurateRows' => (int) $inaccurateRows,
|
||||
'insertedRows' => (int) $insertedRows,
|
||||
'size' => (int) $size
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DUP_Database
|
||||
{
|
||||
const TABLE_CREATION_END_MARKER = "/***** TABLE CREATION END *****/\n";
|
||||
/**
|
||||
* The mysqldump allowed size difference (50MB) to memory limit in bytes. Run musqldump only on DBs smaller than memory_limit minus this value.
|
||||
*/
|
||||
const MYSQLDUMP_ALLOWED_SIZE_DIFFERENCE = 52428800;
|
||||
|
||||
/**
|
||||
* Whitelist of valid mysqldump --compatible mode values.
|
||||
*/
|
||||
const ALLOWED_COMPAT_MODES = array(
|
||||
'mysql323',
|
||||
'mysql40',
|
||||
'postgresql',
|
||||
'oracle',
|
||||
'mssql',
|
||||
'db2',
|
||||
'maxdb',
|
||||
'no_key_options',
|
||||
'no_table_options',
|
||||
'no_field_options',
|
||||
'ansi'
|
||||
);
|
||||
|
||||
/**
|
||||
* Validates and sanitizes mysqldump compatibility mode values.
|
||||
*
|
||||
* @param string|array $compatValue The compatibility value(s) to validate
|
||||
*
|
||||
* @return string Comma-separated string of valid compatibility modes, or empty string if none valid
|
||||
*/
|
||||
public static function sanitizeCompatibilityMode($compatValue)
|
||||
{
|
||||
if (is_array($compatValue)) {
|
||||
$values = array_map('sanitize_text_field', $compatValue);
|
||||
} else {
|
||||
$values = array_map('trim', explode(',', sanitize_text_field($compatValue)));
|
||||
}
|
||||
|
||||
$validated = array_intersect($values, self::ALLOWED_COMPAT_MODES);
|
||||
return implode(',', $validated);
|
||||
}
|
||||
|
||||
//PUBLIC
|
||||
public $Type = 'MySQL';
|
||||
public $Size;
|
||||
public $File;
|
||||
public $Path;
|
||||
public $FilterTables;
|
||||
public $FilterOn;
|
||||
public $Name;
|
||||
public $Compatible;
|
||||
public $Comments;
|
||||
/** @var null|bool */
|
||||
public $sameNameTableExists = null;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var DUP_DatabaseInfo
|
||||
*/
|
||||
public $info = null;
|
||||
//PROTECTED
|
||||
protected $Package;
|
||||
//PRIVATE
|
||||
private $tempDbPath;
|
||||
private $EOFMarker;
|
||||
private $networkFlush;
|
||||
/**
|
||||
* Init this object
|
||||
*/
|
||||
public function __construct($package)
|
||||
{
|
||||
$this->Package = $package;
|
||||
$this->EOFMarker = "";
|
||||
$package_zip_flush = DUP_Settings::Get('package_zip_flush');
|
||||
$this->networkFlush = empty($package_zip_flush) ? false : $package_zip_flush;
|
||||
$this->info = new DUP_DatabaseInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the database script
|
||||
*
|
||||
* @param DUP_Package $package A reference to the package that this database object belongs in
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function build($package, $errorBehavior = Dup_ErrorBehavior::ThrowException)
|
||||
{
|
||||
try {
|
||||
$this->Package = $package;
|
||||
do_action('duplicator_lite_build_database_before_start', $package);
|
||||
$time_start = DUP_Util::getMicrotime();
|
||||
$this->Package->setStatus(DUP_PackageStatus::DBSTART);
|
||||
$this->tempDbPath = DUP_Settings::getSsdirTmpPath() . "/{$this->File}";
|
||||
$package_mysqldump = DUP_Settings::Get('package_mysqldump');
|
||||
$package_phpdump_qrylimit = DUP_Settings::Get('package_phpdump_qrylimit');
|
||||
$mysqlDumpPath = DUP_DB::getMySqlDumpPath();
|
||||
$mode = DUP_DB::getBuildMode();
|
||||
$reserved_db_filepath = duplicator_get_abs_path() . '/database.sql';
|
||||
$log = "\n********************************************************************************\n";
|
||||
$log .= "DATABASE:\n";
|
||||
$log .= "********************************************************************************\n";
|
||||
$log .= "BUILD MODE: {$mode}";
|
||||
$log .= ($mode == 'PHP') ? "(query limit - {$package_phpdump_qrylimit})\n" : "\n";
|
||||
$log .= "MYSQLTIMEOUT: " . DUPLICATOR_DB_MAX_TIME . "\n";
|
||||
$log .= "MYSQLDUMP: ";
|
||||
$log .= ($mysqlDumpPath) ? "Is Supported" : "Not Supported";
|
||||
DUP_Log::Info($log);
|
||||
$log = null;
|
||||
do_action('duplicator_lite_build_database_start', $package);
|
||||
switch ($mode) {
|
||||
case 'MYSQLDUMP':
|
||||
$this->mysqlDump($mysqlDumpPath);
|
||||
break;
|
||||
case 'PHP':
|
||||
$this->phpDump($package);
|
||||
break;
|
||||
}
|
||||
|
||||
DUP_Log::Info("SQL CREATED: {$this->File}");
|
||||
$time_end = DUP_Util::getMicrotime();
|
||||
$time_sum = DUP_Util::elapsedTime($time_end, $time_start);
|
||||
//File below 10k considered incomplete
|
||||
$sql_file_size = is_file($this->tempDbPath) ? @filesize($this->tempDbPath) : 0;
|
||||
DUP_Log::Info("SQL FILE SIZE: " . DUP_Util::byteSize($sql_file_size) . " ({$sql_file_size})");
|
||||
if ($sql_file_size < 1350) {
|
||||
$error_message = "SQL file size too low.";
|
||||
$package->BuildProgress->set_failed($error_message);
|
||||
$package->setStatus(DUP_PackageStatus::ERROR);
|
||||
DUP_Log::error($error_message, "File does not look complete. Check permission on file and parent directory at [{$this->tempDbPath}]", $errorBehavior);
|
||||
do_action('duplicator_lite_build_database_fail', $package);
|
||||
} else {
|
||||
do_action('duplicator_lite_build_database_completed', $package);
|
||||
}
|
||||
|
||||
DUP_Log::Info("SQL FILE TIME: " . date("Y-m-d H:i:s"));
|
||||
DUP_Log::Info("SQL RUNTIME: {$time_sum}");
|
||||
$this->Size = is_file($this->tempDbPath) ? @filesize($this->tempDbPath) : 0;
|
||||
$this->Package->setStatus(DUP_PackageStatus::DBDONE);
|
||||
} catch (Exception $e) {
|
||||
do_action('duplicator_lite_build_database_fail', $package);
|
||||
DUP_Log::error("Runtime error in DUP_Database::Build. " . $e->getMessage(), "Exception: {$e}", $errorBehavior);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the database meta-data such as tables as all there details
|
||||
*
|
||||
* @return array Returns an array full of meta-data about the database
|
||||
*/
|
||||
public function getScannerData()
|
||||
{
|
||||
global $wpdb;
|
||||
$filterTables = isset($this->FilterTables) ? explode(',', $this->FilterTables) : array();
|
||||
$tblBaseCount = 0;
|
||||
$tblCount = 0;
|
||||
$tables = $this->getBaseTables();
|
||||
$info = array();
|
||||
$info['Status']['Success'] = is_null($tables) ? false : true;
|
||||
//DB_Case for the database name is never checked on
|
||||
$info['Status']['DB_Case'] = 'Good';
|
||||
$info['Status']['DB_Rows'] = 'Good';
|
||||
$info['Status']['DB_Size'] = 'Good';
|
||||
$info['Status']['TBL_Case'] = 'Good';
|
||||
$info['Status']['TBL_Rows'] = 'Good';
|
||||
$info['Status']['TBL_Size'] = 'Good';
|
||||
$info['Size'] = 0;
|
||||
$info['Rows'] = 0;
|
||||
$info['TableCount'] = 0;
|
||||
$info['TableList'] = array();
|
||||
$tblCaseFound = 0;
|
||||
$tblRowsFound = 0;
|
||||
$tblSizeFound = 0;
|
||||
//Grab Table Stats
|
||||
$filteredTables = array();
|
||||
$this->info->tablesList = array();
|
||||
|
||||
foreach ($tables as $table) {
|
||||
$tblBaseCount++;
|
||||
$name = $table["name"];
|
||||
if ($this->FilterOn && is_array($filterTables)) {
|
||||
if (in_array($name, $filterTables)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$size = $table['size'];
|
||||
$rows = empty($table["rows"]) ? '0' : $table["rows"];
|
||||
$info['Size'] += $size;
|
||||
$info['Rows'] += $rows;
|
||||
$info['TableList'][$name]['Case'] = preg_match('/[A-Z]/', $name) ? 1 : 0;
|
||||
$info['TableList'][$name]['Rows'] = number_format($rows);
|
||||
$info['TableList'][$name]['Size'] = DUP_Util::byteSize($size);
|
||||
$info['TableList'][$name]['USize'] = $size;
|
||||
$filteredTables[] = $name;
|
||||
|
||||
if (($qRes = $GLOBALS['wpdb']->get_var("SELECT Count(*) FROM `{$name}`")) === null) {
|
||||
$qRes = $rows;
|
||||
}
|
||||
|
||||
$row_count = (int) $qRes;
|
||||
$this->info->addTableInList($name, $rows, $size, $row_count);
|
||||
$tblCount++;
|
||||
|
||||
// Table Uppercase
|
||||
if ($info['TableList'][$name]['Case']) {
|
||||
if (!$tblCaseFound) {
|
||||
$tblCaseFound = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//Table Row Count
|
||||
if ($rows > DUPLICATOR_SCAN_DB_TBL_ROWS) {
|
||||
if (!$tblRowsFound) {
|
||||
$tblRowsFound = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//Table Size
|
||||
if ($size > DUPLICATOR_SCAN_DB_TBL_SIZE) {
|
||||
if (!$tblSizeFound) {
|
||||
$tblSizeFound = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->setInfoObj($filteredTables);
|
||||
$this->info->addTriggers();
|
||||
$info['Status']['DB_Case'] = preg_match('/[A-Z]/', $wpdb->dbname) ? 'Warn' : 'Good';
|
||||
$info['Status']['DB_Rows'] = ($info['Rows'] > DUPLICATOR_SCAN_DB_ALL_ROWS) ? 'Warn' : 'Good';
|
||||
$info['Status']['DB_Size'] = ($info['Size'] > DUPLICATOR_SCAN_DB_ALL_SIZE) ? 'Warn' : 'Good';
|
||||
$info['Status']['TBL_Case'] = ($tblCaseFound) ? 'Warn' : 'Good';
|
||||
$info['Status']['TBL_Rows'] = ($tblRowsFound) ? 'Warn' : 'Good';
|
||||
$info['Status']['TBL_Size'] = ($tblSizeFound) ? 'Warn' : 'Good';
|
||||
$info['Status']['Triggers'] = count($this->info->triggerList) > 0 ? 'Warn' : 'Good';
|
||||
$info['Status']['mysqlDumpMemoryCheck'] = self::mysqldumpMemoryCheck($info['Size']);
|
||||
$info['Status']['requiredMysqlDumpLimit'] = DUP_Util::byteSize(self::requiredMysqlDumpLimit($info['Size']));
|
||||
|
||||
$info['RawSize'] = $info['Size'];
|
||||
$info['TableList'] = $info['TableList'] or "unknown";
|
||||
$info['TableCount'] = $tblCount;
|
||||
$this->info->isTablesUpperCase = $tblCaseFound;
|
||||
$this->info->tablesBaseCount = $tblBaseCount;
|
||||
$this->info->tablesFinalCount = $tblCount;
|
||||
$this->info->tablesRowCount = (int) $info['Rows'];
|
||||
$this->info->tablesSizeOnDisk = (int) $info['Size'];
|
||||
$this->info->dbEngine = SnapDB::getDBEngine($wpdb->dbh);
|
||||
$info['EasySize'] = DUP_Util::byteSize($info['Size']) or "unknown";
|
||||
|
||||
$this->info->viewCount = count($wpdb->get_results("SHOW FULL TABLES WHERE Table_Type = 'VIEW'", ARRAY_A));
|
||||
$query = $wpdb->prepare("SHOW PROCEDURE STATUS WHERE `Db` = %s", DB_NAME);
|
||||
$this->info->procCount = count($wpdb->get_results($query, ARRAY_A));
|
||||
$query = $wpdb->prepare("SHOW FUNCTION STATUS WHERE `Db` = %s", DB_NAME);
|
||||
$this->info->funcCount = count($wpdb->get_results($query, ARRAY_A));
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array &$filteredTables Filtered names of tables to include in collation search.
|
||||
* Parameter does not change in the function, is passed by reference only to avoid copying.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setInfoObj($filteredTables)
|
||||
{
|
||||
global $wpdb;
|
||||
$this->info->buildMode = DUP_DB::getBuildMode();
|
||||
$this->info->version = DUP_DB::getVersion();
|
||||
$this->info->versionComment = DUP_DB::getVariable('version_comment');
|
||||
$this->info->lowerCaseTableNames = DUP_DB::getLowerCaseTableNames();
|
||||
$this->info->name = $wpdb->dbname;
|
||||
$this->info->isNameUpperCase = preg_match('/[A-Z]/', $wpdb->dbname) ? 1 : 0;
|
||||
$this->info->charSetList = DUP_DB::getTableCharSetList($filteredTables);
|
||||
$this->info->collationList = DUP_DB::getTableCollationList($filteredTables);
|
||||
$this->info->engineList = DUP_DB::getTableEngineList($filteredTables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return list of base tables to dump
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getBaseTables($nameOnly = false)
|
||||
{
|
||||
/** @var \wpdb $wpdb */
|
||||
global $wpdb;
|
||||
|
||||
// (TABLE_NAME REGEXP '^rte4ed_(2|6)_' OR TABLE_NAME NOT REGEXP '^rte4ed_[0-9]+_')
|
||||
$query = 'SELECT `TABLE_NAME` as `name`, `TABLE_ROWS` as `rows`, DATA_LENGTH + INDEX_LENGTH as `size` FROM `information_schema`.`tables`';
|
||||
|
||||
$where = array(
|
||||
'TABLE_SCHEMA = "' . esc_sql($wpdb->dbname) . '"',
|
||||
'TABLE_TYPE != "VIEW"'
|
||||
);
|
||||
|
||||
$query .= ' WHERE ' . implode(' AND ', $where);
|
||||
$query .= ' ORDER BY TABLE_NAME';
|
||||
|
||||
if ($nameOnly) {
|
||||
return $wpdb->get_col($query, 0);
|
||||
} else {
|
||||
return $wpdb->get_results($query, ARRAY_A);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the database script using mysqldump
|
||||
*
|
||||
* @return bool Returns true if the sql script was successfully created
|
||||
*/
|
||||
private function mysqlDump($exePath)
|
||||
{
|
||||
global $wpdb;
|
||||
require_once(DUPLICATOR_PLUGIN_PATH . 'classes/utilities/class.u.shell.php');
|
||||
$host = SnapURL::parseUrl(DB_HOST, PHP_URL_HOST);
|
||||
if (($port = SnapURL::parseUrl(DB_HOST, PHP_URL_PORT)) == false) {
|
||||
$port = '';
|
||||
}
|
||||
$name = DB_NAME;
|
||||
$mysqlcompat_on = isset($this->Compatible) && strlen($this->Compatible);
|
||||
//Build command
|
||||
$cmd = escapeshellarg($exePath);
|
||||
$cmd .= ' --no-create-db';
|
||||
$cmd .= ' --single-transaction';
|
||||
$cmd .= ' --hex-blob';
|
||||
$cmd .= ' --skip-add-drop-table';
|
||||
$cmd .= ' --routines';
|
||||
$cmd .= ' --quote-names';
|
||||
$cmd .= ' --skip-comments';
|
||||
$cmd .= ' --skip-set-charset';
|
||||
$cmd .= ' --skip-triggers';
|
||||
$cmd .= ' --allow-keywords';
|
||||
$cmd .= ' --no-tablespaces';
|
||||
//Compatibility mode
|
||||
if ($mysqlcompat_on) {
|
||||
$safeCompatible = self::sanitizeCompatibilityMode($this->Compatible);
|
||||
|
||||
if (!empty($safeCompatible)) {
|
||||
DUP_Log::Info("COMPATIBLE: [{$safeCompatible}]");
|
||||
$cmd .= " --compatible=" . escapeshellarg($safeCompatible);
|
||||
} elseif (!empty($this->Compatible)) {
|
||||
DUP_Log::Info("COMPATIBLE: Invalid value detected and skipped: [{$this->Compatible}]");
|
||||
}
|
||||
}
|
||||
|
||||
//Filter tables
|
||||
$res = $wpdb->get_results('SHOW FULL TABLES', ARRAY_N);
|
||||
$tables = array();
|
||||
$baseTables = array();
|
||||
foreach ($res as $row) {
|
||||
if (DUP_Util::isTableExists($row[0])) {
|
||||
$tables[] = $row[0];
|
||||
if ('BASE TABLE' == $row[1]) {
|
||||
$baseTables[] = $row[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
$filterTables = isset($this->FilterTables) ? explode(',', $this->FilterTables) : null;
|
||||
$tblAllCount = count($tables);
|
||||
|
||||
//$tblFilterOn = ($this->FilterOn) ? 'ON' : 'OFF';
|
||||
if (is_array($filterTables) && $this->FilterOn) {
|
||||
foreach ($tables as $key => $val) {
|
||||
if (in_array($tables[$key], $filterTables)) {
|
||||
$cmd .= " --ignore-table={$name}.{$tables[$key]} ";
|
||||
unset($tables[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$cmd .= ' -u ' . escapeshellarg(DB_USER);
|
||||
$cmd .= (DB_PASSWORD) ?
|
||||
' -p' . DUP_Shell_U::escapeshellargWindowsSupport(DB_PASSWORD) : '';
|
||||
$cmd .= ' -h ' . escapeshellarg($host);
|
||||
$cmd .= (!empty($port) && is_numeric($port) ) ?
|
||||
' -P ' . $port : '';
|
||||
$isPopenEnabled = DUP_Shell_U::isPopenEnabled();
|
||||
if (!$isPopenEnabled) {
|
||||
$cmd .= ' -r ' . escapeshellarg($this->tempDbPath);
|
||||
}
|
||||
|
||||
$cmd .= ' ' . escapeshellarg(DB_NAME);
|
||||
$cmd .= ' 2>&1';
|
||||
if ($isPopenEnabled) {
|
||||
$needToRewrite = false;
|
||||
foreach ($tables as $tableName) {
|
||||
$rewriteTableAs = $this->rewriteTableNameAs($tableName);
|
||||
if ($tableName != $rewriteTableAs) {
|
||||
$needToRewrite = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($needToRewrite) {
|
||||
$findReplaceTableNames = array();
|
||||
// orignal table name => rewrite table name
|
||||
|
||||
foreach ($tables as $tableName) {
|
||||
$rewriteTableAs = $this->rewriteTableNameAs($tableName);
|
||||
if ($tableName != $rewriteTableAs) {
|
||||
$findReplaceTableNames[$tableName] = $rewriteTableAs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$firstLine = '';
|
||||
DUP_LOG::trace("Executing mysql dump command by popen: $cmd");
|
||||
$handle = popen($cmd, "r");
|
||||
if ($handle) {
|
||||
$sql_header = "/* DUPLICATOR-LITE (MYSQL-DUMP BUILD MODE) MYSQL SCRIPT CREATED ON : " . @date("Y-m-d H:i:s") . " */\n\n";
|
||||
file_put_contents($this->tempDbPath, $sql_header, FILE_APPEND);
|
||||
while (!feof($handle)) {
|
||||
$line = fgets($handle);
|
||||
//get ony one line
|
||||
if ($line) {
|
||||
if (empty($firstLine)) {
|
||||
$firstLine = $line;
|
||||
if (false !== stripos($line, 'Using a password on the command line interface can be insecure')) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ($needToRewrite) {
|
||||
$replaceCount = 1;
|
||||
if (preg_match('/CREATE TABLE `(.*?)`/', $line, $matches)) {
|
||||
$tableName = $matches[1];
|
||||
if (isset($findReplaceTableNames[$tableName])) {
|
||||
$rewriteTableAs = $findReplaceTableNames[$tableName];
|
||||
$line = str_replace('CREATE TABLE `' . $tableName . '`', 'CREATE TABLE `' . $rewriteTableAs . '`', $line, $replaceCount);
|
||||
}
|
||||
} elseif (preg_match('/INSERT INTO `(.*?)`/', $line, $matches)) {
|
||||
$tableName = $matches[1];
|
||||
if (isset($findReplaceTableNames[$tableName])) {
|
||||
$rewriteTableAs = $findReplaceTableNames[$tableName];
|
||||
$line = str_replace('INSERT INTO `' . $tableName . '`', 'INSERT INTO `' . $rewriteTableAs . '`', $line, $replaceCount);
|
||||
}
|
||||
} elseif (preg_match('/LOCK TABLES `(.*?)`/', $line, $matches)) {
|
||||
$tableName = $matches[1];
|
||||
if (isset($findReplaceTableNames[$tableName])) {
|
||||
$rewriteTableAs = $findReplaceTableNames[$tableName];
|
||||
$line = str_replace('LOCK TABLES `' . $tableName . '`', 'LOCK TABLES `' . $rewriteTableAs . '`', $line, $replaceCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
file_put_contents($this->tempDbPath, $line, FILE_APPEND);
|
||||
$output = "Ran from {$exePath}";
|
||||
}
|
||||
}
|
||||
$mysqlResult = pclose($handle);
|
||||
} else {
|
||||
$output = '';
|
||||
}
|
||||
|
||||
// Password bug > 5.6 (@see http://bugs.mysql.com/bug.php?id=66546)
|
||||
if (empty($output) && trim($firstLine) === 'Warning: Using a password on the command line interface can be insecure.') {
|
||||
$output = '';
|
||||
}
|
||||
} else {
|
||||
DUP_LOG::trace("Executing mysql dump command $cmd");
|
||||
exec($cmd, $output, $mysqlResult);
|
||||
$output = implode("\n", $output);
|
||||
// Password bug > 5.6 (@see http://bugs.mysql.com/bug.php?id=66546)
|
||||
if (trim($output) === 'Warning: Using a password on the command line interface can be insecure.') {
|
||||
$output = '';
|
||||
}
|
||||
$output = (strlen($output)) ? $output : "Ran from {$exePath}";
|
||||
$tblCreateCount = count($tables);
|
||||
$tblFilterCount = $tblAllCount - $tblCreateCount;
|
||||
//DEBUG
|
||||
//DUP_Log::Info("COMMAND: {$cmd}");
|
||||
DUP_Log::Info("FILTERED: [{$this->FilterTables}]");
|
||||
DUP_Log::Info("RESPONSE: {$output}");
|
||||
DUP_Log::Info("TABLES: total:{$tblAllCount} | filtered:{$tblFilterCount} | create:{$tblCreateCount}");
|
||||
}
|
||||
|
||||
$sql_footer = "\n\n/* Duplicator WordPress Timestamp: " . date("Y-m-d H:i:s") . "*/\n";
|
||||
$sql_footer .= "/* " . DUPLICATOR_DB_EOF_MARKER . " */\n";
|
||||
file_put_contents($this->tempDbPath, $sql_footer, FILE_APPEND);
|
||||
if ($mysqlResult !== 0) {
|
||||
/**
|
||||
* -1 error command shell
|
||||
* mysqldump return
|
||||
* 0 - Success
|
||||
* 1 - Warning
|
||||
* 2 - Exception
|
||||
*/
|
||||
DUP_Log::Info('MYSQL DUMP ERROR ' . print_r($mysqlResult, true));
|
||||
DUP_Log::error(
|
||||
__('Shell mysql dump error. Change SQL Mode to the "PHP Code" in the Duplicator > Settings > Backups.', 'duplicator'),
|
||||
implode("\n", SnapIO::getLastLinesOfFile(
|
||||
$this->tempDbPath,
|
||||
DUPLICATOR_DB_MYSQLDUMP_ERROR_CONTAINING_LINE_COUNT,
|
||||
DUPLICATOR_DB_MYSQLDUMP_ERROR_CHARS_IN_LINE_COUNT
|
||||
)),
|
||||
Dup_ErrorBehavior::ThrowException
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if database size is within the mysqldump size limit
|
||||
*
|
||||
* @param int $dbSize Size of the database to check
|
||||
*
|
||||
* @return bool Returns true if DB size is within the mysqldump size limit, otherwise false
|
||||
*/
|
||||
protected static function mysqldumpMemoryCheck($dbSize)
|
||||
{
|
||||
$mem = SnapUtil::phpIniGet('memory_limit', false);
|
||||
$memInBytes = SnapUtil::convertToBytes($mem);
|
||||
|
||||
// If the memory limit is unknown or unlimited (-1), return true
|
||||
if ($mem === false || $memInBytes <= 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (self::requiredMysqlDumpLimit($dbSize) <= $memInBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return mysql required limit
|
||||
*
|
||||
* @param int $dbSize Size of the database to check
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected static function requiredMysqlDumpLimit($dbSize)
|
||||
{
|
||||
return $dbSize + self::MYSQLDUMP_ALLOWED_SIZE_DIFFERENCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the database script using php
|
||||
*
|
||||
* @return bool Returns true if the sql script was successfully created
|
||||
*/
|
||||
private function phpDump($package)
|
||||
{
|
||||
global $wpdb;
|
||||
$query = $wpdb->prepare("SET session wait_timeout = %d", DUPLICATOR_DB_MAX_TIME);
|
||||
$wpdb->query($query);
|
||||
if (($handle = fopen($this->tempDbPath, 'w+')) == false) {
|
||||
DUP_Log::error('[PHP DUMP] ERROR Can\'t open sbStorePath "' . $this->tempDbPath . '"', Dup_ErrorBehavior::ThrowException);
|
||||
}
|
||||
$tables = $wpdb->get_col("SHOW FULL TABLES WHERE Table_Type != 'VIEW'");
|
||||
$filterTables = isset($this->FilterTables) ? explode(',', $this->FilterTables) : null;
|
||||
$tblAllCount = count($tables);
|
||||
//$tblFilterOn = ($this->FilterOn) ? 'ON' : 'OFF';
|
||||
$qryLimit = DUP_Settings::Get('package_phpdump_qrylimit');
|
||||
if (is_array($filterTables) && $this->FilterOn) {
|
||||
foreach ($tables as $key => $val) {
|
||||
if (in_array($tables[$key], $filterTables)) {
|
||||
unset($tables[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
$tblCreateCount = count($tables);
|
||||
$tblFilterCount = $tblAllCount - $tblCreateCount;
|
||||
DUP_Log::Info("TABLES: total:{$tblAllCount} | filtered:{$tblFilterCount} | create:{$tblCreateCount}");
|
||||
DUP_Log::Info("FILTERED: [{$this->FilterTables}]");
|
||||
//Added 'NO_AUTO_VALUE_ON_ZERO' at plugin version 1.2.12 to fix :
|
||||
//**ERROR** database error write 'Invalid default value for for older mysql versions
|
||||
$sql_header = "/* DUPLICATOR-LITE (PHP BUILD MODE) MYSQL SCRIPT CREATED ON : " . @date("Y-m-d H:i:s") . " */\n\n";
|
||||
$sql_header .= "/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;\n\n";
|
||||
$sql_header .= "SET FOREIGN_KEY_CHECKS = 0;\n\n";
|
||||
fwrite($handle, $sql_header);
|
||||
//BUILD CREATES:
|
||||
//All creates must be created before inserts do to foreign key constraints
|
||||
foreach ($tables as $table) {
|
||||
$rewrite_table_as = $this->rewriteTableNameAs($table);
|
||||
$create = $wpdb->get_row("SHOW CREATE TABLE `{$table}`", ARRAY_N);
|
||||
$count = 1;
|
||||
$create_table_query = str_replace($table, $rewrite_table_as, $create[1], $count);
|
||||
@fwrite($handle, "{$create_table_query};\n\n");
|
||||
}
|
||||
|
||||
|
||||
$query = $wpdb->prepare("SHOW PROCEDURE STATUS WHERE `Db` = %s", $wpdb->dbname);
|
||||
$procedures = $wpdb->get_col($query, 1);
|
||||
if (count($procedures)) {
|
||||
foreach ($procedures as $procedure) {
|
||||
@fwrite($handle, "DELIMITER ;;\n");
|
||||
$create = $wpdb->get_row("SHOW CREATE PROCEDURE `{$procedure}`", ARRAY_N);
|
||||
@fwrite($handle, "{$create[2]} ;;\n");
|
||||
@fwrite($handle, "DELIMITER ;\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
$query = $wpdb->prepare("SHOW FUNCTION STATUS WHERE `Db` = %s", $wpdb->dbname);
|
||||
$functions = $wpdb->get_col($query, 1);
|
||||
if (count($functions)) {
|
||||
foreach ($functions as $function) {
|
||||
@fwrite($handle, "DELIMITER ;;\n");
|
||||
$create = $wpdb->get_row("SHOW CREATE FUNCTION `{$function}`", ARRAY_N);
|
||||
@fwrite($handle, "{$create[2]} ;;\n");
|
||||
@fwrite($handle, "DELIMITER ;\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
$views = $wpdb->get_col("SHOW FULL TABLES WHERE Table_Type = 'VIEW'");
|
||||
if (count($views)) {
|
||||
foreach ($views as $view) {
|
||||
$create = $wpdb->get_row("SHOW CREATE VIEW `{$view}`", ARRAY_N);
|
||||
@fwrite($handle, "{$create[1]};\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
@fwrite($handle, self::TABLE_CREATION_END_MARKER . "\n");
|
||||
|
||||
$table_count = count($tables);
|
||||
$table_number = 0;
|
||||
//BUILD INSERTS:
|
||||
//Create Insert in 100 row increments to better handle memory
|
||||
foreach ($tables as $table) {
|
||||
$table_number++;
|
||||
if ($table_number % 2 == 0) {
|
||||
$this->Package->Status = SnapUtil::getWorkPercent(DUP_PackageStatus::DBSTART, DUP_PackageStatus::DBDONE, $table_count, $table_number);
|
||||
$this->Package->update();
|
||||
}
|
||||
|
||||
$row_count = $wpdb->get_var("SELECT Count(*) FROM `{$table}`");
|
||||
$rewrite_table_as = $this->rewriteTableNameAs($table);
|
||||
|
||||
if ($row_count > $qryLimit) {
|
||||
$row_count = ceil($row_count / $qryLimit);
|
||||
} elseif ($row_count > 0) {
|
||||
$row_count = 1;
|
||||
}
|
||||
|
||||
if ($row_count >= 1) {
|
||||
fwrite($handle, "\n/* INSERT TABLE DATA: {$table} */\n");
|
||||
}
|
||||
|
||||
for ($i = 0; $i < $row_count; $i++) {
|
||||
$sql = "";
|
||||
$limit = $i * $qryLimit;
|
||||
$query = "SELECT * FROM `{$table}` LIMIT {$limit}, {$qryLimit}";
|
||||
$rows = $wpdb->get_results($query, ARRAY_A);
|
||||
$select_last_error = $wpdb->last_error;
|
||||
if ('' !== $select_last_error) {
|
||||
$fix = esc_html__('Please contact your DataBase administrator to fix the error.', 'duplicator');
|
||||
$errorMessage = $select_last_error . ' ' . $fix . '.';
|
||||
$package->BuildProgress->set_failed($errorMessage);
|
||||
$package->BuildProgress->failed = true;
|
||||
$package->failed = true;
|
||||
$package->Status = DUP_PackageStatus::ERROR;
|
||||
$package->Update();
|
||||
DUP_Log::error($select_last_error, $fix, Dup_ErrorBehavior::ThrowException);
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_array($rows)) {
|
||||
foreach ($rows as $row) {
|
||||
$sql .= "INSERT INTO `{$rewrite_table_as}` VALUES(";
|
||||
$num_values = count($row);
|
||||
$num_counter = 1;
|
||||
foreach ($row as $value) {
|
||||
if (is_null($value) || !isset($value)) {
|
||||
($num_values == $num_counter) ? $sql .= 'NULL' : $sql .= 'NULL, ';
|
||||
} else {
|
||||
($num_values == $num_counter) ? $sql .= '"' . DUP_DB::escSQL($value, true) . '"' : $sql .= '"' . DUP_DB::escSQL($value, true) . '", ';
|
||||
}
|
||||
$num_counter++;
|
||||
}
|
||||
$sql .= ");\n";
|
||||
}
|
||||
fwrite($handle, $sql);
|
||||
}
|
||||
}
|
||||
|
||||
//Flush buffer if enabled
|
||||
if ($this->networkFlush) {
|
||||
DUP_Util::fcgiFlush();
|
||||
}
|
||||
$sql = null;
|
||||
$rows = null;
|
||||
}
|
||||
|
||||
$sql_footer = "\nSET FOREIGN_KEY_CHECKS = 1; \n\n";
|
||||
$sql_footer .= "/* Duplicator WordPress Timestamp: " . date("Y-m-d H:i:s") . "*/\n";
|
||||
$sql_footer .= "/* " . DUPLICATOR_DB_EOF_MARKER . " */\n";
|
||||
fwrite($handle, $sql_footer);
|
||||
$wpdb->flush();
|
||||
fclose($handle);
|
||||
}
|
||||
|
||||
private function rewriteTableNameAs($table)
|
||||
{
|
||||
$table_prefix = $this->getTablePrefix();
|
||||
if (!isset($this->sameNameTableExists)) {
|
||||
global $wpdb;
|
||||
$this->sameNameTableExists = false;
|
||||
$all_tables = $wpdb->get_col("SHOW FULL TABLES WHERE Table_Type != 'VIEW'");
|
||||
foreach ($all_tables as $table_name) {
|
||||
if (strtolower($table_name) != $table_name && in_array(strtolower($table_name), $all_tables)) {
|
||||
$this->sameNameTableExists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (false === $this->sameNameTableExists && 0 === stripos($table, $table_prefix) && 0 !== strpos($table, $table_prefix)) {
|
||||
$post_fix = substr($table, strlen($table_prefix));
|
||||
$rewrite_table_name = $table_prefix . $post_fix;
|
||||
} else {
|
||||
$rewrite_table_name = $table;
|
||||
}
|
||||
return $rewrite_table_name;
|
||||
}
|
||||
|
||||
private function getTablePrefix()
|
||||
{
|
||||
global $wpdb;
|
||||
$table_prefix = (is_multisite() && !defined('MULTISITE')) ? $wpdb->base_prefix : $wpdb->get_blog_prefix(0);
|
||||
return $table_prefix;
|
||||
}
|
||||
|
||||
public function getUrl()
|
||||
{
|
||||
return DUP_Settings::getSsdirUrl() . "/" . $this->File;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
1926
html/wp-content/plugins/duplicator/classes/package/class.pack.php
Normal file
1926
html/wp-content/plugins/duplicator/classes/package/class.pack.php
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,268 @@
|
||||
<?php
|
||||
|
||||
use Duplicator\Libs\DupArchive\DupArchiveEngine;
|
||||
use Duplicator\Libs\DupArchive\DupArchiveLoggerBase;
|
||||
use Duplicator\Libs\DupArchive\States\DupArchiveExpandState;
|
||||
use Duplicator\Libs\Snap\SnapUtil;
|
||||
|
||||
require_once(DUPLICATOR_PLUGIN_PATH . 'classes/package/duparchive/class.pack.archive.duparchive.state.expand.php');
|
||||
require_once(DUPLICATOR_PLUGIN_PATH . 'classes/package/duparchive/class.pack.archive.duparchive.state.create.php');
|
||||
|
||||
class DUP_DupArchive_Logger extends DupArchiveLoggerBase
|
||||
{
|
||||
public function log($s, $flush = false, $callingFunctionOverride = null)
|
||||
{
|
||||
DUP_Log::Trace($s, true, $callingFunctionOverride);
|
||||
}
|
||||
}
|
||||
|
||||
class DUP_DupArchive
|
||||
{
|
||||
// Using a worker time override since evidence shorter time works much
|
||||
const WORKER_TIME_IN_SEC = 10;
|
||||
/**
|
||||
* CREATE
|
||||
* Creates the zip file and adds the SQL file to the archive
|
||||
*/
|
||||
public static function create(DUP_Archive $archive, $buildProgress, $package)
|
||||
{
|
||||
/* @var $buildProgress DUP_Build_Progress */
|
||||
|
||||
DUP_LOG::trace("start");
|
||||
try {
|
||||
DUP_Log::Open($package->NameHash);
|
||||
if ($buildProgress->retries > DUPLICATOR_MAX_BUILD_RETRIES) {
|
||||
$error_msg = __('Backup build appears stuck so marking Backup as failed. Is the Max Worker Time set too high?.', 'duplicator');
|
||||
DUP_Log::error(esc_html__('Build Failure', 'duplicator'), esc_html($error_msg), Dup_ErrorBehavior::LogOnly);
|
||||
//$buildProgress->failed = true;
|
||||
$buildProgress->set_failed($error_msg);
|
||||
$package->setStatus(DUP_PackageStatus::ERROR);
|
||||
;
|
||||
return true;
|
||||
} else {
|
||||
// If all goes well retries will be reset to 0 at the end of this function.
|
||||
$buildProgress->retries++;
|
||||
$package->update();
|
||||
}
|
||||
|
||||
$done = false;
|
||||
DupArchiveEngine::init(new DUP_DupArchive_Logger(), $archive->getTargetRootPath());
|
||||
DUP_Package::safeTmpCleanup(true);
|
||||
$compressDir = rtrim(DUP_Util::safePath($archive->PackDir), '/');
|
||||
$sqlPath = DUP_Settings::getSsdirTmpPath() . "/{$package->Database->File}";
|
||||
$archivePath = DUP_Settings::getSsdirTmpPath() . "/{$archive->File}";
|
||||
$scanFilepath = DUP_Settings::getSsdirTmpPath() . "/{$package->NameHash}_scan.json";
|
||||
$skipArchiveFinalization = false;
|
||||
$json = '';
|
||||
if (file_exists($scanFilepath)) {
|
||||
$json = file_get_contents($scanFilepath);
|
||||
if (empty($json)) {
|
||||
$errorText = __("Scan file $scanFilepath is empty!", 'duplicator');
|
||||
$fixText = __("Click on \"Resolve This\" button to fix the JSON settings.", 'duplicator');
|
||||
DUP_Log::Trace($errorText);
|
||||
DUP_Log::error(esc_html($errorText) . " **RECOMMENDATION: " . esc_html($fixText) . ".", '', Dup_ErrorBehavior::LogOnly);
|
||||
//$buildProgress->failed = true;
|
||||
$buildProgress->set_failed($errorText);
|
||||
$package->setStatus(DUP_PackageStatus::ERROR);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
DUP_Log::trace("**** scan file $scanFilepath doesn't exist!!");
|
||||
$errorMessage = sprintf(__("ERROR: Can't find Scanfile %s. Please ensure there no non-English characters in the Backup or schedule name.", 'duplicator'), $scanFilepath);
|
||||
DUP_Log::error($errorMessage, '', Dup_ErrorBehavior::LogOnly);
|
||||
//$buildProgress->failed = true;
|
||||
$buildProgress->set_failed($errorMessage);
|
||||
$package->setStatus(DUP_PackageStatus::ERROR);
|
||||
return true;
|
||||
}
|
||||
|
||||
Dup_Log::TraceObject("buildprogress object", $buildProgress, false);
|
||||
$scanReport = json_decode($json);
|
||||
if ($buildProgress->archive_started == false) {
|
||||
$filterDirs = empty($archive->FilterDirs) ? 'not set' : $archive->FilterDirs;
|
||||
$filterExts = empty($archive->FilterExts) ? 'not set' : $archive->FilterExts;
|
||||
$filterFiles = empty($archive->FilterFiles) ? 'not set' : $archive->FilterFiles;
|
||||
$filterOn = ($archive->FilterOn) ? 'ON' : 'OFF';
|
||||
$filterDirsFormat = rtrim(str_replace(';', "\n\t", $filterDirs));
|
||||
$filterFilesFormat = rtrim(str_replace(';', "\n\t", $filterFiles));
|
||||
DUP_Log::info("\n********************************************************************************");
|
||||
DUP_Log::info("ARCHIVE Type=DUP Mode=DupArchive");
|
||||
DUP_Log::info("********************************************************************************");
|
||||
DUP_Log::info("ARCHIVE DIR: " . $compressDir);
|
||||
DUP_Log::info("ARCHIVE FILE: " . basename($archivePath));
|
||||
DUP_Log::info("FILTERS: *{$filterOn}*");
|
||||
DUP_Log::Info("DIRS:\n\t{$filterDirsFormat}");
|
||||
DUP_Log::Info("FILES:\n\t{$filterFilesFormat}");
|
||||
DUP_Log::info("EXTS: {$filterExts}");
|
||||
DUP_Log::info("----------------------------------------");
|
||||
DUP_Log::info("COMPRESSING");
|
||||
DUP_Log::info("SIZE:\t" . $scanReport->ARC->Size);
|
||||
DUP_Log::info("STATS:\tDirs " . $scanReport->ARC->DirCount . " | Files " . $scanReport->ARC->FileCount . " | Total " . $scanReport->ARC->FullCount);
|
||||
if (($scanReport->ARC->DirCount == '') || ($scanReport->ARC->FileCount == '') || ($scanReport->ARC->FullCount == '')) {
|
||||
$error_message = 'Invalid Scan Report Detected';
|
||||
DUP_Log::error($error_message, 'Invalid Scan Report Detected', Dup_ErrorBehavior::LogOnly);
|
||||
$buildProgress->set_failed($error_message);
|
||||
$package->setStatus(DUP_PackageStatus::ERROR);
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
DupArchiveEngine::createArchive($archivePath, true);
|
||||
$sql_ark_file_path = $package->getSqlArkFilePath();
|
||||
DupArchiveEngine::addRelativeFileToArchiveST($archivePath, $sqlPath, $sql_ark_file_path);
|
||||
} catch (Exception $ex) {
|
||||
$error_message = 'Error adding database.sql to archive';
|
||||
DUP_Log::error($error_message, $ex->getMessage(), Dup_ErrorBehavior::LogOnly);
|
||||
$buildProgress->set_failed($error_message);
|
||||
$package->setStatus(DUP_PackageStatus::ERROR);
|
||||
return true;
|
||||
}
|
||||
|
||||
$buildProgress->archive_started = true;
|
||||
$buildProgress->retries = 0;
|
||||
$createState = DUP_DupArchive_Create_State::createNew($archivePath, $compressDir, self::WORKER_TIME_IN_SEC, true, true);
|
||||
$createState->throttleDelayInUs = 0;
|
||||
$createState->save();
|
||||
$package->Update();
|
||||
}
|
||||
|
||||
try {
|
||||
$createState = DUP_DupArchive_Create_State::get_instance();
|
||||
if ($buildProgress->retries > 1) {
|
||||
$createState->isRobust = true;
|
||||
$createState->save();
|
||||
}
|
||||
|
||||
if ($createState->working) {
|
||||
DUP_LOG::Trace("Create state is working");
|
||||
DupArchiveEngine::addItemsToArchive($createState, $scanReport->ARC);
|
||||
$buildProgress->set_build_failures($createState->failures);
|
||||
if ($createState->isCriticalFailurePresent()) {
|
||||
throw new Exception($createState->getFailureSummary());
|
||||
}
|
||||
|
||||
$totalFileCount = count($scanReport->ARC->Files);
|
||||
$package->Status = SnapUtil::getWorkPercent(DUP_PackageStatus::ARCSTART, DUP_PackageStatus::ARCVALIDATION, $totalFileCount, $createState->currentFileIndex);
|
||||
$buildProgress->retries = 0;
|
||||
$createState->save();
|
||||
DUP_LOG::TraceObject("Stored Create State", $createState);
|
||||
DUP_LOG::TraceObject('Stored build_progress', $package->BuildProgress);
|
||||
if ($createState->working == false) {
|
||||
// Want it to do the final cleanup work in an entirely new thread so return immediately
|
||||
$skipArchiveFinalization = true;
|
||||
DUP_LOG::TraceObject("Done build phase. Create State=", $createState);
|
||||
}
|
||||
}
|
||||
} catch (Exception $ex) {
|
||||
$message = __('Problem adding items to archive.', 'duplicator') . ' ' . $ex->getMessage();
|
||||
DUP_Log::error(__('Problems adding items to archive.', 'duplicator'), $message, Dup_ErrorBehavior::LogOnly);
|
||||
DUP_Log::TraceObject($message . " EXCEPTION:", $ex);
|
||||
//$buildProgress->failed = true;
|
||||
$buildProgress->set_failed($message);
|
||||
$package->setStatus(DUP_PackageStatus::ERROR);
|
||||
return true;
|
||||
}
|
||||
|
||||
//-- Final Wrapup of the Archive
|
||||
if ((!$skipArchiveFinalization) && ($createState->working == false)) {
|
||||
DUP_LOG::Trace("Create state is not working and not skip archive finalization");
|
||||
if (!$buildProgress->installer_built) {
|
||||
if ($package->Installer->build($package, false)) {
|
||||
$package->Runtime = -1;
|
||||
$package->ExeSize = DUP_Util::byteSize($package->Installer->Size);
|
||||
$package->ZipSize = DUP_Util::byteSize($package->Archive->Size);
|
||||
$package->update();
|
||||
} else {
|
||||
$package->update();
|
||||
return;
|
||||
}
|
||||
|
||||
DUP_Log::Trace("Installer has been built so running expand now");
|
||||
$expandState = DUP_DupArchive_Expand_State::getInstance(true);
|
||||
$expandState->archivePath = $archivePath;
|
||||
$expandState->working = true;
|
||||
$expandState->timeSliceInSecs = self::WORKER_TIME_IN_SEC;
|
||||
$expandState->basePath = DUP_Settings::getSsdirTmpPath() . '/validate';
|
||||
$expandState->throttleDelayInUs = 0;
|
||||
// RSR TODO
|
||||
$expandState->validateOnly = true;
|
||||
$expandState->validationType = DupArchiveExpandState::VALIDATION_NONE;
|
||||
$expandState->working = true;
|
||||
$expandState->expectedDirectoryCount = count($scanReport->ARC->Dirs) - $createState->skippedDirectoryCount + $package->Installer->numDirsAdded;
|
||||
$expandState->expectedFileCount = 1 + count($scanReport->ARC->Files) + 1 - $createState->skippedFileCount + $package->Installer->numFilesAdded;
|
||||
// database.sql will be in there
|
||||
|
||||
$expandState->save();
|
||||
$sfc = count($scanReport->ARC->Files);
|
||||
$nfa = $package->Installer->numFilesAdded;
|
||||
Dup_Log::trace("####scan files {$sfc} skipped files {$createState->skippedFileCount} num files added {$nfa}");
|
||||
DUP_LOG::traceObject("EXPAND STATE AFTER SAVE", $expandState);
|
||||
} else {
|
||||
try {
|
||||
$expandState = DUP_DupArchive_Expand_State::getInstance();
|
||||
if ($buildProgress->retries > 1) {
|
||||
// Indicates it had problems before so move into robustness mode
|
||||
$expandState->isRobust = true;
|
||||
$expandState->save();
|
||||
}
|
||||
|
||||
DUP_Log::traceObject('Resumed validation expand state', $expandState);
|
||||
DupArchiveEngine::expandArchive($expandState);
|
||||
$buildProgress->set_validation_failures($expandState->failures);
|
||||
$totalFileCount = count($scanReport->ARC->Files);
|
||||
$archiveSize = @filesize($expandState->archivePath);
|
||||
|
||||
$package->Status = SnapUtil::getWorkPercent(
|
||||
DUP_PackageStatus::ARCVALIDATION,
|
||||
DUP_PackageStatus::COMPLETE,
|
||||
$archiveSize,
|
||||
$expandState->archiveOffset
|
||||
);
|
||||
DUP_LOG::TraceObject("package status after expand=", $package->Status);
|
||||
DUP_LOG::Trace("archive size:{$archiveSize} expand offset:{$expandState->archiveOffset}");
|
||||
} catch (Exception $ex) {
|
||||
DUP_Log::Trace('Exception:' . $ex->getMessage() . ':' . $ex->getTraceAsString());
|
||||
$buildProgress->set_failed('Error validating archive');
|
||||
$package->setStatus(DUP_PackageStatus::ERROR);
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($expandState->isCriticalFailurePresent()) {
|
||||
// Fail immediately if critical failure present - even if havent completed processing the entire archive.
|
||||
$error_message = __('Critical failure present in validation', 'duplicator');
|
||||
DUP_Log::error($error_message, $expandState->getFailureSummary(), Dup_ErrorBehavior::LogOnly);
|
||||
$buildProgress->set_failed($error_message);
|
||||
return true;
|
||||
} elseif (!$expandState->working) {
|
||||
$buildProgress->archive_built = true;
|
||||
$buildProgress->retries = 0;
|
||||
$package->update();
|
||||
$timerAllEnd = DUP_Util::getMicrotime();
|
||||
$timerAllSum = DUP_Util::elapsedTime($timerAllEnd, $package->TimerStart);
|
||||
DUP_LOG::traceObject("create state", $createState);
|
||||
$archiveFileSize = @filesize($archivePath);
|
||||
DUP_Log::info("COMPRESSED SIZE: " . DUP_Util::byteSize($archiveFileSize));
|
||||
DUP_Log::info("ARCHIVE RUNTIME: {$timerAllSum}");
|
||||
DUP_Log::info("MEMORY STACK: " . DUP_Server::getPHPMemory());
|
||||
DUP_Log::info("CREATE WARNINGS: " . $createState->getFailureSummary(false, true));
|
||||
DUP_Log::info("VALIDATION WARNINGS: " . $expandState->getFailureSummary(false, true));
|
||||
$archive->file_count = $expandState->fileWriteCount + $expandState->directoryWriteCount;
|
||||
$package->update();
|
||||
$done = true;
|
||||
} else {
|
||||
$expandState->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception $ex) {
|
||||
// Have to have a catchall since the main system that calls this function is not prepared to handle exceptions
|
||||
DUP_Log::trace('Top level create Exception:' . $ex->getMessage() . ':' . $ex->getTraceAsString());
|
||||
//$buildProgress->failed = true;
|
||||
$buildProgress->set_failed('Error encoundtered creating archive. See Backup log');
|
||||
return true;
|
||||
}
|
||||
|
||||
$buildProgress->retries = 0;
|
||||
return $done;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
use Duplicator\Libs\DupArchive\States\DupArchiveCreateState;
|
||||
|
||||
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
||||
|
||||
class DUP_DupArchive_Create_State extends DupArchiveCreateState
|
||||
{
|
||||
/**
|
||||
* Class constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->throttleDelayInUs = 10;
|
||||
}
|
||||
|
||||
// Only one active package so straightforward
|
||||
// public static function createFromPackage(&$package)
|
||||
public static function get_instance()
|
||||
{
|
||||
$instance = new DUP_DupArchive_Create_State();
|
||||
$data = DUP_Settings::Get('duparchive_create_state');
|
||||
DUP_Util::objectCopy($data, $instance);
|
||||
$instance->startTimestamp = time();
|
||||
DUP_Log::TraceObject("retrieving create state", $instance);
|
||||
return $instance;
|
||||
}
|
||||
|
||||
public static function createNew($archivePath, $basePath, $timeSliceInSecs, $isCompressed, $setArchiveOffsetToEndOfArchive)
|
||||
{
|
||||
$instance = new DUP_DupArchive_Create_State();
|
||||
if ($setArchiveOffsetToEndOfArchive) {
|
||||
$instance->archiveOffset = filesize($archivePath);
|
||||
} else {
|
||||
$instance->archiveOffset = 0;
|
||||
}
|
||||
|
||||
$instance->archivePath = $archivePath;
|
||||
$instance->basePath = $basePath;
|
||||
$instance->currentDirectoryIndex = 0;
|
||||
$instance->currentFileOffset = 0;
|
||||
$instance->currentFileIndex = 0;
|
||||
$instance->failures = array();
|
||||
$instance->globSize = DupArchiveCreateState::DEFAULT_GLOB_SIZE;
|
||||
$instance->isCompressed = $isCompressed;
|
||||
$instance->timeSliceInSecs = $timeSliceInSecs;
|
||||
$instance->working = true;
|
||||
$instance->skippedDirectoryCount = 0;
|
||||
$instance->skippedFileCount = 0;
|
||||
$instance->startTimestamp = time();
|
||||
return $instance;
|
||||
}
|
||||
|
||||
public function addFailure($type, $subject, $description, $isCritical = false)
|
||||
{
|
||||
parent::addFailure($type, $subject, $description, $isCritical);
|
||||
}
|
||||
|
||||
public function save()
|
||||
{
|
||||
DUP_Log::TraceObject("Saving create state", $this);
|
||||
DUP_Settings::Set('duparchive_create_state', $this);
|
||||
DUP_Settings::Save();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
use Duplicator\Libs\DupArchive\States\DupArchiveExpandState;
|
||||
use Duplicator\Libs\Snap\SnapJson;
|
||||
|
||||
class DUP_DupArchive_Expand_State extends DupArchiveExpandState
|
||||
{
|
||||
public static function getInstance($reset = false)
|
||||
{
|
||||
$instance = new DUP_DupArchive_Expand_State();
|
||||
if ($reset) {
|
||||
$instance->initMembers();
|
||||
} else {
|
||||
$instance->loadMembers();
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
private function loadMembers()
|
||||
{
|
||||
/** @var object $data */
|
||||
$data = DUP_Settings::Get('duparchive_expand_state');
|
||||
DUP_LOG::traceObject("****RAW EXPAND STATE LOADED****", $data);
|
||||
if ($data->currentFileHeaderString != null) {
|
||||
$this->currentFileHeader = DUP_JSON::decode($data->currentFileHeaderString);
|
||||
} else {
|
||||
$this->currentFileHeader = null;
|
||||
}
|
||||
|
||||
if ($data->archiveHeaderString != null) {
|
||||
$this->archiveHeader = DUP_JSON::decode($data->archiveHeaderString);
|
||||
} else {
|
||||
$this->archiveHeader = null;
|
||||
}
|
||||
|
||||
if ($data->failuresString) {
|
||||
$this->failures = DUP_JSON::decode($data->failuresString);
|
||||
} else {
|
||||
$this->failures = array();
|
||||
}
|
||||
|
||||
DUP_Util::objectCopy($data, $this, array('archiveHeaderString', 'currentFileHeaderString', 'failuresString'));
|
||||
}
|
||||
|
||||
public function save()
|
||||
{
|
||||
$data = new stdClass();
|
||||
if ($this->currentFileHeader != null) {
|
||||
$data->currentFileHeaderString = SnapJson::jsonEncode($this->currentFileHeader);
|
||||
} else {
|
||||
$data->currentFileHeaderString = null;
|
||||
}
|
||||
|
||||
if ($this->archiveHeader != null) {
|
||||
$data->archiveHeaderString = SnapJson::jsonEncode($this->archiveHeader);
|
||||
} else {
|
||||
$data->archiveHeaderString = null;
|
||||
}
|
||||
|
||||
$data->failuresString = SnapJson::jsonEncode($this->failures);
|
||||
// Object members auto skipped
|
||||
DUP_Util::objectCopy($this, $data);
|
||||
DUP_LOG::traceObject("****SAVING EXPAND STATE****", $this);
|
||||
DUP_LOG::traceObject("****SERIALIZED STATE****", $data);
|
||||
DUP_Settings::Set('duparchive_expand_state', $data);
|
||||
DUP_Settings::Save();
|
||||
}
|
||||
|
||||
private function initMembers()
|
||||
{
|
||||
$this->currentFileHeader = null;
|
||||
$this->archiveOffset = 0;
|
||||
$this->archiveHeader = null;
|
||||
$this->archivePath = null;
|
||||
$this->basePath = null;
|
||||
$this->currentFileOffset = 0;
|
||||
$this->failures = array();
|
||||
$this->isCompressed = false;
|
||||
$this->startTimestamp = time();
|
||||
$this->timeSliceInSecs = -1;
|
||||
$this->working = false;
|
||||
$this->validateOnly = false;
|
||||
$this->directoryModeOverride = -1;
|
||||
$this->fileModeOverride = -1;
|
||||
$this->throttleDelayInUs = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
//silent
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
//silent
|
||||
@@ -0,0 +1,220 @@
|
||||
<?php
|
||||
|
||||
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
||||
/**
|
||||
* Used to generate a thick-box inline dialog such as an alert or confirm pop-up
|
||||
*
|
||||
* Standard: PSR-2
|
||||
*
|
||||
* @link http://www.php-fig.org/psr/psr-2
|
||||
*
|
||||
* @package Duplicator
|
||||
* @subpackage classes/ui
|
||||
* @copyright (c) 2017, Snapcreek LLC
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if (! defined('DUPLICATOR_VERSION')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class DUP_UI_Dialog
|
||||
{
|
||||
/**
|
||||
* The title that shows up in the dialog
|
||||
*/
|
||||
public $title;
|
||||
/**
|
||||
* The message displayed in the body of the dialog
|
||||
*/
|
||||
public $message;
|
||||
/**
|
||||
* The width of the dialog the default is used if not set
|
||||
* Alert = 475px (default) | Confirm = 500px (default)
|
||||
*/
|
||||
public $width;
|
||||
/**
|
||||
* The height of the dialog the default is used if not set
|
||||
* Alert = 125px (default) | Confirm = 150px (default)
|
||||
*/
|
||||
public $height;
|
||||
/**
|
||||
* When the progress meter is running show this text
|
||||
* Available only on confirm dialogs
|
||||
*/
|
||||
public $progressText;
|
||||
/**
|
||||
* When true a progress meter will run until page is reloaded
|
||||
* Available only on confirm dialogs
|
||||
*/
|
||||
public $progressOn = true;
|
||||
/**
|
||||
* The javascript call back method to call when the 'Yes' button is clicked
|
||||
* Available only on confirm dialogs
|
||||
*/
|
||||
public $jscallback;
|
||||
/**
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $okText;
|
||||
/**
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $cancelText;
|
||||
/**
|
||||
* If true close dialog on confirm
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $closeOnConfirm = false;
|
||||
/**
|
||||
* The id given to the full dialog
|
||||
*/
|
||||
private $id;
|
||||
/**
|
||||
* A unique id that is added to all id elements
|
||||
*/
|
||||
private $uniqid;
|
||||
/**
|
||||
* Init this object when created
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
add_thickbox();
|
||||
$this->progressText = __('Processing please wait...', 'duplicator');
|
||||
$this->uniqid = substr(uniqid('', true), 0, 14) . rand();
|
||||
$this->id = 'dup-dlg-' . $this->uniqid;
|
||||
$this->okText = __('OK', 'duplicator');
|
||||
$this->cancelText = __('Cancel', 'duplicator');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the unique id that is assigned to each instance of a dialog
|
||||
*
|
||||
* @return int The unique ID of this dialog
|
||||
*/
|
||||
public function getID()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the unique id that is assigned to each instance of a dialogs message text
|
||||
*
|
||||
* @return int The unique ID of the message
|
||||
*/
|
||||
public function getMessageID()
|
||||
{
|
||||
return "{$this->id}_message";
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the alert base html code used to display when needed
|
||||
*
|
||||
* @return string The html content used for the alert dialog
|
||||
*/
|
||||
public function initAlert()
|
||||
{
|
||||
$onClickClose = '';
|
||||
if (!is_null($this->jscallback)) {
|
||||
$onClickClose .= $this->jscallback . ';';
|
||||
}
|
||||
$onClickClose .= 'tb_remove();';
|
||||
$hideButton = "";
|
||||
if (strlen($this->okText) == 0) {
|
||||
$hideButton = "style='display:none'";
|
||||
}
|
||||
|
||||
$html = '
|
||||
<div id="' . esc_attr($this->id) . '" style="display:none">
|
||||
<div class="dup-dlg-alert-txt">
|
||||
' . $this->message . '
|
||||
<br/><br/>
|
||||
</div>
|
||||
<div class="dup-dlg-alert-btns">
|
||||
<input type="button" class="button button-large" value="' . esc_attr($this->okText) . '" onclick="' . $onClickClose . '" ' . $hideButton . '/>
|
||||
</div>
|
||||
</div>';
|
||||
echo $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the alert base JS code used to display when needed
|
||||
*
|
||||
* @return string The javascript content used for the alert dialog
|
||||
*/
|
||||
public function showAlert()
|
||||
{
|
||||
$this->width = is_numeric($this->width) ? $this->width : 500;
|
||||
$this->height = is_numeric($this->height) ? $this->height : 175;
|
||||
$html = "tb_show('" . esc_js($this->title) . "', '#TB_inline?width=" . esc_js($this->width) . "&height=" . esc_js($this->height) . "&inlineId=" . esc_js($this->id) . "');\n" .
|
||||
"var styleData = jQuery('#TB_window').attr('style') + 'height: " . esc_js($this->height) . "px !important';\n" .
|
||||
"jQuery('#TB_window').attr('style', styleData);";
|
||||
echo $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the confirm base JS code used to display when needed
|
||||
*
|
||||
* @return string The javascript content used for the confirm dialog
|
||||
*/
|
||||
public function initConfirm()
|
||||
{
|
||||
$progress_data = '';
|
||||
$progress_func2 = '';
|
||||
$onClickConfirm = '';
|
||||
if (!is_null($this->jscallback)) {
|
||||
$onClickConfirm .= $this->jscallback . ';';
|
||||
}
|
||||
|
||||
//Enable the progress spinner
|
||||
if ($this->progressOn) {
|
||||
$progress_func1 = "__DUP_UI_Dialog_" . $this->uniqid;
|
||||
$progress_func2 = ";{$progress_func1}(this)";
|
||||
$progress_data = "<div class='dup-dlg-confirm-progress'><i class='fas fa-circle-notch fa-spin fa-lg fa-fw'></i> " . esc_js($this->progressText) . "</div>
|
||||
<script>
|
||||
function {$progress_func1}(obj)
|
||||
{
|
||||
jQuery(obj).parent().parent().find('.dup-dlg-confirm-progress').show();
|
||||
jQuery(obj).closest('.dup-dlg-confirm-btns').find('input').attr('disabled', 'true');
|
||||
}
|
||||
</script>";
|
||||
$onClickConfirm .= $progress_func2 . ';';
|
||||
}
|
||||
|
||||
if ($this->closeOnConfirm) {
|
||||
$onClickConfirm .= 'tb_remove();';
|
||||
}
|
||||
|
||||
$html =
|
||||
'<div id="' . esc_attr($this->id) . '" style="display:none">
|
||||
<div class="dup-dlg-confirm-txt">
|
||||
<span id="' . esc_attr($this->id) . '_message">' . esc_html($this->message) . '</span>
|
||||
<br/><br/>
|
||||
' . $progress_data . '
|
||||
</div>
|
||||
<div class="dup-dlg-confirm-btns">
|
||||
<input type="button" class="button button-large" value="' . esc_attr($this->okText) . '" onclick="' . $onClickConfirm . '" />
|
||||
<input type="button" class="button button-large" value="' . esc_attr($this->cancelText) . '" onclick="tb_remove()" />
|
||||
</div>
|
||||
</div>';
|
||||
echo $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the confirm base JS code used to display when needed
|
||||
*
|
||||
* @return string The javascript content used for the confirm dialog
|
||||
*/
|
||||
public function showConfirm()
|
||||
{
|
||||
$this->width = is_numeric($this->width) ? $this->width : 500;
|
||||
$this->height = is_numeric($this->height) ? $this->height : 225;
|
||||
$html = "tb_show('" . esc_js($this->title) . "', '#TB_inline?width=" . esc_js($this->width) . "&height=" . esc_js($this->height) . "&inlineId=" . esc_js($this->id) . "');\n" .
|
||||
"var styleData = jQuery('#TB_window').attr('style') + 'height: " . esc_js($this->height) . "px !important';\n" .
|
||||
"jQuery('#TB_window').attr('style', styleData);";
|
||||
echo $html;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
||||
/**
|
||||
* Used to generate a thick box inline dialog such as an alert or confirm pop-up
|
||||
*
|
||||
* Standard: PSR-2
|
||||
*
|
||||
* @link http://www.php-fig.org/psr/psr-2
|
||||
*
|
||||
* @package Duplicator
|
||||
* @subpackage classes/ui
|
||||
* @copyright (c) 2017, Snapcreek LLC
|
||||
*/
|
||||
|
||||
class DUP_UI_Messages
|
||||
{
|
||||
const UNIQUE_ID_PREFIX = 'dup_ui_msg_';
|
||||
const NOTICE = 'updated';
|
||||
const WARNING = 'update-nag';
|
||||
const ERROR = 'error';
|
||||
private static $unique_id = 0;
|
||||
private $id;
|
||||
public $type = self::NOTICE;
|
||||
public $content = '';
|
||||
public $wrap_cont_tag = 'p';
|
||||
public $hide_on_init = true;
|
||||
public $is_dismissible = false;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var int delay in milliseconds
|
||||
*/
|
||||
public $auto_hide_delay = 0;
|
||||
public $callback_on_show = null;
|
||||
public $callback_on_hide = null;
|
||||
public function __construct($content = '', $type = self::NOTICE)
|
||||
{
|
||||
self::$unique_id++;
|
||||
$this->id = self::UNIQUE_ID_PREFIX . self::$unique_id;
|
||||
$this->content = (string) $content;
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
protected function get_notice_classes($classes = array())
|
||||
{
|
||||
if (is_string($classes)) {
|
||||
$classes = explode(' ', $classes);
|
||||
} elseif (is_array($classes)) {
|
||||
} else {
|
||||
$classes = array();
|
||||
}
|
||||
|
||||
if ($this->is_dismissible) {
|
||||
$classes[] = 'is-dismissible';
|
||||
}
|
||||
|
||||
$result = array_merge(array('notice', $this->type), $classes);
|
||||
return trim(implode(' ', $result));
|
||||
}
|
||||
|
||||
public function initMessage()
|
||||
{
|
||||
$classes = array();
|
||||
if ($this->hide_on_init) {
|
||||
$classes[] = 'no_display';
|
||||
}
|
||||
|
||||
$this->wrap_cont_tag = empty($this->wrap_cont_tag) ? 'p' : $this->wrap_cont_tag;
|
||||
echo '<div id="' . $this->id . '" class="' . $this->get_notice_classes($classes) . '">' .
|
||||
'<' . $this->wrap_cont_tag . ' class="msg-content">' .
|
||||
$this->content .
|
||||
'</' . $this->wrap_cont_tag . '>' .
|
||||
'</div>';
|
||||
}
|
||||
|
||||
public function updateMessage($jsVarName, $echo = true)
|
||||
{
|
||||
$result = 'jQuery("#' . $this->id . ' > .msg-content").html(' . $jsVarName . ');';
|
||||
if ($echo) {
|
||||
echo $result;
|
||||
} else {
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
public function showMessage($echo = true)
|
||||
{
|
||||
$callStr = !empty($this->callback_on_show) ? $this->callback_on_show . ';' : '';
|
||||
$result = 'jQuery("#' . $this->id . '").fadeIn( "slow", function() { $(this).removeClass("no_display");' . $callStr . ' });';
|
||||
if ($this->auto_hide_delay > 0) {
|
||||
$result .= 'setTimeout(function () { ' . $this->hideMessage(false) . ' }, ' . $this->auto_hide_delay . ');';
|
||||
}
|
||||
|
||||
if ($echo) {
|
||||
echo $result;
|
||||
} else {
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
public function hideMessage($echo = true)
|
||||
{
|
||||
$callStr = !empty($this->callback_on_hide) ? $this->callback_on_hide . ';' : '';
|
||||
$result = 'jQuery("#' . $this->id . '").fadeOut( "slow", function() { $(this).addClass("no_display");' . $callStr . ' });';
|
||||
if ($echo) {
|
||||
echo $result;
|
||||
} else {
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
|
||||
use Duplicator\Libs\Snap\SnapUtil;
|
||||
|
||||
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
||||
/**
|
||||
* The base class for all screen.php files. This class is used to control items that are common
|
||||
* among all screens, namely the Help tab and Screen Options drop down items. When creating a
|
||||
* screen object please extent this class.
|
||||
*
|
||||
* Standard: PSR-2
|
||||
*
|
||||
* @link http://www.php-fig.org/psr/psr-2
|
||||
*
|
||||
* @package Duplicator
|
||||
* @subpackage classes/ui
|
||||
* @copyright (c) 2017, Snapcreek LLC
|
||||
*/
|
||||
// Exit if accessed directly
|
||||
if (!defined('DUPLICATOR_VERSION')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class DUP_UI_Screen
|
||||
{
|
||||
/**
|
||||
* Used as a placeholder for the current screen object
|
||||
*/
|
||||
public $screen;
|
||||
|
||||
/**
|
||||
* Init this object when created
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public static function getCustomCss()
|
||||
{
|
||||
$screen = get_current_screen();
|
||||
if (
|
||||
!in_array($screen->id, array(
|
||||
'toplevel_page_duplicator',
|
||||
'duplicator_page_duplicator-tools',
|
||||
'duplicator_page_duplicator-settings',
|
||||
'duplicator_page_duplicator-gopro'))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$colorScheme = self::getCurrentColorScheme();
|
||||
$primaryButtonColor = self::getPrimaryButtonColorByScheme();
|
||||
if ($colorScheme !== false) { ?>
|
||||
<style>
|
||||
.link-style {
|
||||
color: <?php echo $colorScheme->colors[2]; ?>;
|
||||
}
|
||||
|
||||
.link-style:hover {
|
||||
color: <?php echo $colorScheme->colors[3]; ?>;
|
||||
}
|
||||
|
||||
|
||||
.dup-radio-button-group-wrapper input[type="radio"] + label {
|
||||
color: <?php echo $primaryButtonColor; ?>;
|
||||
}
|
||||
|
||||
.dup-radio-button-group-wrapper input[type="radio"] + label:hover,
|
||||
.dup-radio-button-group-wrapper input[type="radio"]:focus + label,
|
||||
.dup-radio-button-group-wrapper input[type="radio"]:checked + label {
|
||||
background: <?php echo $primaryButtonColor; ?>;
|
||||
border-color: <?php echo $primaryButtonColor; ?>;
|
||||
}
|
||||
</style>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unfortunately not all color schemes take the same color as the buttons so you need to make a custom switch/
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getPrimaryButtonColorByScheme()
|
||||
{
|
||||
$colorScheme = self::getCurrentColorScheme();
|
||||
$name = strtolower($colorScheme->name);
|
||||
switch ($name) {
|
||||
case 'blue':
|
||||
return '#e3af55';
|
||||
case 'light':
|
||||
case 'midnight':
|
||||
return $colorScheme->colors[3];
|
||||
case 'ocean':
|
||||
case 'ectoplasm':
|
||||
case 'coffee':
|
||||
case 'sunrise':
|
||||
case 'default':
|
||||
default:
|
||||
return $colorScheme->colors[2];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function getCurrentColorScheme()
|
||||
{
|
||||
global $_wp_admin_css_colors;
|
||||
if (!isset($_wp_admin_css_colors) || !is_array($_wp_admin_css_colors)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$colorScheme = get_user_option('admin_color');
|
||||
|
||||
if (isset($_wp_admin_css_colors[$colorScheme])) {
|
||||
return $_wp_admin_css_colors[$colorScheme];
|
||||
} else {
|
||||
return $_wp_admin_css_colors[SnapUtil::arrayKeyFirst($_wp_admin_css_colors)];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
||||
/**
|
||||
* Gets the view state of UI elements to remember its viewable state
|
||||
*
|
||||
* Standard: PSR-2
|
||||
*
|
||||
* @link http://www.php-fig.org/psr/psr-2
|
||||
*
|
||||
* @package Duplicator
|
||||
* @subpackage classes/ui
|
||||
* @copyright (c) 2017, Snapcreek LLC
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if (! defined('DUPLICATOR_VERSION')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class DUP_UI_ViewState
|
||||
{
|
||||
/**
|
||||
* The key used in the wp_options table
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private static $optionsViewStateKey = 'duplicator_ui_view_state';
|
||||
/**
|
||||
* Save the view state of UI elements
|
||||
*
|
||||
* @param string $key A unique key to define the UI element
|
||||
* @param string $value A generic value to use for the view state
|
||||
*
|
||||
* @return bool Returns true if the value was successfully saved
|
||||
*/
|
||||
public static function save($key, $value)
|
||||
{
|
||||
$view_state = array();
|
||||
$view_state = get_option(self::$optionsViewStateKey);
|
||||
$view_state[$key] = $value;
|
||||
$success = update_option(self::$optionsViewStateKey, $view_state);
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the values from the settings array
|
||||
*
|
||||
* @return array Returns and array of all the values stored in the settings array
|
||||
*/
|
||||
public static function getArray()
|
||||
{
|
||||
return get_option(self::$optionsViewStateKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all the values from the settings array
|
||||
*
|
||||
* @param array $view_state states
|
||||
*
|
||||
* @return boolean Returns whether updated or not
|
||||
*/
|
||||
public static function setArray($view_state)
|
||||
{
|
||||
return update_option(self::$optionsViewStateKey, $view_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value of the of view state item
|
||||
*
|
||||
* @param type $searchKey The key to search on
|
||||
*
|
||||
* @return string Returns the value of the key searched or null if key is not found
|
||||
*/
|
||||
public static function getValue($searchKey)
|
||||
{
|
||||
$view_state = get_option(self::$optionsViewStateKey);
|
||||
if (is_array($view_state)) {
|
||||
foreach ($view_state as $key => $value) {
|
||||
if ($key == $searchKey) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
3
html/wp-content/plugins/duplicator/classes/ui/index.php
Normal file
3
html/wp-content/plugins/duplicator/classes/ui/index.php
Normal file
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
//silent
|
||||
@@ -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