Changed source root directory

This commit is contained in:
2026-03-05 16:30:11 +01:00
parent dc85447ee1
commit 538f85d7a2
5868 changed files with 749734 additions and 99 deletions

View File

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

View 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) . '"';
}
}

View 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!! */
}

View 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']);
}
}
}

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

View File

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

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

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

View File

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

View File

@@ -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()
{
}
}

View File

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

View File

@@ -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()
{
}
}

View File

@@ -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()
{
}
}

View File

@@ -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()
{
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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());
}
}

View File

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

View File

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

View File

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

View File

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

View File

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