first commit
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Ajax;
|
||||
|
||||
abstract class AbstractAjaxService
|
||||
{
|
||||
/**
|
||||
* Init ajax calls
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
abstract public function init();
|
||||
|
||||
/**
|
||||
* Add ajax action
|
||||
*
|
||||
* @param string $tag ajax tag name
|
||||
* @param string $methodName method name
|
||||
*
|
||||
* @return bool Always returns true
|
||||
*/
|
||||
protected function addAjaxCall($tag, $methodName)
|
||||
{
|
||||
return add_action($tag, array($this, $methodName));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Ajax;
|
||||
|
||||
use Duplicator\Ajax\ServicesBrand;
|
||||
use Duplicator\Ajax\ServicesDashboard;
|
||||
use Duplicator\Ajax\ServicesImport;
|
||||
use Duplicator\Ajax\ServicesNotifications;
|
||||
use Duplicator\Ajax\ServicesPackage;
|
||||
use Duplicator\Ajax\ServicesRecovery;
|
||||
use Duplicator\Ajax\ServicesSchedule;
|
||||
use Duplicator\Ajax\ServicesSettings;
|
||||
use Duplicator\Ajax\ServicesStorage;
|
||||
use Duplicator\Ajax\ServicesTools;
|
||||
|
||||
class AjaxServicesUtils
|
||||
{
|
||||
/**
|
||||
* Init ajax hooks
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function loadServices()
|
||||
{
|
||||
(new ServicesImport())->init();
|
||||
(new ServicesRecovery())->init();
|
||||
(new ServicesSchedule())->init();
|
||||
(new ServicesStorage())->init();
|
||||
(new ServicesDashboard())->init();
|
||||
(new ServicesSettings())->init();
|
||||
(new ServicesNotifications())->init();
|
||||
(new ServicesPackage())->init();
|
||||
(new ServicesTools())->init();
|
||||
(new ServicesBrand())->init();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Ajax;
|
||||
|
||||
use DUP_PRO_Handler;
|
||||
use DUP_PRO_Log;
|
||||
use Duplicator\Core\CapMng;
|
||||
use Duplicator\Libs\Snap\SnapUtil;
|
||||
use Exception;
|
||||
|
||||
class AjaxWrapper
|
||||
{
|
||||
/**
|
||||
* This function wrap a callback and return always a json well formatted output.
|
||||
*
|
||||
* check nonce and capability if passed and return a json with this format
|
||||
* [
|
||||
* success : bool
|
||||
* data : [
|
||||
* funcData : mixed // callback return data
|
||||
* message : string // a message for jvascript func (for example an exception message)
|
||||
* output : string // all normal output wrapped between ob_start and ob_get_clean
|
||||
* // if $errorUnespectedOutput is true and output isn't empty the json return an error
|
||||
* ]
|
||||
* ]
|
||||
*
|
||||
* @param callable $callback callback function
|
||||
* @param string $nonceaction if action is null don't verify nonce
|
||||
* @param string $nonce nonce string
|
||||
* @param string $capability if capability is null don't verify capability
|
||||
* @param bool $errorUnespectedOutput if true thorw exception with unespected optput
|
||||
*
|
||||
* @return never
|
||||
*/
|
||||
public static function json(
|
||||
$callback,
|
||||
$nonceaction = null,
|
||||
$nonce = null,
|
||||
$capability = null,
|
||||
$errorUnespectedOutput = true
|
||||
) {
|
||||
$error = false;
|
||||
|
||||
$result = array(
|
||||
'funcData' => null,
|
||||
'output' => '',
|
||||
'message' => '',
|
||||
);
|
||||
|
||||
ob_start();
|
||||
try {
|
||||
DUP_PRO_Handler::init_error_handler();
|
||||
$nonce = SnapUtil::sanitizeNSCharsNewline($nonce);
|
||||
if (!is_null($nonceaction) && !wp_verify_nonce($nonce, $nonceaction)) {
|
||||
DUP_PRO_Log::trace('Security issue');
|
||||
throw new Exception('Security issue');
|
||||
}
|
||||
if (!is_null($capability)) {
|
||||
CapMng::can($capability);
|
||||
}
|
||||
|
||||
// execute ajax function
|
||||
$result['funcData'] = call_user_func($callback);
|
||||
} catch (Exception $e) {
|
||||
$error = true;
|
||||
$result['message'] = $e->getMessage();
|
||||
}
|
||||
|
||||
$result['output'] = ob_get_clean();
|
||||
if ($errorUnespectedOutput && !empty($result['output'])) {
|
||||
$error = true;
|
||||
}
|
||||
|
||||
if ($error) {
|
||||
wp_send_json_error($result);
|
||||
} else {
|
||||
wp_send_json_success($result);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,338 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Ajax\FileTransfer;
|
||||
|
||||
use DUP_PRO_Log;
|
||||
use VendorDuplicator\WpOrg\Requests\Requests;
|
||||
use VendorDuplicator\Amk\JsonSerialize\JsonSerialize;
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
use Duplicator\Libs\Snap\SnapLog;
|
||||
use Duplicator\Libs\Snap\SnapURL;
|
||||
use Duplicator\Libs\Snap\SnapUtil;
|
||||
use Duplicator\Package\Import\PackageImporter;
|
||||
use Duplicator\Utils\HTTP\DynamicChunkRequests;
|
||||
use Exception;
|
||||
|
||||
class ImportUpload
|
||||
{
|
||||
const P2P_TIMEOUT = 15; // seconds, can be a float number
|
||||
|
||||
const MODE_UPLOAD_LOCAL = 'upload'; // Upload archive from local PC
|
||||
const MODE_DOWNLOAD_REMOTE = 'remote'; // Download archive from remote URL
|
||||
const MODE_UPLOADED = 'uploaded'; // Archive is already uploaded
|
||||
|
||||
const STATUS_CHUNKING = 'chunking';
|
||||
const STATUS_COMPLETE = 'complete';
|
||||
|
||||
const INIT_REMOTE_URL_DATA_RETRIALS = 2;
|
||||
|
||||
/** @var string */
|
||||
protected $mode = '';
|
||||
/** @var string*/
|
||||
protected $status = self::STATUS_CHUNKING;
|
||||
/** @var bool */
|
||||
protected $isImportable = false;
|
||||
/** @var string */
|
||||
protected $archivePath = '';
|
||||
/** @var string */
|
||||
protected $installerPageLink = '';
|
||||
/** @var string */
|
||||
protected $htmlDetails = '';
|
||||
/** @var string */
|
||||
protected $created = '';
|
||||
/** @var string */
|
||||
protected $invalidMessage = '';
|
||||
/** @var int */
|
||||
protected $archiveSize = -1;
|
||||
/** @var false|DynamicChunkRequests */
|
||||
protected $remoteChunk = false;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param string $mode upload mode
|
||||
* @param string $archivePath archive path, use in mode uploaded
|
||||
*/
|
||||
public function __construct($mode, $archivePath = '')
|
||||
{
|
||||
switch ($mode) {
|
||||
case self::MODE_UPLOAD_LOCAL:
|
||||
case self::MODE_DOWNLOAD_REMOTE:
|
||||
break;
|
||||
case self::MODE_UPLOADED:
|
||||
if (strlen($archivePath) == 0 || !is_file($archivePath)) {
|
||||
throw new Exception('Invalid archive');
|
||||
}
|
||||
$this->archivePath = $archivePath;
|
||||
break;
|
||||
default:
|
||||
throw new Exception('Invalid transfer mode');
|
||||
}
|
||||
$this->mode = $mode;
|
||||
|
||||
add_filter('duplicator_pro_remote_download_data', array(RemoteDownloadCustom::class, 'dropboxRemoteUrlFilter'));
|
||||
add_filter('duplicator_pro_remote_download_data', array(RemoteDownloadCustom::class, 'gDriveRemoteUrlFilter'));
|
||||
add_filter('duplicator_pro_remote_download_data', array(RemoteDownloadCustom::class, 'oneDriveRemoteUrlFilter'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Exec upload and return result
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function exec()
|
||||
{
|
||||
if (!file_exists(DUPLICATOR_PRO_PATH_IMPORTS)) {
|
||||
SnapIO::mkdir(DUPLICATOR_PRO_PATH_IMPORTS, 0755, true);
|
||||
SnapIO::createSilenceIndex(DUPLICATOR_PRO_PATH_IMPORTS);
|
||||
}
|
||||
|
||||
switch ($this->mode) {
|
||||
case self::MODE_UPLOAD_LOCAL:
|
||||
$this->uploadLocal();
|
||||
break;
|
||||
case self::MODE_DOWNLOAD_REMOTE:
|
||||
$this->remoteDownload();
|
||||
break;
|
||||
case self::MODE_UPLOADED:
|
||||
$this->setCompleteData();
|
||||
break;
|
||||
}
|
||||
|
||||
return JsonSerialize::serializeToData($this, JsonSerialize::JSON_SKIP_CLASS_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload in local mode
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function uploadLocal()
|
||||
{
|
||||
$archiveName = isset($_FILES["file"]["name"]) ? SnapUtil::sanitizeNSCharsNewlineTabs($_FILES["file"]["name"]) : null;
|
||||
if (!preg_match('/^.*\.(zip|daf)$/', $archiveName)) {
|
||||
throw new Exception(__("Invalid archive file name. Please use the valid archive file!", 'duplicator-pro'));
|
||||
}
|
||||
$archiveNameTemp = isset($_FILES["file"]["tmp_name"]) ? SnapUtil::sanitizeNSCharsNewlineTabs($_FILES["file"]["tmp_name"]) : null;
|
||||
|
||||
$currentChunk = filter_input(INPUT_POST, 'chunk', FILTER_VALIDATE_INT, array('options' => array('default' => false)));
|
||||
$numChunks = filter_input(INPUT_POST, 'chunks', FILTER_VALIDATE_INT, array('options' => array('default' => false)));
|
||||
|
||||
$this->archivePath = DUPLICATOR_PRO_PATH_IMPORTS . '/' . $archiveName;
|
||||
|
||||
if ($numChunks !== false) {
|
||||
//CHUNK MODE
|
||||
$archivePart = $this->getArchivePart();
|
||||
|
||||
// Clean last upload part leaved as it is (The situation in which user navigate to another url while uploading archive file path)
|
||||
if ($currentChunk === 0 && file_exists($archivePart)) {
|
||||
@unlink($archivePart);
|
||||
}
|
||||
|
||||
SnapIO::appendFileToFile($archiveNameTemp, $archivePart);
|
||||
|
||||
if ($currentChunk == ($numChunks - 1)) {
|
||||
if (SnapIO::rename($archivePart, $this->archivePath, true) === false) {
|
||||
throw new Exception('Can\'t rename file part to file');
|
||||
}
|
||||
$this->setCompleteData();
|
||||
} else {
|
||||
$this->status = self::STATUS_CHUNKING;
|
||||
}
|
||||
} else {
|
||||
// DIRECT MODE
|
||||
if (move_uploaded_file($archiveNameTemp, $this->archivePath) === false) {
|
||||
throw new Exception(esc_html__('Can\'t rename file part to file', 'duplicator-pro'));
|
||||
}
|
||||
$this->setCompleteData();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Download archive from remote URL
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function remoteDownload()
|
||||
{
|
||||
$startingRemoteURL = filter_input(INPUT_POST, 'url', FILTER_VALIDATE_URL, array('options' => array('default' => false)));
|
||||
if ($startingRemoteURL == false) {
|
||||
throw new Exception('Remote URL must be a valid URL');
|
||||
}
|
||||
|
||||
$this->remoteChunk = self::getRestoreChunkDownload();
|
||||
if (!($this->remoteChunk instanceof DynamicChunkRequests)) {
|
||||
$this->remoteChunk = new DynamicChunkRequests();
|
||||
$this->remoteChunk->setExtraData('retrials', 0);
|
||||
$this->remoteChunk->setExtraData('maxRetrials', 0); // Default value 0, but particular storage can change it
|
||||
$this->remoteChunk->setExtraData('startedDownload', false);
|
||||
}
|
||||
|
||||
if (!$this->remoteChunk->getExtraData('startedDownload')) {
|
||||
$downloadData = array();
|
||||
try {
|
||||
$downloadData = apply_filters('duplicator_pro_remote_download_data', [
|
||||
'url' => $startingRemoteURL,
|
||||
'archiveName' => basename(SnapURL::parseUrl($startingRemoteURL, PHP_URL_PATH)),
|
||||
'chunkTime' => DynamicChunkRequests::DEFAULT_CHUNK_TIME,
|
||||
'maxRetrials' => 0,
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
$retrials = $this->remoteChunk->getExtraData('retrials');
|
||||
if ($retrials <= self::INIT_REMOTE_URL_DATA_RETRIALS) {
|
||||
$this->remoteChunk->setExtraData('retrials', $retrials + 1);
|
||||
return;
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
|
||||
if (!preg_match('/^.*\.(zip|daf)$/', $downloadData['archiveName'])) {
|
||||
throw new Exception(__("Invalid archive file name. Please use the valid archive file!", 'duplicator-pro'));
|
||||
}
|
||||
|
||||
$this->remoteChunk->setDownloadUrl($downloadData['url']);
|
||||
$this->remoteChunk->setChunkTime($downloadData['chunkTime']);
|
||||
$this->remoteChunk->setExtraData('archiveName', $downloadData['archiveName']);
|
||||
$this->remoteChunk->setExtraData('startingRemoteURL', $startingRemoteURL);
|
||||
$this->remoteChunk->setExtraData('retrials', 0); // Reset retrials
|
||||
$this->remoteChunk->setExtraData('maxRetrials', $downloadData['maxRetrials']);
|
||||
$this->remoteChunk->setExtraData('startedDownload', true);
|
||||
|
||||
$this->archivePath = DUPLICATOR_PRO_PATH_IMPORTS . '/' . $downloadData['archiveName'];
|
||||
$archivePart = $this->getArchivePart();
|
||||
if (file_exists($archivePart)) {
|
||||
unlink($archivePart);
|
||||
}
|
||||
} else {
|
||||
$this->archivePath = DUPLICATOR_PRO_PATH_IMPORTS . '/' . $this->remoteChunk->getExtraData('archiveName');
|
||||
$archivePart = $this->getArchivePart();
|
||||
|
||||
if (!file_exists($archivePart)) {
|
||||
throw new Exception('Can\t resume the download, archive part file don\'t exists');
|
||||
}
|
||||
|
||||
if ($this->remoteChunk->getExtraData('startingRemoteURL') !== $startingRemoteURL) {
|
||||
throw new Exception('Input params not valid');
|
||||
}
|
||||
}
|
||||
|
||||
$startTime = microtime(true);
|
||||
do {
|
||||
$tmpFile = tempnam(DUPLICATOR_PRO_PATH_IMPORTS, 'tmp_p2p_part_');
|
||||
|
||||
$options = array();
|
||||
if (session_id() == "") {
|
||||
session_start();
|
||||
}
|
||||
if (isset($_SESSION["duplicator_pro_import_from_link_cookies"])) {
|
||||
$options['cookies'] = $_SESSION["duplicator_pro_import_from_link_cookies"];
|
||||
}
|
||||
$options['filename'] = $tmpFile;
|
||||
$options['verify'] = false;
|
||||
$options['verifyname'] = false;
|
||||
|
||||
$response = $this->remoteChunk->request(
|
||||
array(),
|
||||
array(),
|
||||
Requests::GET,
|
||||
$options,
|
||||
false
|
||||
);
|
||||
|
||||
if ($response->success == false) {
|
||||
$retrials = $this->remoteChunk->getExtraData('retrials');
|
||||
$maxRetrials = $this->remoteChunk->getExtraData('maxRetrials');
|
||||
|
||||
if ($retrials <= $maxRetrials) {
|
||||
$this->remoteChunk->setExtraData('retrials', $retrials + 1);
|
||||
break;
|
||||
} else {
|
||||
throw new Exception("Remote URL request on " . $this->remoteChunk->getExtraData('startingRemoteURL') . " failed");
|
||||
}
|
||||
}
|
||||
|
||||
SnapIO::appendFileToFile($tmpFile, $archivePart);
|
||||
// Preserve cookies for use in the next request
|
||||
$_SESSION["duplicator_pro_import_from_link_cookies"] = property_exists($response, 'cookies') ? $response->cookies : null;
|
||||
$this->remoteChunk->setExtraData('retrials', 0); // Reset retrials
|
||||
$deltaTime = microtime(true) - $startTime;
|
||||
} while (!$this->remoteChunk->isComplete() && $deltaTime < self::P2P_TIMEOUT);
|
||||
|
||||
if ($this->remoteChunk->isComplete()) {
|
||||
if (SnapIO::rename($archivePart, $this->archivePath, true) === false) {
|
||||
throw new Exception('Can\'t rename file part to file');
|
||||
}
|
||||
$this->setCompleteData();
|
||||
} else {
|
||||
$this->status = self::STATUS_CHUNKING;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return restore chunk download object
|
||||
*
|
||||
* @return false|DynamicChunkRequests false if restore isn't set
|
||||
*/
|
||||
protected static function getRestoreChunkDownload()
|
||||
{
|
||||
try {
|
||||
$restoreDownload = (isset($_POST['restoreDownload']) ? SnapUtil::sanitizeNSCharsNewline($_POST['restoreDownload']) : '');
|
||||
$result = false;
|
||||
if (strlen($restoreDownload) === 0) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$restoreDownload = stripslashes($restoreDownload);
|
||||
$result = JsonSerialize::unserializeToObj($restoreDownload, DynamicChunkRequests::class);
|
||||
} catch (Exception $e) {
|
||||
$result = false;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get archvie part full path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getArchivePart()
|
||||
{
|
||||
return $this->archivePath . '.part';
|
||||
}
|
||||
|
||||
/**
|
||||
* Set completa package upload data
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setCompleteData()
|
||||
{
|
||||
$this->status = self::STATUS_COMPLETE;
|
||||
$this->remoteChunk = false;
|
||||
|
||||
try {
|
||||
$importObj = new PackageImporter($this->archivePath);
|
||||
$importObj->cleanFolder();
|
||||
|
||||
$this->isImportable = $importObj->isImportable();
|
||||
$this->installerPageLink = $importObj->getInstallerPageLink();
|
||||
$this->htmlDetails = $importObj->getHtmlDetails(false);
|
||||
$this->created = $importObj->getCreated();
|
||||
if (($this->archiveSize = filesize($this->archivePath)) === false) {
|
||||
$this->archiveSize = -1;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->isImportable = false;
|
||||
$this->installerPageLink = '';
|
||||
$this->htmlDetails = sprintf(esc_html__('Problem on import, message: %s', 'duplicator-pro'), $e->getMessage());
|
||||
$this->created = '';
|
||||
$this->invalidMessage = $e->getMessage();
|
||||
DUP_PRO_Log::trace("Set complete data rerror\n" . SnapLog::getTextException($e));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Ajax\FileTransfer;
|
||||
|
||||
use DUP_PRO_Log;
|
||||
use VendorDuplicator\WpOrg\Requests\Requests;
|
||||
use Duplicator\Libs\Snap\SnapURL;
|
||||
use Exception;
|
||||
|
||||
class RemoteDownloadCustom
|
||||
{
|
||||
/**
|
||||
* Apply Dropbox remote download data filter
|
||||
*
|
||||
* @param array{url: string, archiveName: string, chunkTime: float, maxRetrials: int} $downloadData download data
|
||||
*
|
||||
* @return array{url: string, archiveName: string, chunkTime: float, maxRetrials: int}
|
||||
*/
|
||||
public static function dropboxRemoteUrlFilter($downloadData)
|
||||
{
|
||||
$parseUrl = SnapURL::parseUrl($downloadData['url']);
|
||||
if (SnapURL::wwwRemove($parseUrl['host']) === 'dropbox.com') {
|
||||
$downloadData['maxRetrials'] = 0;
|
||||
parse_str($parseUrl['query'], $queryVals);
|
||||
if (isset($queryVals['dl'])) {
|
||||
$queryVals['dl'] = 1;
|
||||
$parseUrl['query'] = http_build_query($queryVals);
|
||||
$realURL = SnapURL::buildUrl($parseUrl);
|
||||
DUP_PRO_Log::trace("Real Dropbox URL: $realURL");
|
||||
$downloadData['url'] = $realURL;
|
||||
}
|
||||
$downloadData['chunkTime'] = 20;
|
||||
}
|
||||
return $downloadData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply Google drive remote download data filter
|
||||
*
|
||||
* @param array{url: string, archiveName: string, chunkTime: float, maxRetrials: int} $downloadData download data
|
||||
*
|
||||
* @return array{url: string, archiveName: string, chunkTime: float, maxRetrials: int}
|
||||
*/
|
||||
public static function gDriveRemoteUrlFilter($downloadData)
|
||||
{
|
||||
$url = $downloadData['url'];
|
||||
$parseUrl = SnapURL::parseUrl($url);
|
||||
if (SnapURL::wwwRemove($parseUrl['host']) === 'drive.google.com') {
|
||||
$downloadData['maxRetrials'] = 0;
|
||||
// $url example: https://drive.google.com/file/d/10BQxD48Qf2eq8vg5uIiRxSd_ho8DczKW/view?usp=sharing
|
||||
// Take id from $url, then use it to form this link:
|
||||
// https://drive.google.com/uc?id=10BQxD48Qf2eq8vg5uIiRxSd_ho8DczKW&export=download&confirm=t
|
||||
$revUrl = strrev($url);
|
||||
$pattern = "/\\/(.+?)\\//"; // Take text between last 2 signs '/'
|
||||
$result = preg_match($pattern, $revUrl, $matches);
|
||||
if (!$result) {
|
||||
throw new Exception("Could not get id from the GDrive URL.");
|
||||
}
|
||||
$id = strrev($matches[1]);
|
||||
$indirectURL = "https://drive.google.com/uc?id=" . $id . "&export=download&confirm=t";
|
||||
|
||||
// Get cookies from view file page
|
||||
$response = Requests::get(
|
||||
$url,
|
||||
array(),
|
||||
array(
|
||||
'timeout' => 60,
|
||||
'follow_redirects' => false,
|
||||
)
|
||||
);
|
||||
|
||||
if ($response->success == false) {
|
||||
throw new Exception("Could not get real download url from the GDrive website");
|
||||
}
|
||||
|
||||
$cookies = property_exists($response, 'cookies') ? $response->cookies : null;
|
||||
DUP_PRO_Log::trace("Real GDrive URL: $indirectURL");
|
||||
$downloadData['url'] = $indirectURL;
|
||||
|
||||
// Extracting archive name from response headers of $indirectURL
|
||||
$response = Requests::get(
|
||||
$indirectURL,
|
||||
array('Range' => "bytes=0-0"),
|
||||
array(
|
||||
'timeout' => 60,
|
||||
'cookies' => $cookies,
|
||||
)
|
||||
);
|
||||
|
||||
if (
|
||||
$response->success == false ||
|
||||
!isset($response->headers["content-disposition"]) ||
|
||||
strlen($response->headers["content-disposition"]) == 0
|
||||
) {
|
||||
throw new Exception("Could not get archive name for GDrive url: $url, unexpected headers");
|
||||
}
|
||||
|
||||
$pattern = "/filename=\"(.+)\"/msU";
|
||||
$result = preg_match($pattern, $response->headers["content-disposition"], $matches);
|
||||
if (!$result) {
|
||||
throw new Exception("Could not get archive name for GDrive url: $url, no filename in headers");
|
||||
}
|
||||
$archiveName = $matches[1];
|
||||
DUP_PRO_Log::trace("Archive name on GDrive: $archiveName");
|
||||
$downloadData['archiveName'] = $archiveName;
|
||||
$downloadData['chunkTime'] = 10;
|
||||
|
||||
// Preserve cookies for later use in future chunk requests
|
||||
if (session_id() == "") {
|
||||
session_start();
|
||||
}
|
||||
$_SESSION["duplicator_pro_import_from_link_cookies"] = property_exists($response, 'cookies') ? $response->cookies : null;
|
||||
}
|
||||
|
||||
return $downloadData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply OneDrive remote download data filter
|
||||
*
|
||||
* @param array{url: string, archiveName: string, chunkTime: float, maxRetrials: int} $downloadData download data
|
||||
*
|
||||
* @return array{url: string, archiveName: string, chunkTime: float, maxRetrials: int}
|
||||
*/
|
||||
public static function oneDriveRemoteUrlFilter($downloadData)
|
||||
{
|
||||
$url = $downloadData['url'];
|
||||
$parseUrl = SnapURL::parseUrl($url);
|
||||
if (SnapURL::wwwRemove($parseUrl['host']) === '1drv.ms') {
|
||||
$downloadData['maxRetrials'] = 3;
|
||||
// According to instructions from:
|
||||
// https://towardsdatascience.com/how-to-get-onedrive-direct-download-link-ecb52a62fee4
|
||||
$base64Value = base64_encode($url);
|
||||
$encodedUrl = "u!" . rtrim($base64Value, "=");
|
||||
$encodedUrl = str_replace('+', '-', str_replace('/', '_', $encodedUrl));
|
||||
$nextURL = "https://api.onedrive.com/v1.0/shares/$encodedUrl/root/content";
|
||||
DUP_PRO_Log::trace("Next step OneDrive URL: $nextURL");
|
||||
|
||||
// Extracting archive name and real url from headers of $nextURL
|
||||
$response = Requests::get(
|
||||
$nextURL,
|
||||
array('Range' => "bytes=0-0"),
|
||||
array(
|
||||
'timeout' => 60,
|
||||
//'protocol_version' => 1.1,
|
||||
//'transport' => "\\VendorDuplicator\\WpOrg\\Requests\\Transport\\Fsockopen",
|
||||
'verify' => false,
|
||||
'verifyname' => false,
|
||||
)
|
||||
);
|
||||
|
||||
if (
|
||||
$response->success == false ||
|
||||
!isset($response->headers["content-disposition"]) ||
|
||||
strlen($response->headers["content-disposition"]) == 0 ||
|
||||
!isset($response->headers["content-location"]) ||
|
||||
strlen($response->headers["content-location"]) == 0
|
||||
) {
|
||||
throw new Exception("Could not get direct url and archive name for OneDrive url: $url, unexpected headers");
|
||||
}
|
||||
$pattern = "/filename=\"(.+)\"/msU";
|
||||
$result = preg_match($pattern, $response->headers["content-disposition"], $matches);
|
||||
if (!$result) {
|
||||
throw new Exception("Could not get archive name for OneDrive url: $url, no filename in headers");
|
||||
}
|
||||
$archiveName = $matches[1];
|
||||
$downloadData['archiveName'] = $archiveName;
|
||||
DUP_PRO_Log::trace("Archive name on OneDrive: $archiveName");
|
||||
|
||||
$realURL = $response->headers["content-location"];
|
||||
DUP_PRO_Log::trace("Real OneDrive URL: $realURL");
|
||||
$downloadData['url'] = $realURL;
|
||||
|
||||
$downloadData['chunkTime'] = 5;
|
||||
}
|
||||
return $downloadData;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Ajax;
|
||||
|
||||
use DUP_PRO_Handler;
|
||||
use Duplicator\Addons\ProBase\License\License;
|
||||
use Duplicator\Core\CapMng;
|
||||
use Duplicator\Libs\Snap\SnapJson;
|
||||
use Duplicator\Models\BrandEntity;
|
||||
use Exception;
|
||||
|
||||
class ServicesBrand extends AbstractAjaxService
|
||||
{
|
||||
/**
|
||||
* Init ajax calls
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
if (!License::can(License::CAPABILITY_PRO_BASE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->addAjaxCall('wp_ajax_duplicator_pro_brand_delete', 'brandDelete');
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook ajax wp_ajax_duplicator_pro_brand_delete
|
||||
*
|
||||
* @return never
|
||||
*/
|
||||
public function brandDelete()
|
||||
{
|
||||
DUP_PRO_Handler::init_error_handler();
|
||||
check_ajax_referer('duplicator_pro_brand_delete', 'nonce');
|
||||
|
||||
$json = array(
|
||||
'success' => false,
|
||||
'message' => '',
|
||||
);
|
||||
$isValid = true;
|
||||
$inputData = filter_input_array(INPUT_POST, array(
|
||||
'brand_ids' => array(
|
||||
'filter' => FILTER_VALIDATE_INT,
|
||||
'flags' => FILTER_REQUIRE_ARRAY,
|
||||
'options' => array('default' => false),
|
||||
),
|
||||
));
|
||||
$brandIDs = $inputData['brand_ids'];
|
||||
$delCount = 0;
|
||||
|
||||
if (empty($brandIDs) || in_array(false, $brandIDs)) {
|
||||
$isValid = false;
|
||||
}
|
||||
|
||||
try {
|
||||
CapMng::can(CapMng::CAP_CREATE);
|
||||
if (!$isValid) {
|
||||
throw new Exception(__('Invalid Request.', 'duplicator-pro'));
|
||||
}
|
||||
|
||||
foreach ($brandIDs as $id) {
|
||||
$brand = BrandEntity::deleteById($id);
|
||||
if ($brand) {
|
||||
$delCount++;
|
||||
}
|
||||
}
|
||||
|
||||
$json['success'] = true;
|
||||
$json['ids'] = $brandIDs;
|
||||
$json['removed'] = $delCount;
|
||||
} catch (Exception $e) {
|
||||
$json['message'] = $e->getMessage();
|
||||
}
|
||||
|
||||
die(SnapJson::jsonEncode($json));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Ajax;
|
||||
|
||||
use DUP_PRO_Package;
|
||||
use Duplicator\Ajax\AjaxWrapper;
|
||||
use Duplicator\Core\CapMng;
|
||||
use Duplicator\Views\DashboardWidget;
|
||||
|
||||
class ServicesDashboard extends AbstractAjaxService
|
||||
{
|
||||
/**
|
||||
* Init ajax calls
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->addAjaxCall('wp_ajax_duplicator_pro_dashboad_widget_info', 'dashboardWidgetInfo');
|
||||
$this->addAjaxCall('wp_ajax_duplicator_pro_dismiss_recommended_plugin', 'dismissRecommendedPlugin');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set recovery callback
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public static function dashboardWidgetInfoCallback()
|
||||
{
|
||||
$result = [
|
||||
'isRunning' => DUP_PRO_Package::isPackageRunning(),
|
||||
'lastBackupInfo' => DashboardWidget::getLastBackupString(),
|
||||
];
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set recovery action
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function dashboardWidgetInfo()
|
||||
{
|
||||
AjaxWrapper::json(
|
||||
array(
|
||||
__CLASS__,
|
||||
'dashboardWidgetInfoCallback',
|
||||
),
|
||||
'duplicator_pro_dashboad_widget_info',
|
||||
$_POST['nonce'],
|
||||
CapMng::CAP_BASIC
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set dismiss recommended callback
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function dismissRecommendedPluginCallback()
|
||||
{
|
||||
return (update_user_meta(get_current_user_id(), DashboardWidget::RECOMMENDED_PLUGIN_DISMISSED_OPT_KEY, true) !== false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set recovery action
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function dismissRecommendedPlugin()
|
||||
{
|
||||
AjaxWrapper::json(
|
||||
array(
|
||||
__CLASS__,
|
||||
'dismissRecommendedPluginCallback',
|
||||
),
|
||||
'duplicator_pro_dashboad_widget_dismiss_recommended',
|
||||
$_POST['nonce'],
|
||||
CapMng::CAP_BASIC
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Ajax;
|
||||
|
||||
use Duplicator\Addons\ProBase\License\License;
|
||||
use Duplicator\Ajax\AjaxWrapper;
|
||||
use Duplicator\Ajax\FileTransfer\ImportUpload;
|
||||
use Duplicator\Controllers\ImportPageController;
|
||||
use Duplicator\Core\CapMng;
|
||||
use Duplicator\Libs\Snap\SnapUtil;
|
||||
use Duplicator\Package\Import\PackageImporter;
|
||||
use Exception;
|
||||
|
||||
class ServicesImport extends AbstractAjaxService
|
||||
{
|
||||
/**
|
||||
* Init ajax calls
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
if (!License::can(License::CAPABILITY_PRO_BASE)) {
|
||||
return;
|
||||
}
|
||||
$this->addAjaxCall('wp_ajax_duplicator_pro_import_upload', 'importUpload');
|
||||
$this->addAjaxCall('wp_ajax_duplicator_pro_import_package_delete', 'deletePackage');
|
||||
$this->addAjaxCall('wp_ajax_duplicator_pro_import_set_view_mode', 'setViewMode');
|
||||
$this->addAjaxCall('wp_ajax_duplicator_pro_import_remote_download', 'remoteDownload');
|
||||
$this->addAjaxCall('wp_ajax_duplicator_pro_import_set_archive_password', 'setArchivePassword');
|
||||
}
|
||||
|
||||
/**
|
||||
* Import upload callback logic
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
public static function importUploadCallback()
|
||||
{
|
||||
$uploader = new ImportUpload(ImportUpload::MODE_UPLOAD_LOCAL);
|
||||
return $uploader->exec();
|
||||
}
|
||||
|
||||
/**
|
||||
* Import upload action
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function importUpload()
|
||||
{
|
||||
AjaxWrapper::json(
|
||||
array(
|
||||
__CLASS__,
|
||||
'importUploadCallback',
|
||||
),
|
||||
'duplicator_pro_import_upload',
|
||||
$_POST['nonce'],
|
||||
CapMng::CAP_IMPORT
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Import download remote callback logic
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
public static function remoteDownloadCallback()
|
||||
{
|
||||
$uploader = new ImportUpload(ImportUpload::MODE_DOWNLOAD_REMOTE);
|
||||
return $uploader->exec();
|
||||
}
|
||||
|
||||
/**
|
||||
* Import download remote action
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function remoteDownload()
|
||||
{
|
||||
AjaxWrapper::json(
|
||||
array(
|
||||
__CLASS__,
|
||||
'remoteDownloadCallback',
|
||||
),
|
||||
'duplicator_pro_remote_download',
|
||||
$_POST['nonce'],
|
||||
CapMng::CAP_IMPORT
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Import delete package callback
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function deletePackageCallback()
|
||||
{
|
||||
$inputData = filter_input_array(INPUT_POST, array(
|
||||
'path' => array(
|
||||
'filter' => FILTER_SANITIZE_SPECIAL_CHARS,
|
||||
'flags' => FILTER_REQUIRE_SCALAR,
|
||||
'options' => array('default' => ''),
|
||||
),
|
||||
));
|
||||
|
||||
if (empty($inputData['path'])) {
|
||||
throw new Exception(__("Invalid Request!", 'duplicator-pro'));
|
||||
}
|
||||
|
||||
if (in_array($inputData['path'], PackageImporter::getArchiveList())) {
|
||||
if (unlink($inputData['path']) == false) {
|
||||
throw new Exception(__("Can\'t remove archvie!", 'duplicator-pro'));
|
||||
}
|
||||
PackageImporter::cleanFolder();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Import delete backage action
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function deletePackage()
|
||||
{
|
||||
AjaxWrapper::json(
|
||||
array(
|
||||
__CLASS__,
|
||||
'deletePackageCallback',
|
||||
),
|
||||
'duplicator_pro_import_package_delete',
|
||||
$_POST['nonce'],
|
||||
CapMng::CAP_IMPORT
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set import view mode callback
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function setViewModeCallback()
|
||||
{
|
||||
$viewMode = filter_input(INPUT_POST, 'view_mode', FILTER_SANITIZE_SPECIAL_CHARS);
|
||||
|
||||
switch ($viewMode) {
|
||||
case ImportPageController::VIEW_MODE_ADVANCED:
|
||||
case ImportPageController::VIEW_MODE_BASIC:
|
||||
break;
|
||||
default:
|
||||
throw new Exception(__('Invalid view mode', 'duplicator-pro'));
|
||||
}
|
||||
|
||||
if (!($userId = get_current_user_id())) {
|
||||
throw new Exception(__('Invalid current urser id', 'duplicator-pro'));
|
||||
}
|
||||
|
||||
$archives = PackageImporter::getArchiveList();
|
||||
if ($viewMode == ImportPageController::VIEW_MODE_BASIC && count($archives) > 1) {
|
||||
update_user_meta($userId, ImportPageController::USER_META_VIEW_MODE, ImportPageController::VIEW_MODE_ADVANCED);
|
||||
$message = __(
|
||||
'It is not possible to set the view mode to basic if the number of packages is more than one.',
|
||||
'duplicator-pro'
|
||||
) . ' ' .
|
||||
__('Remove packages before performing this action.', 'duplicator-pro');
|
||||
throw new Exception($message);
|
||||
}
|
||||
|
||||
if ($viewMode != ImportPageController::getViewMode()) {
|
||||
if (update_user_meta($userId, ImportPageController::USER_META_VIEW_MODE, $viewMode) == false) {
|
||||
throw new Exception(__('Can\'t update user meta value', 'duplicator-pro'));
|
||||
}
|
||||
}
|
||||
|
||||
return ImportPageController::getViewMode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set import view mode action
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setViewMode()
|
||||
{
|
||||
AjaxWrapper::json(
|
||||
array(
|
||||
__CLASS__,
|
||||
'setViewModeCallback',
|
||||
),
|
||||
'duplicator_pro_import_set_view_mode',
|
||||
$_POST['nonce'],
|
||||
CapMng::CAP_IMPORT
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set import view mode callback
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
public static function setArchivePasswordCallback()
|
||||
{
|
||||
$result = [];
|
||||
|
||||
$archiveFile = filter_input(INPUT_POST, 'archive', FILTER_CALLBACK, ['options' => [SnapUtil::class, 'sanitizeNSCharsNewlineTabs']]);
|
||||
$password = filter_input(INPUT_POST, 'password', FILTER_CALLBACK, ['options' => [SnapUtil::class, 'sanitizeNSCharsNewlineTrim']]);
|
||||
|
||||
if (preg_match('/^.*\.(zip|daf)$/', $archiveFile) !== 1) {
|
||||
throw new Exception('Invalid archive name "' . $archiveFile . '"');
|
||||
}
|
||||
|
||||
$importObj = new PackageImporter($archiveFile);
|
||||
$errMsg = '';
|
||||
if (!$importObj->encryptCheck($errMs)) {
|
||||
throw new Exception($errMsg);
|
||||
} elseif ($importObj->passwordCheck($password)) {
|
||||
$importObj->updatePasswordCookie();
|
||||
$uploader = new ImportUpload(ImportUpload::MODE_UPLOADED, $archiveFile);
|
||||
$result = $uploader->exec();
|
||||
} else {
|
||||
throw new Exception('Invalid password');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set import view mode action
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setArchivePassword()
|
||||
{
|
||||
AjaxWrapper::json(
|
||||
array(
|
||||
__CLASS__,
|
||||
'setArchivePasswordCallback',
|
||||
),
|
||||
'duplicator_pro_import_set_archive_password',
|
||||
$_POST['nonce'],
|
||||
CapMng::CAP_IMPORT
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Ajax;
|
||||
|
||||
use Duplicator\Addons\ProBase\License\License;
|
||||
use Duplicator\Ajax\AjaxWrapper;
|
||||
use Duplicator\Core\CapMng;
|
||||
use Duplicator\Core\Views\Notifications;
|
||||
use Duplicator\Models\SystemGlobalEntity;
|
||||
use Duplicator\Views\AdminNotices;
|
||||
use Exception;
|
||||
|
||||
class ServicesNotifications extends AbstractAjaxService
|
||||
{
|
||||
/**
|
||||
* Init ajax calls
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
if (!License::can(License::CAPABILITY_PRO_BASE)) {
|
||||
return;
|
||||
}
|
||||
$this->addAjaxCall('wp_ajax_duplicator_notification_dismiss', 'setDissmisedNotifications');
|
||||
$this->addAjaxCall('wp_ajax_duplicator_pro_admin_notice_to_dismiss', 'admin_notice_to_dismiss');
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismiss notification
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function dismissNotifications()
|
||||
{
|
||||
$id = sanitize_key($_POST['id']);
|
||||
return Notifications::dismiss($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set dismiss notification action
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setDissmisedNotifications()
|
||||
{
|
||||
AjaxWrapper::json(
|
||||
array(
|
||||
__CLASS__,
|
||||
'dismissNotifications',
|
||||
),
|
||||
Notifications::DUPLICATOR_NOTIFICATION_NONCE_KEY,
|
||||
$_POST['nonce'],
|
||||
'manage_options'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* AJjax callback for admin_notice_to_dismiss
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function adminNoticeToDismissCallback()
|
||||
{
|
||||
|
||||
$noticeToDismiss = filter_input(INPUT_POST, 'notice', FILTER_SANITIZE_SPECIAL_CHARS);
|
||||
$systemGlobal = SystemGlobalEntity::getInstance();
|
||||
switch ($noticeToDismiss) {
|
||||
case AdminNotices::OPTION_KEY_ACTIVATE_PLUGINS_AFTER_INSTALL:
|
||||
case AdminNotices::OPTION_KEY_MIGRATION_SUCCESS_NOTICE:
|
||||
$ret = delete_option($noticeToDismiss);
|
||||
break;
|
||||
case AdminNotices::OPTION_KEY_S3_CONTENTS_FETCH_FAIL_NOTICE:
|
||||
$ret = update_option(AdminNotices::OPTION_KEY_S3_CONTENTS_FETCH_FAIL_NOTICE, false);
|
||||
break;
|
||||
case AdminNotices::QUICK_FIX_NOTICE:
|
||||
$systemGlobal->clearFixes();
|
||||
$ret = $systemGlobal->save();
|
||||
break;
|
||||
case AdminNotices::FAILED_SCHEDULE_NOTICE:
|
||||
$systemGlobal->schedule_failed = false;
|
||||
$ret = $systemGlobal->save();
|
||||
break;
|
||||
default:
|
||||
throw new Exception('Notice invalid');
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook ajax wp_ajax_duplicator_pro_admin_notice_to_dismiss
|
||||
*
|
||||
* @return never
|
||||
*/
|
||||
public static function adminNoticeToDismiss()
|
||||
{
|
||||
AjaxWrapper::json(
|
||||
array(
|
||||
__CLASS__,
|
||||
'adminNoticeToDismissCallback',
|
||||
),
|
||||
'duplicator_pro_admin_notice_to_dismiss',
|
||||
$_POST['nonce'],
|
||||
CapMng::CAP_BASIC
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,918 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Ajax;
|
||||
|
||||
use DUP_PRO_Archive;
|
||||
use DUP_PRO_Archive_Build_Mode;
|
||||
use DUP_PRO_DATE;
|
||||
use DUP_PRO_Global_Entity;
|
||||
use DUP_PRO_Handler;
|
||||
use DUP_PRO_Log;
|
||||
use DUP_PRO_Package;
|
||||
use DUP_PRO_Package_File_Type;
|
||||
use DUP_PRO_Package_Runner;
|
||||
use DUP_PRO_Package_Template_Entity;
|
||||
use DUP_PRO_Package_Upload_Info;
|
||||
use DUP_PRO_PackageStatus;
|
||||
use DUP_PRO_Thread_Lock_Mode;
|
||||
use DUP_PRO_Tree_files;
|
||||
use DUP_PRO_U;
|
||||
use DUP_PRO_Upload_Status;
|
||||
use DUP_PRO_ZipArchive_Mode;
|
||||
use Duplicator\Addons\ProBase\License\License;
|
||||
use Duplicator\Core\CapMng;
|
||||
use Duplicator\Libs\Snap\SnapJson;
|
||||
use Duplicator\Libs\Snap\SnapUtil;
|
||||
use Duplicator\Models\Storages\AbstractStorageEntity;
|
||||
use Duplicator\Models\Storages\StoragesUtil;
|
||||
use Error;
|
||||
use Exception;
|
||||
use stdClass;
|
||||
use VendorDuplicator\Amk\JsonSerialize\JsonSerialize;
|
||||
|
||||
class ServicesPackage extends AbstractAjaxService
|
||||
{
|
||||
const EXEC_STATUS_PASS = 1;
|
||||
const EXEC_STATUS_WARN = 2;
|
||||
const EXEC_STATUS_FAIL = 3;
|
||||
const EXEC_STATUS_INCOMPLETE = 4; // Still more to go
|
||||
const EXEC_STATUS_SCHEDULE_RUNNING = 5;
|
||||
|
||||
/**
|
||||
* Init ajax calls
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->addAjaxCall('wp_ajax_duplicator_pro_process_worker', 'processWorker');
|
||||
$this->addAjaxCall('wp_ajax_nopriv_duplicator_pro_process_worker', 'processWorker');
|
||||
|
||||
$this->addAjaxCall('wp_ajax_duplicator_pro_download_package_file', 'downloadPackageFile');
|
||||
$this->addAjaxCall('wp_ajax_nopriv_duplicator_pro_download_package_file', 'downloadPackageFile');
|
||||
|
||||
if (!License::can(License::CAPABILITY_PRO_BASE)) {
|
||||
return;
|
||||
}
|
||||
$this->addAjaxCall('wp_ajax_duplicator_add_quick_filters', 'addQuickFilters');
|
||||
$this->addAjaxCall('wp_ajax_duplicator_pro_package_scan', 'packageScan');
|
||||
$this->addAjaxCall('wp_ajax_duplicator_pro_package_delete', 'packageDelete');
|
||||
$this->addAjaxCall('wp_ajax_duplicator_pro_reset_packages', 'resetPackages');
|
||||
$this->addAjaxCall('wp_ajax_duplicator_pro_get_package_statii', 'packageStatii');
|
||||
$this->addAjaxCall('wp_ajax_duplicator_pro_package_stop_build', 'stopBuild');
|
||||
$this->addAjaxCall('wp_ajax_duplicator_pro_manual_transfer_storage', 'manualTransferStorage');
|
||||
$this->addAjaxCall('wp_ajax_duplicator_pro_packages_details_transfer_get_package_vm', 'detailsTransferGetPackageVM');
|
||||
$this->addAjaxCall('wp_ajax_duplicator_pro_get_folder_children', 'getFolderChildren');
|
||||
}
|
||||
|
||||
/**
|
||||
* Removed all reserved installer files names
|
||||
*
|
||||
* @return never
|
||||
*/
|
||||
public function addQuickFilters()
|
||||
{
|
||||
DUP_PRO_Handler::init_error_handler();
|
||||
check_ajax_referer('duplicator_add_quick_filters', 'nonce');
|
||||
$inputData = filter_input_array(INPUT_POST, array(
|
||||
'dir_paths' => array(
|
||||
'filter' => FILTER_DEFAULT,
|
||||
'flags' => FILTER_REQUIRE_SCALAR,
|
||||
'options' => array('default' => ''),
|
||||
),
|
||||
'file_paths' => array(
|
||||
'filter' => FILTER_DEFAULT,
|
||||
'flags' => FILTER_REQUIRE_SCALAR,
|
||||
'options' => array('default' => ''),
|
||||
),
|
||||
));
|
||||
$result = [
|
||||
'success' => false,
|
||||
'message' => '',
|
||||
'filter-dirs' => '',
|
||||
'filter-files' => '',
|
||||
'filter-names' => '',
|
||||
];
|
||||
try {
|
||||
// CONTROLLER LOGIC
|
||||
// Need to update both the template and the temporary package because:
|
||||
// 1) We need to preserve preferences of this build for future manual builds - the manual template is used for this.
|
||||
// 2) Temporary package is used during this build - keeps all the settings/storage information.
|
||||
// Will be inserted into the package table after they ok the scan results.
|
||||
$template = DUP_PRO_Package_Template_Entity::get_manual_template();
|
||||
$dirPaths = DUP_PRO_Archive::parseDirectoryFilter(SnapUtil::sanitizeNSChars($inputData['dir_paths']));
|
||||
$filePaths = DUP_PRO_Archive::parseFileFilter(SnapUtil::sanitizeNSChars($inputData['file_paths']));
|
||||
|
||||
if (strlen($dirPaths) > 0) {
|
||||
$template->archive_filter_dirs .= strlen($template->archive_filter_dirs) > 0 ? ';' . $dirPaths : $dirPaths;
|
||||
}
|
||||
|
||||
if (strlen($filePaths) > 0) {
|
||||
$template->archive_filter_files .= strlen($template->archive_filter_files) > 0 ? ';' . $filePaths : $filePaths;
|
||||
}
|
||||
|
||||
if (!$template->archive_filter_on) {
|
||||
$template->archive_filter_exts = '';
|
||||
}
|
||||
|
||||
$template->archive_filter_on = 1;
|
||||
$template->archive_filter_names = true;
|
||||
$template->save();
|
||||
|
||||
$temporary_package = DUP_PRO_Package::get_temporary_package();
|
||||
$temporary_package->Archive->FilterDirs = $template->archive_filter_dirs;
|
||||
$temporary_package->Archive->FilterFiles = $template->archive_filter_files;
|
||||
$temporary_package->Archive->FilterOn = true;
|
||||
$temporary_package->Archive->FilterNames = $template->archive_filter_names;
|
||||
$temporary_package->set_temporary_package();
|
||||
|
||||
$result['success'] = true;
|
||||
$result['filter-dirs'] = $temporary_package->Archive->FilterDirs;
|
||||
$result['filter-files'] = $temporary_package->Archive->FilterFiles;
|
||||
$result['filter-names'] = $temporary_package->Archive->FilterNames;
|
||||
} catch (Exception $exc) {
|
||||
$result['success'] = false;
|
||||
$result['message'] = $exc->getMessage();
|
||||
}
|
||||
|
||||
wp_send_json($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* DUPLICATOR_PRO_PACKAGE_SCAN
|
||||
*
|
||||
* @example to test: /wp-admin/admin-ajax.php?action=duplicator_pro_package_scan
|
||||
*
|
||||
* @return never
|
||||
*/
|
||||
public function packageScan()
|
||||
{
|
||||
DUP_PRO_Handler::init_error_handler();
|
||||
try {
|
||||
CapMng::can(CapMng::CAP_CREATE);
|
||||
$global = DUP_PRO_Global_Entity::getInstance();
|
||||
|
||||
// Should be used $_REQUEST sometimes it gets in _GET and sometimes in _POST
|
||||
check_ajax_referer('duplicator_pro_package_scan', 'nonce');
|
||||
header('Content-Type: application/json');
|
||||
@ob_flush();
|
||||
|
||||
$json = array();
|
||||
$errLevel = error_reporting();
|
||||
|
||||
// Keep the locking file opening and closing just to avoid adding even more complexity
|
||||
$locking_file = true;
|
||||
if ($global->lock_mode == DUP_PRO_Thread_Lock_Mode::Flock) {
|
||||
$locking_file = fopen(DUPLICATOR_PRO_LOCKING_FILE_FILENAME, 'c+');
|
||||
}
|
||||
|
||||
if ($locking_file != false) {
|
||||
if ($global->lock_mode == DUP_PRO_Thread_Lock_Mode::Flock) {
|
||||
$acquired_lock = (flock($locking_file, LOCK_EX | LOCK_NB) != false);
|
||||
if ($acquired_lock) {
|
||||
DUP_PRO_Log::trace("File lock acquired " . DUPLICATOR_PRO_LOCKING_FILE_FILENAME);
|
||||
} else {
|
||||
DUP_PRO_Log::trace("File lock denied " . DUPLICATOR_PRO_LOCKING_FILE_FILENAME);
|
||||
}
|
||||
} else {
|
||||
$acquired_lock = DUP_PRO_U::getSqlLock();
|
||||
}
|
||||
|
||||
if ($acquired_lock) {
|
||||
@set_time_limit(0);
|
||||
error_reporting(E_ERROR);
|
||||
StoragesUtil::getDefaultStorage()->initStorageDirectory(true);
|
||||
|
||||
$package = DUP_PRO_Package::get_temporary_package();
|
||||
$package->ID = null;
|
||||
$report = $package->create_scan_report();
|
||||
//After scanner runs save FilterInfo (unreadable, warnings, globals etc)
|
||||
$package->set_temporary_package();
|
||||
|
||||
//delif($package->Archive->ScanStatus == DUP_PRO_Archive::ScanStatusComplete){
|
||||
$report['Status'] = self::EXEC_STATUS_PASS;
|
||||
|
||||
// The package has now been corrupted with directories and scans so cant reuse it after this point
|
||||
DUP_PRO_Package::set_temporary_package_member('ScanFile', $package->ScanFile);
|
||||
DUP_PRO_Package::tmp_cleanup();
|
||||
DUP_PRO_Package::set_temporary_package_member('Status', DUP_PRO_PackageStatus::AFTER_SCAN);
|
||||
|
||||
//del}
|
||||
|
||||
if ($global->lock_mode == DUP_PRO_Thread_Lock_Mode::Flock) {
|
||||
if (!flock($locking_file, LOCK_UN)) {
|
||||
DUP_PRO_Log::trace("File lock can't release " . $locking_file);
|
||||
} else {
|
||||
DUP_PRO_Log::trace("File lock released " . $locking_file);
|
||||
}
|
||||
fclose($locking_file);
|
||||
} else {
|
||||
DUP_PRO_U::releaseSqlLock();
|
||||
}
|
||||
} else {
|
||||
// File is already locked indicating schedule is running
|
||||
$report['Status'] = self::EXEC_STATUS_SCHEDULE_RUNNING;
|
||||
DUP_PRO_Log::trace("Already locked when attempting manual build - schedule running");
|
||||
}
|
||||
} else {
|
||||
// Problem opening the locking file report this is a critical error
|
||||
$report['Status'] = self::EXEC_STATUS_FAIL;
|
||||
|
||||
DUP_PRO_Log::trace("Problem opening locking file so auto switching to SQL lock mode");
|
||||
$global->lock_mode = DUP_PRO_Thread_Lock_Mode::SQL_Lock;
|
||||
$global->save();
|
||||
}
|
||||
} catch (Exception $ex) {
|
||||
$data = array(
|
||||
'Status' => 3,
|
||||
'Message' => sprintf(__("Exception occurred. Exception message: %s", 'duplicator-pro'), $ex->getMessage()),
|
||||
'File' => $ex->getFile(),
|
||||
'Line' => $ex->getLine(),
|
||||
'Trace' => $ex->getTrace(),
|
||||
);
|
||||
die(json_encode($data));
|
||||
} catch (Error $ex) {
|
||||
$data = array(
|
||||
'Status' => 3,
|
||||
'Message' => sprintf(
|
||||
esc_html__("Fatal Error occurred. Error message: %1\$s<br>\nTrace: %2\$s", 'duplicator-pro'),
|
||||
$ex->getMessage(),
|
||||
$ex->getTraceAsString()
|
||||
),
|
||||
'File' => $ex->getFile(),
|
||||
'Line' => $ex->getLine(),
|
||||
'Trace' => $ex->getTrace(),
|
||||
);
|
||||
die(json_encode($data));
|
||||
}
|
||||
|
||||
try {
|
||||
if (($json = JsonSerialize::serialize($report, JSON_PRETTY_PRINT | JsonSerialize::JSON_SKIP_CLASS_NAME)) === false) {
|
||||
throw new Exception('Problem encoding json');
|
||||
}
|
||||
} catch (Exception $ex) {
|
||||
$data = array(
|
||||
'Status' => 3,
|
||||
'Message' => sprintf(esc_html__("Fatal Error occurred. Error message: %s", 'duplicator-pro'), $ex->getMessage()),
|
||||
'File' => $ex->getFile(),
|
||||
'Line' => $ex->getLine(),
|
||||
'Trace' => $ex->getTrace(),
|
||||
);
|
||||
die(json_encode($data));
|
||||
}
|
||||
|
||||
error_reporting($errLevel);
|
||||
die($json);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Hook ajax wp_ajax_duplicator_pro_package_delete
|
||||
* Deletes the files and database record entries
|
||||
*
|
||||
* @return never
|
||||
*/
|
||||
public function packageDelete()
|
||||
{
|
||||
DUP_PRO_Handler::init_error_handler();
|
||||
check_ajax_referer('duplicator_pro_package_delete', 'nonce');
|
||||
|
||||
$json = array(
|
||||
'error' => '',
|
||||
'ids' => '',
|
||||
'removed' => 0,
|
||||
);
|
||||
$isValid = true;
|
||||
$deletedCount = 0;
|
||||
|
||||
$inputData = filter_input_array(INPUT_POST, array(
|
||||
'package_ids' => array(
|
||||
'filter' => FILTER_VALIDATE_INT,
|
||||
'flags' => FILTER_REQUIRE_ARRAY,
|
||||
'options' => array('default' => false),
|
||||
),
|
||||
));
|
||||
$packageIDList = $inputData['package_ids'];
|
||||
|
||||
if (empty($packageIDList) || in_array(false, $packageIDList)) {
|
||||
$isValid = false;
|
||||
}
|
||||
//END OF VALIDATION
|
||||
|
||||
try {
|
||||
CapMng::can(CapMng::CAP_CREATE);
|
||||
if (!$isValid) {
|
||||
throw new Exception(__("Invalid request.", 'duplicator-pro'));
|
||||
}
|
||||
|
||||
DUP_PRO_Log::traceObject("Starting deletion of packages by ids: ", $packageIDList);
|
||||
foreach ($packageIDList as $id) {
|
||||
if ($package = DUP_PRO_Package::get_by_id($id)) {
|
||||
if ($package->delete()) {
|
||||
$deletedCount++;
|
||||
}
|
||||
} else {
|
||||
$json['error'] = "Invalid package ID.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception $ex) {
|
||||
$json['error'] = $ex->getMessage();
|
||||
}
|
||||
|
||||
$json['ids'] = $packageIDList;
|
||||
$json['removed'] = $deletedCount;
|
||||
die(SnapJson::jsonEncode($json));
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook ajax wp_ajax_duplicator_pro_reset_packages
|
||||
*
|
||||
* @return never
|
||||
*/
|
||||
public function resetPackages()
|
||||
{
|
||||
ob_start();
|
||||
try {
|
||||
DUP_PRO_Handler::init_error_handler();
|
||||
|
||||
$error = false;
|
||||
$result = array(
|
||||
'data' => array('status' => null),
|
||||
'html' => '',
|
||||
'message' => '',
|
||||
);
|
||||
|
||||
$nonce = sanitize_text_field($_POST['nonce']);
|
||||
if (!wp_verify_nonce($nonce, 'duplicator_pro_reset_packages')) {
|
||||
DUP_PRO_Log::trace('Security issue');
|
||||
throw new Exception('Security issue');
|
||||
}
|
||||
CapMng::can(CapMng::CAP_SETTINGS);
|
||||
|
||||
// first last package id
|
||||
$ids = DUP_PRO_Package::get_ids_by_status(
|
||||
array(array('op' => '<', 'status' => DUP_PRO_PackageStatus::COMPLETE)),
|
||||
0,
|
||||
0,
|
||||
'`id` DESC'
|
||||
);
|
||||
foreach ($ids as $id) {
|
||||
// A smooth deletion is not performed because it is a forced reset.
|
||||
DUP_PRO_Package::force_delete($id);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$error = true;
|
||||
$result['message'] = $e->getMessage();
|
||||
}
|
||||
|
||||
$result['html'] = ob_get_clean();
|
||||
if ($error) {
|
||||
wp_send_json_error($result);
|
||||
} else {
|
||||
wp_send_json_success($result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Hook ajax wp_ajax_duplicator_pro_get_package_statii
|
||||
*
|
||||
* @return never
|
||||
*/
|
||||
public function packageStatii()
|
||||
{
|
||||
DUP_PRO_Handler::init_error_handler();
|
||||
check_ajax_referer('duplicator_pro_get_package_statii', 'nonce');
|
||||
CapMng::can(CapMng::CAP_BASIC);
|
||||
|
||||
$limit = SnapUtil::sanitizeIntInput(SnapUtil::INPUT_REQUEST, 'limit', 0);
|
||||
$offset = SnapUtil::sanitizeIntInput(SnapUtil::INPUT_REQUEST, 'offset', 0);
|
||||
|
||||
$resultData = [];
|
||||
|
||||
DUP_PRO_Package::by_status_callback(
|
||||
function (DUP_PRO_Package $package) use (&$resultData) {
|
||||
$package_status = new stdClass();
|
||||
$package_status->ID = $package->ID;
|
||||
$package_status->status = self::getAdjustedPackageStatus($package);
|
||||
$package_status->status_progress = $package->get_status_progress();
|
||||
$package_status->size = $package->get_display_size();
|
||||
$package_status->status_progress_text = '';
|
||||
|
||||
if ($package_status->status < DUP_PRO_PackageStatus::COMPLETE) {
|
||||
$active_storage = $package->get_active_storage();
|
||||
if ($active_storage !== false) {
|
||||
$package_status->status_progress_text = $active_storage->getActionText();
|
||||
} else {
|
||||
$package_status->status_progress_text = '';
|
||||
}
|
||||
}
|
||||
$resultData[] = $package_status;
|
||||
},
|
||||
[],
|
||||
$limit,
|
||||
$offset,
|
||||
'`id` DESC'
|
||||
);
|
||||
|
||||
wp_send_json($resultData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the package status
|
||||
*
|
||||
* @param DUP_PRO_Package $package The package to get the status for
|
||||
*
|
||||
* @return int|float
|
||||
*/
|
||||
private static function getAdjustedPackageStatus(DUP_PRO_Package $package)
|
||||
{
|
||||
$estimated_progress = ($package->build_progress->current_build_mode == DUP_PRO_Archive_Build_Mode::Shell_Exec) ||
|
||||
($package->ziparchive_mode == DUP_PRO_ZipArchive_Mode::SingleThread);
|
||||
|
||||
if (($package->Status == DUP_PRO_PackageStatus::ARCSTART) && $estimated_progress) {
|
||||
// Amount of time passing before we give them a 1%
|
||||
$time_per_percent = 11;
|
||||
$thread_age = time() - $package->build_progress->thread_start_time;
|
||||
$total_percentage_delta = DUP_PRO_PackageStatus::ARCDONE - DUP_PRO_PackageStatus::ARCSTART;
|
||||
|
||||
if ($thread_age > ($total_percentage_delta * $time_per_percent)) {
|
||||
// It's maxed out so just give them the done condition for the rest of the time
|
||||
return DUP_PRO_PackageStatus::ARCDONE;
|
||||
} else {
|
||||
$percentage_delta = (int) ($thread_age / $time_per_percent);
|
||||
|
||||
return DUP_PRO_PackageStatus::ARCSTART + $percentage_delta;
|
||||
}
|
||||
} else {
|
||||
return $package->Status;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook ajax wp_ajax_duplicator_pro_package_stop_build
|
||||
*
|
||||
* @return never
|
||||
*/
|
||||
public function stopBuild()
|
||||
{
|
||||
DUP_PRO_Handler::init_error_handler();
|
||||
check_ajax_referer('duplicator_pro_package_stop_build', 'nonce');
|
||||
|
||||
CapMng::can(CapMng::CAP_CREATE);
|
||||
|
||||
$json = array(
|
||||
'success' => false,
|
||||
'message' => '',
|
||||
);
|
||||
$isValid = true;
|
||||
$inputData = filter_input_array(INPUT_POST, array(
|
||||
'package_id' => array(
|
||||
'filter' => FILTER_VALIDATE_INT,
|
||||
'flags' => FILTER_REQUIRE_SCALAR,
|
||||
'options' => array('default' => false),
|
||||
),
|
||||
));
|
||||
$package_id = $inputData['package_id'];
|
||||
|
||||
if (!$package_id) {
|
||||
$isValid = false;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!$isValid) {
|
||||
throw new Exception('Invalid request.');
|
||||
}
|
||||
|
||||
DUP_PRO_Log::trace("Web service stop build of $package_id");
|
||||
$package = DUP_PRO_Package::get_by_id($package_id);
|
||||
|
||||
if ($package == null) {
|
||||
DUP_PRO_Log::trace(
|
||||
"could not find package so attempting hard delete.
|
||||
Old files may end up sticking around although chances are there isnt much if we couldnt nicely cancel it."
|
||||
);
|
||||
$result = DUP_PRO_Package::force_delete($package_id);
|
||||
|
||||
if ($result) {
|
||||
$json['message'] = 'Hard delete success';
|
||||
$json['success'] = true;
|
||||
} else {
|
||||
throw new Exception('Hard delete failure');
|
||||
}
|
||||
} else {
|
||||
DUP_PRO_Log::trace("set $package->ID for cancel");
|
||||
$package->set_for_cancel();
|
||||
$json['success'] = true;
|
||||
}
|
||||
} catch (Exception $ex) {
|
||||
DUP_PRO_Log::trace($ex->getMessage());
|
||||
$json['message'] = $ex->getMessage();
|
||||
}
|
||||
|
||||
die(SnapJson::jsonEncode($json));
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook ajax process worker
|
||||
*
|
||||
* @return never
|
||||
*/
|
||||
public function processWorker()
|
||||
{
|
||||
DUP_PRO_Handler::init_error_handler();
|
||||
DUP_PRO_U::checkAjax();
|
||||
header("HTTP/1.1 200 OK");
|
||||
|
||||
/*
|
||||
$nonce = sanitize_text_field($_REQUEST['nonce']);
|
||||
if (!wp_verify_nonce($nonce, 'duplicator_pro_process_worker')) {
|
||||
DUP_PRO_Log::trace('Security issue');
|
||||
die('Security issue');
|
||||
}
|
||||
*/
|
||||
|
||||
DUP_PRO_Log::trace("Process worker request");
|
||||
|
||||
DUP_PRO_Package_Runner::process();
|
||||
|
||||
DUP_PRO_Log::trace("Exiting process worker request");
|
||||
|
||||
echo 'ok';
|
||||
exit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook ajax wp_ajax_duplicator_pro_download_package_file
|
||||
*
|
||||
* @return never
|
||||
*/
|
||||
public function downloadPackageFile()
|
||||
{
|
||||
DUP_PRO_Handler::init_error_handler();
|
||||
$inputData = filter_input_array(INPUT_GET, array(
|
||||
'fileType' => array(
|
||||
'filter' => FILTER_VALIDATE_INT,
|
||||
'flags' => FILTER_REQUIRE_SCALAR,
|
||||
'options' => array('default' => false),
|
||||
),
|
||||
'hash' => array(
|
||||
'filter' => FILTER_SANITIZE_SPECIAL_CHARS,
|
||||
'flags' => FILTER_REQUIRE_SCALAR,
|
||||
'options' => array('default' => false),
|
||||
),
|
||||
'token' => array(
|
||||
'filter' => FILTER_SANITIZE_SPECIAL_CHARS,
|
||||
'flags' => FILTER_REQUIRE_SCALAR,
|
||||
'options' => array('default' => false),
|
||||
),
|
||||
));
|
||||
|
||||
try {
|
||||
if (
|
||||
$inputData['token'] === false || $inputData['hash'] === false || $inputData["fileType"] === false
|
||||
|| md5(\Duplicator\Utils\Crypt\CryptBlowfish::encrypt($inputData['hash'])) !== $inputData['token']
|
||||
|| ($package = DUP_PRO_Package::get_by_hash($inputData['hash'])) == false
|
||||
) {
|
||||
throw new Exception(__("Invalid request.", 'duplicator-pro'));
|
||||
}
|
||||
|
||||
switch ($inputData['fileType']) {
|
||||
case DUP_PRO_Package_File_Type::Installer:
|
||||
$filePath = $package->getLocalPackageFilePath(DUP_PRO_Package_File_Type::Installer);
|
||||
$fileName = $package->Installer->getDownloadName();
|
||||
break;
|
||||
case DUP_PRO_Package_File_Type::Archive:
|
||||
$filePath = $package->getLocalPackageFilePath(DUP_PRO_Package_File_Type::Archive);
|
||||
$fileName = basename($filePath);
|
||||
break;
|
||||
case DUP_PRO_Package_File_Type::Log:
|
||||
$filePath = $package->getLocalPackageFilePath(DUP_PRO_Package_File_Type::Log);
|
||||
$fileName = basename($filePath);
|
||||
break;
|
||||
default:
|
||||
throw new Exception(__("File type not supported.", 'duplicator-pro'));
|
||||
}
|
||||
|
||||
if ($filePath == false) {
|
||||
throw new Exception(__("File don\'t exists", 'duplicator-pro'));
|
||||
}
|
||||
|
||||
\Duplicator\Libs\Snap\SnapIO::serveFileForDownload($filePath, $fileName, DUPLICATOR_PRO_BUFFER_DOWNLOAD_SIZE);
|
||||
} catch (Exception $ex) {
|
||||
wp_die($ex->getMessage());
|
||||
}
|
||||
die();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook ajax handler for packages_details_transfer_get_package_vm
|
||||
* Retrieve view model for the Packages/Details/Transfer screen
|
||||
* active_package_id: true/false
|
||||
* percent_text: Percent through the current transfer
|
||||
* text: Text to display
|
||||
* transfer_logs: array of transfer request vms (start, stop, status, message)
|
||||
*
|
||||
* @return never
|
||||
*/
|
||||
public function detailsTransferGetPackageVM()
|
||||
{
|
||||
DUP_PRO_Handler::init_error_handler();
|
||||
check_ajax_referer('duplicator_pro_packages_details_transfer_get_package_vm', 'nonce');
|
||||
|
||||
$json = array(
|
||||
'success' => false,
|
||||
'message' => '',
|
||||
);
|
||||
$isValid = true;
|
||||
$inputData = filter_input_array(INPUT_POST, array(
|
||||
'package_id' => array(
|
||||
'filter' => FILTER_VALIDATE_INT,
|
||||
'flags' => FILTER_REQUIRE_SCALAR,
|
||||
'options' => array('default' => false),
|
||||
),
|
||||
));
|
||||
|
||||
$package_id = $inputData['package_id'];
|
||||
if (!$package_id) {
|
||||
$isValid = false;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!CapMng::can(CapMng::CAP_STORAGE, false) && !CapMng::can(CapMng::CAP_CREATE, false)) {
|
||||
throw new Exception('Security issue.');
|
||||
}
|
||||
|
||||
if (!$isValid) {
|
||||
throw new Exception(__("Invalid request.", 'duplicator-pro'));
|
||||
}
|
||||
|
||||
$package = DUP_PRO_Package::get_by_id($package_id);
|
||||
if (!$package) {
|
||||
$msg = sprintf(__('Could not get package by ID %s', 'duplicator-pro'), $package_id);
|
||||
throw new Exception($msg);
|
||||
}
|
||||
|
||||
$vm = new stdClass();
|
||||
|
||||
/* -- First populate the transfer log information -- */
|
||||
|
||||
// If this is the package being requested include the transfer details
|
||||
$vm->transfer_logs = array();
|
||||
|
||||
$active_upload_info = null;
|
||||
|
||||
$storages = AbstractStorageEntity::getAll();
|
||||
|
||||
foreach ($package->upload_infos as &$upload_info) {
|
||||
if ($upload_info->getStorageId() === StoragesUtil::getDefaultStorageId()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$status = $upload_info->get_status();
|
||||
$status_text = $upload_info->get_status_text();
|
||||
|
||||
$transfer_log = new stdClass();
|
||||
|
||||
if ($upload_info->get_started_timestamp() == null) {
|
||||
$transfer_log->started = __('N/A', 'duplicator-pro');
|
||||
} else {
|
||||
$transfer_log->started = DUP_PRO_DATE::getLocalTimeFromGMTTicks($upload_info->get_started_timestamp());
|
||||
}
|
||||
|
||||
if ($upload_info->get_stopped_timestamp() == null) {
|
||||
$transfer_log->stopped = __('N/A', 'duplicator-pro');
|
||||
} else {
|
||||
$transfer_log->stopped = DUP_PRO_DATE::getLocalTimeFromGMTTicks($upload_info->get_stopped_timestamp());
|
||||
}
|
||||
|
||||
$transfer_log->status_text = $status_text;
|
||||
$transfer_log->message = $upload_info->get_status_message();
|
||||
|
||||
$transfer_log->storage_type_text = __('Unknown', 'duplicator-pro');
|
||||
foreach ($storages as $storage) {
|
||||
if ($storage->getId() == $upload_info->getStorageId()) {
|
||||
$transfer_log->storage_type_text = $storage->getStypeName();
|
||||
// break;
|
||||
}
|
||||
}
|
||||
|
||||
array_unshift($vm->transfer_logs, $transfer_log);
|
||||
|
||||
if ($status == DUP_PRO_Upload_Status::Running) {
|
||||
if ($active_upload_info != null) {
|
||||
DUP_PRO_Log::trace("More than one upload info is running at the same time for package {$package->ID}");
|
||||
}
|
||||
|
||||
$active_upload_info = &$upload_info;
|
||||
}
|
||||
}
|
||||
|
||||
/* -- Now populate the activa package information -- */
|
||||
$active_package = DUP_PRO_Package::get_next_active_package();
|
||||
|
||||
if ($active_package == null) {
|
||||
// No active package
|
||||
$vm->active_package_id = -1;
|
||||
$vm->text = __('No package is building.', 'duplicator-pro');
|
||||
} else {
|
||||
$vm->active_package_id = $active_package->ID;
|
||||
|
||||
if ($active_package->ID == $package_id) {
|
||||
if ($active_upload_info != null) {
|
||||
$vm->percent_text = "{$active_upload_info->progress}%";
|
||||
$vm->text = $active_upload_info->get_status_message();
|
||||
} else {
|
||||
// We see this condition at the beginning and end of the transfer so throw up a generic message
|
||||
$vm->percent_text = "";
|
||||
$vm->text = __("Synchronizing with server...", 'duplicator-pro');
|
||||
}
|
||||
} else {
|
||||
$vm->text = __("Another package is presently running.", 'duplicator-pro');
|
||||
}
|
||||
|
||||
if ($active_package->is_cancel_pending()) {
|
||||
// If it's getting cancelled override the normal text
|
||||
$vm->text = __("Cancellation pending...", 'duplicator-pro');
|
||||
}
|
||||
}
|
||||
|
||||
$json['success'] = true;
|
||||
$json['vm'] = $vm;
|
||||
} catch (Exception $ex) {
|
||||
$json['message'] = $ex->getMessage();
|
||||
DUP_PRO_Log::trace($ex->getMessage());
|
||||
}
|
||||
|
||||
die(SnapJson::jsonEncode($json));
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook ajax manual transfer storage
|
||||
*
|
||||
* @return never
|
||||
*/
|
||||
public function manualTransferStorage()
|
||||
{
|
||||
DUP_PRO_Handler::init_error_handler();
|
||||
check_ajax_referer('duplicator_pro_manual_transfer_storage', 'nonce');
|
||||
|
||||
$json = array(
|
||||
'success' => false,
|
||||
'message' => '',
|
||||
);
|
||||
$isValid = true;
|
||||
$inputData = filter_input_array(INPUT_POST, array(
|
||||
'package_id' => array(
|
||||
'filter' => FILTER_VALIDATE_INT,
|
||||
'flags' => FILTER_REQUIRE_SCALAR,
|
||||
'options' => array('default' => false),
|
||||
),
|
||||
'storage_ids' => array(
|
||||
'filter' => FILTER_VALIDATE_INT,
|
||||
'flags' => FILTER_REQUIRE_ARRAY,
|
||||
'options' => array('default' => false),
|
||||
),
|
||||
));
|
||||
|
||||
$package_id = $inputData['package_id'];
|
||||
$storage_ids = $inputData['storage_ids'];
|
||||
$json['data'] = $inputData;
|
||||
if (!$package_id || !$storage_ids) {
|
||||
$isValid = false;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!CapMng::can(CapMng::CAP_STORAGE, false) && !CapMng::can(CapMng::CAP_CREATE, false)) {
|
||||
throw new Exception('Security issue.');
|
||||
}
|
||||
if (!$isValid) {
|
||||
throw new Exception(__("Invalid request.", 'duplicator-pro'));
|
||||
}
|
||||
|
||||
if (DUP_PRO_Package::isPackageRunning()) {
|
||||
$msg = sprintf(__('Trying to queue a transfer for package %d but a package is already active!', 'duplicator-pro'), $package_id);
|
||||
throw new Exception($msg);
|
||||
}
|
||||
|
||||
$package = DUP_PRO_Package::get_by_id($package_id);
|
||||
DUP_PRO_Log::open($package->NameHash);
|
||||
|
||||
if (!$package) {
|
||||
throw new Exception(sprintf(esc_html__('Could not find package ID %d!', 'duplicator-pro'), $package_id));
|
||||
}
|
||||
|
||||
if (empty($storage_ids)) {
|
||||
throw new Exception("Please select a storage.");
|
||||
}
|
||||
|
||||
$info = "\n";
|
||||
$info .= "********************************************************************************\n";
|
||||
$info .= "********************************************************************************\n";
|
||||
$info .= "PACKAGE MANUAL TRANSFER REQUESTED: " . @date("Y-m-d H:i:s") . "\n";
|
||||
$info .= "********************************************************************************\n";
|
||||
$info .= "********************************************************************************\n\n";
|
||||
DUP_PRO_Log::infoTrace($info);
|
||||
|
||||
foreach ($storage_ids as $storage_id) {
|
||||
if (($storage = AbstractStorageEntity::getById($storage_id)) === false) {
|
||||
throw new Exception(sprintf(__('Could not find storage ID %d!', 'duplicator-pro'), $storage_id));
|
||||
}
|
||||
|
||||
DUP_PRO_Log::infoTrace(
|
||||
'Storage adding to the package "' . $package->Name .
|
||||
' [Package Id: ' . $package_id . ']":: Storage Id: "' . $storage_id .
|
||||
'" Storage Name: "' . esc_html($storage->getName()) .
|
||||
'" Storage Type: "' . esc_html($storage->getStypeName()) . '"'
|
||||
);
|
||||
|
||||
$upload_info = new DUP_PRO_Package_Upload_Info($storage_id);
|
||||
array_push($package->upload_infos, $upload_info);
|
||||
}
|
||||
|
||||
$package->set_status(DUP_PRO_PackageStatus::STORAGE_PROCESSING);
|
||||
$package->timer_start = DUP_PRO_U::getMicrotime();
|
||||
|
||||
$json['success'] = true;
|
||||
|
||||
$package->update();
|
||||
} catch (Exception $ex) {
|
||||
$json['message'] = $ex->getMessage();
|
||||
DUP_PRO_Log::trace($ex->getMessage());
|
||||
}
|
||||
|
||||
DUP_PRO_Log::close();
|
||||
|
||||
die(SnapJson::jsonEncode($json));
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook ajax wp_ajax_duplicator_pro_get_folder_children
|
||||
*
|
||||
* @return never
|
||||
*/
|
||||
public function getFolderChildren()
|
||||
{
|
||||
DUP_PRO_Handler::init_error_handler();
|
||||
check_ajax_referer('duplicator_pro_get_folder_children', 'nonce');
|
||||
|
||||
$json = array();
|
||||
$isValid = true;
|
||||
$inputData = filter_input_array(INPUT_GET, array(
|
||||
'folder' => array(
|
||||
'filter' => FILTER_SANITIZE_SPECIAL_CHARS,
|
||||
'flags' => FILTER_REQUIRE_SCALAR,
|
||||
'options' => array('default' => false),
|
||||
),
|
||||
'exclude' => array(
|
||||
'filter' => FILTER_SANITIZE_SPECIAL_CHARS,
|
||||
'flags' => FILTER_REQUIRE_ARRAY,
|
||||
'options' => array(
|
||||
'default' => array(),
|
||||
),
|
||||
),
|
||||
));
|
||||
$folder = $inputData['folder'];
|
||||
$exclude = $inputData['exclude'];
|
||||
|
||||
if ($folder === false) {
|
||||
$isValid = false;
|
||||
}
|
||||
|
||||
ob_start();
|
||||
try {
|
||||
CapMng::can(CapMng::CAP_BASIC);
|
||||
|
||||
if (!$isValid) {
|
||||
throw new Exception(__('Invalid request.', 'duplicator-pro'));
|
||||
}
|
||||
if (is_dir($folder)) {
|
||||
try {
|
||||
$Package = DUP_PRO_Package::get_temporary_package();
|
||||
} catch (Exception $e) {
|
||||
$Package = null;
|
||||
}
|
||||
|
||||
$treeObj = new DUP_PRO_Tree_files($folder, true, $exclude);
|
||||
$treeObj->uasort(array('DUP_PRO_Archive', 'sortTreeByFolderWarningName'));
|
||||
if (!is_null($Package)) {
|
||||
$treeObj->treeTraverseCallback(array($Package->Archive, 'checkTreeNodesFolder'));
|
||||
}
|
||||
|
||||
$jsTreeData = DUP_PRO_Archive::getJsTreeStructure($treeObj, '', false);
|
||||
$json = $jsTreeData['children'];
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
DUP_PRO_Log::trace($e->getMessage());
|
||||
$json['message'] = $e->getMessage();
|
||||
}
|
||||
ob_clean();
|
||||
wp_send_json($json);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,540 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Ajax;
|
||||
|
||||
use DUP_PRO_Archive;
|
||||
use DUP_PRO_Log;
|
||||
use DUP_PRO_Package;
|
||||
use DUP_PRO_Package_File_Type;
|
||||
use Duplicator\Ajax\AbstractAjaxService;
|
||||
use Duplicator\Ajax\AjaxWrapper;
|
||||
use Duplicator\Controllers\RecoveryController;
|
||||
use Duplicator\Controllers\SettingsPageController;
|
||||
use Duplicator\Core\Controllers\ControllersManager;
|
||||
use Duplicator\Controllers\ToolsPageController;
|
||||
use Duplicator\Core\CapMng;
|
||||
use Duplicator\Core\Views\TplMng;
|
||||
use Duplicator\Libs\Snap\SnapJson;
|
||||
use Duplicator\Libs\Snap\SnapUtil;
|
||||
use Duplicator\Package\Recovery\BackupPackage;
|
||||
use Duplicator\Package\Recovery\RecoveryPackage;
|
||||
use Exception;
|
||||
|
||||
class ServicesRecovery extends AbstractAjaxService
|
||||
{
|
||||
/**
|
||||
* Init ajax calls
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->addAjaxCall('wp_ajax_duplicator_pro_get_recovery_widget', 'getWidget');
|
||||
$this->addAjaxCall('wp_ajax_duplicator_pro_set_recovery', 'setRecovery');
|
||||
$this->addAjaxCall('wp_ajax_duplicator_pro_reset_recovery', 'resetRecovery');
|
||||
$this->addAjaxCall('wp_ajax_duplicator_pro_backup_redirect', 'restoreBackupRedirect');
|
||||
$this->addAjaxCall('wp_ajax_duplicator_pro_disaster_launcher_download', 'launcherDownload');
|
||||
$this->addAjaxCall('wp_ajax_duplicator_pro_get_recovery_box_content', 'recoveryBoxContent');
|
||||
$this->addAjaxCall('wp_ajax_duplicator_pro_restore_backup_prepare', 'restoreBackupPrepare');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get recovery widget detail elements
|
||||
*
|
||||
* @param string $fromPageTab from page/tab unique id
|
||||
*
|
||||
* @return bool[]
|
||||
*/
|
||||
protected static function getRecoveryDetailsOptions($fromPageTab)
|
||||
{
|
||||
if ($fromPageTab == ControllersManager::getPageUniqueId(ControllersManager::TOOLS_SUBMENU_SLUG, ToolsPageController::L2_SLUG_RECOVERY)) {
|
||||
$detailsOptions = array(
|
||||
'selector' => true,
|
||||
'copyLink' => true,
|
||||
'copyButton' => true,
|
||||
'launch' => true,
|
||||
'download' => true,
|
||||
'info' => true,
|
||||
);
|
||||
} elseif ($fromPageTab == ControllersManager::getPageUniqueId(ControllersManager::IMPORT_SUBMENU_SLUG)) {
|
||||
$detailsOptions = array(
|
||||
'selector' => true,
|
||||
'launch' => false,
|
||||
'download' => true,
|
||||
'copyLink' => true,
|
||||
'copyButton' => true,
|
||||
'info' => true,
|
||||
);
|
||||
} else {
|
||||
$detailsOptions = array();
|
||||
}
|
||||
|
||||
return $detailsOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set recovery callback
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public static function setRecoveryCallback()
|
||||
{
|
||||
$recPackageId = SnapUtil::sanitizeIntInput(SnapUtil::INPUT_REQUEST, 'recovery_package', -1);
|
||||
DUP_PRO_Log::trace("SET RECOVERY PACKAGE ID {$recPackageId}");
|
||||
if ($recPackageId !== RecoveryPackage::getRecoverPackageId()) {
|
||||
DUP_PRO_Log::trace("RESET OLD RECORY PACKAGE ID " . RecoveryPackage::getRecoverPackageId());
|
||||
RecoveryPackage::removeRecoveryFolder();
|
||||
|
||||
$errorMessage = '';
|
||||
if (!RecoveryPackage::setRecoveablePackage($recPackageId, $errorMessage)) {
|
||||
$urlImport = ControllersManager::getMenuLink(ControllersManager::SETTINGS_SUBMENU_SLUG, SettingsPageController::L2_SLUG_IMPORT);
|
||||
|
||||
$msg = sprintf(__("Error: <b>%s</b>", 'duplicator-pro'), $errorMessage) . '<br><br>';
|
||||
$msg .= __("The old Recovery Point was removed but this package can’t be set as the Recovery Point.", 'duplicator-pro') . '<br>';
|
||||
$msg .= __("Possible solutions:", 'duplicator-pro') . '<br>';
|
||||
$msg .= sprintf(
|
||||
_x(
|
||||
'- In some hosting the execution of PHP scripts are blocked in the wp-content folder, %1$s[try set a custom recovery path]%2$s',
|
||||
'%1$s and %2$s represents the opening and closing HTML tags for an anchor or link',
|
||||
'duplicator-pro'
|
||||
),
|
||||
'<a href="' . esc_url($urlImport) . '" target="_blank">',
|
||||
'</a>'
|
||||
) . '<br>';
|
||||
$msg .= __(
|
||||
"- you may still be able to to download the package manually and perform an import or a classic backup installation.
|
||||
If you wish to install the package on the site where it was create the restore backup mode should be activated.",
|
||||
'duplicator-pro'
|
||||
);
|
||||
throw new Exception($msg);
|
||||
}
|
||||
DUP_PRO_Log::trace("RECOVER PACKAGE SET");
|
||||
}
|
||||
|
||||
$recoverPackage = RecoveryPackage::getRecoverPackage();
|
||||
DUP_PRO_Log::trace("RECOVER PACKAGE READED");
|
||||
if (!$recoverPackage instanceof RecoveryPackage) {
|
||||
throw new Exception(esc_html__('Can\'t get recover package', 'duplicator-pro'));
|
||||
}
|
||||
$fromPageTab = SnapUtil::sanitizeDefaultInput(INPUT_POST, 'fromPageTab', false);
|
||||
$detailsOptions = self::getRecoveryDetailsOptions($fromPageTab);
|
||||
DUP_PRO_Log::trace("RECOVER PACKAGE DETAILS OPTIONS READED");
|
||||
|
||||
$subtitle = __('Copy the Link and keep it in case of need or download Disaster Recovery Launcher.', 'duplicator-pro');
|
||||
|
||||
$result = array(
|
||||
'id' => $recoverPackage->getPackageId(),
|
||||
'name' => $recoverPackage->getPackageName(),
|
||||
'recoveryLink' => $recoverPackage->getInstallLink(),
|
||||
'adminMessage' => RecoveryController::renderRecoveryWidged(array(
|
||||
'selector' => false,
|
||||
'subtitle' => $subtitle,
|
||||
'copyLink' => false,
|
||||
'copyButton' => true,
|
||||
'launch' => false,
|
||||
'download' => true,
|
||||
'info' => true,
|
||||
), false),
|
||||
'packageDetails' => RecoveryController::renderRecoveryWidged($detailsOptions, false),
|
||||
);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set recovery action
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setRecovery()
|
||||
{
|
||||
AjaxWrapper::json(
|
||||
array(
|
||||
__CLASS__,
|
||||
'setRecoveryCallback',
|
||||
),
|
||||
'duplicator_pro_set_recovery',
|
||||
SnapUtil::sanitizeTextInput(INPUT_POST, 'nonce', ''),
|
||||
CapMng::CAP_BACKUP_RESTORE
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get widget callback
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public static function getWidgetCallback()
|
||||
{
|
||||
$fromPageTab = SnapUtil::sanitizeDefaultInput(INPUT_POST, 'fromPageTab', false);
|
||||
$detailsOptions = self::getRecoveryDetailsOptions($fromPageTab);
|
||||
|
||||
return array(
|
||||
'widget' => RecoveryController::renderRecoveryWidged($detailsOptions, false),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get widget action
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getWidget()
|
||||
{
|
||||
AjaxWrapper::json(
|
||||
array(
|
||||
__CLASS__,
|
||||
'getWidgetCallback',
|
||||
),
|
||||
'duplicator_pro_get_recovery_widget',
|
||||
SnapUtil::sanitizeTextInput(INPUT_POST, 'nonce', ''),
|
||||
CapMng::CAP_BACKUP_RESTORE
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset recovery callback
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public static function resetRecoveryCallback()
|
||||
{
|
||||
if (RecoveryController::actionResetRecoveryPoint() === false) {
|
||||
throw new Exception(RecoveryController::getErrorMessage());
|
||||
}
|
||||
|
||||
$fromPageTab = SnapUtil::sanitizeDefaultInput(INPUT_POST, 'fromPageTab', false);
|
||||
$detailsOptions = self::getRecoveryDetailsOptions($fromPageTab);
|
||||
|
||||
$result = array(
|
||||
'adminMessage' => RecoveryController::renderRecoveryWidged(array(), false),
|
||||
'packageDetails' => RecoveryController::renderRecoveryWidged($detailsOptions, false),
|
||||
);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset recovery action
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function resetRecovery()
|
||||
{
|
||||
AjaxWrapper::json(
|
||||
array(
|
||||
__CLASS__,
|
||||
'resetRecoveryCallback',
|
||||
),
|
||||
'duplicator_pro_reset_recovery',
|
||||
SnapUtil::sanitizeTextInput(INPUT_POST, 'nonce', ''),
|
||||
CapMng::CAP_BACKUP_RESTORE
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare restore backup and redirect to the installer URL
|
||||
*
|
||||
* @return array<string,scalar>
|
||||
*/
|
||||
public static function restoreBackupRedirectCallback()
|
||||
{
|
||||
$result = array(
|
||||
'success' => false,
|
||||
'message' => '',
|
||||
'redirect_url' => '',
|
||||
);
|
||||
|
||||
try {
|
||||
$packageId = SnapUtil::sanitizeIntInput(SnapUtil::INPUT_REQUEST, 'packageId', 0);
|
||||
|
||||
if (($package = DUP_PRO_Package::get_by_id($packageId)) === false) {
|
||||
throw new Exception(__('Backup is invalid', 'duplicator-pro'));
|
||||
}
|
||||
|
||||
if (!$package->haveLocalStorage()) {
|
||||
throw new Exception(__('Backup isn\'t local', 'duplicator-pro'));
|
||||
}
|
||||
|
||||
$arachivePath = $package->getLocalPackageFilePath(DUP_PRO_Package_File_Type::Archive);
|
||||
if (!file_exists($arachivePath)) {
|
||||
throw new Exception(__('Backup archive file doesn\'t exist', 'duplicator-pro'));
|
||||
}
|
||||
|
||||
$restore = new BackupPackage($arachivePath, $package);
|
||||
|
||||
$result['redirect_url'] = $restore->prepareToInstall();
|
||||
$result['success'] = true;
|
||||
} catch (Exception $ex) {
|
||||
$result['success'] = false;
|
||||
$result['message'] = $ex->getMessage();
|
||||
DUP_PRO_Log::traceError($ex->getMessage());
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset recovery action
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function restoreBackupRedirect()
|
||||
{
|
||||
AjaxWrapper::json(
|
||||
array(
|
||||
__CLASS__,
|
||||
'restoreBackupRedirectCallback',
|
||||
),
|
||||
'duplicator_pro_backup_redirect',
|
||||
SnapUtil::sanitizeTextInput(SnapUtil::INPUT_REQUEST, 'nonce', ''),
|
||||
CapMng::CAP_BACKUP_RESTORE
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Launcher download callback
|
||||
*
|
||||
* @return array<string,scalar>
|
||||
*/
|
||||
public static function launcherDownloadCallback()
|
||||
{
|
||||
$result = array(
|
||||
'success' => false,
|
||||
'message' => '',
|
||||
'fileContent' => '',
|
||||
'fileName' => '',
|
||||
);
|
||||
|
||||
try {
|
||||
if (($recoverPackage = RecoveryPackage::getRecoverPackage()) == false) {
|
||||
throw new Exception(__('Can\'t get recover package', 'duplicator-pro'));
|
||||
}
|
||||
|
||||
$result['fileContent'] = TplMng::getInstance()->render(
|
||||
'parts/recovery/launcher_content',
|
||||
array('recoverPackage' => $recoverPackage),
|
||||
false
|
||||
);
|
||||
|
||||
$result['fileName'] = $recoverPackage->getLauncherFileName();
|
||||
$result['success'] = true;
|
||||
} catch (Exception $ex) {
|
||||
$result['success'] = false;
|
||||
$result['message'] = $ex->getMessage();
|
||||
DUP_PRO_Log::traceError($ex->getMessage());
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset recovery action
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function launcherDownload()
|
||||
{
|
||||
AjaxWrapper::json(
|
||||
array(
|
||||
__CLASS__,
|
||||
'launcherDownloadCallback',
|
||||
),
|
||||
'duplicator_pro_disaster_launcher_download',
|
||||
SnapUtil::sanitizeTextInput(SnapUtil::INPUT_REQUEST, 'nonce', ''),
|
||||
CapMng::CAP_BACKUP_RESTORE
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prepare restore backup and redirect to the installer URL
|
||||
*
|
||||
* @return array<string,scalar>
|
||||
*/
|
||||
public static function recoveryBoxContentCallback()
|
||||
{
|
||||
$result = array(
|
||||
'success' => false,
|
||||
'message' => '',
|
||||
'content' => '',
|
||||
'isRecoveable' => false,
|
||||
);
|
||||
|
||||
try {
|
||||
$packageId = SnapUtil::sanitizeIntInput(SnapUtil::INPUT_REQUEST, 'packageId', 0);
|
||||
|
||||
if (($package = DUP_PRO_Package::get_by_id($packageId)) === false) {
|
||||
throw new Exception(__('Backup is invalid', 'duplicator-pro'));
|
||||
}
|
||||
|
||||
$result['content'] = TplMng::getInstance()->render(
|
||||
'admin_pages/packages/recovery_info/row_recovery_box',
|
||||
['package' => $package],
|
||||
false
|
||||
);
|
||||
$result['isRecoveable'] = RecoveryPackage::isPackageIdRecoveable($package->ID);
|
||||
$result['success'] = true;
|
||||
} catch (Exception $ex) {
|
||||
$result['success'] = false;
|
||||
$result['message'] = $ex->getMessage();
|
||||
DUP_PRO_Log::traceError($ex->getMessage());
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset recovery action
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function recoveryBoxContent()
|
||||
{
|
||||
AjaxWrapper::json(
|
||||
array(
|
||||
__CLASS__,
|
||||
'recoveryBoxContentCallback',
|
||||
),
|
||||
'duplicator_pro_get_recovery_box_content',
|
||||
SnapUtil::sanitizeTextInput(SnapUtil::INPUT_REQUEST, 'nonce', ''),
|
||||
CapMng::CAP_BACKUP_RESTORE
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore backup prepare callback
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function restoreBackupPrepareCallback()
|
||||
{
|
||||
$packageId = filter_input(INPUT_POST, 'packageId', FILTER_VALIDATE_INT);
|
||||
if (!$packageId) {
|
||||
throw new Exception('Invalid package ID in request.');
|
||||
}
|
||||
$result = array();
|
||||
|
||||
if (($package = DUP_PRO_Package::get_by_id($packageId)) === false) {
|
||||
throw new Exception(esc_html__('Invalid package ID', 'duplicator-pro'));
|
||||
}
|
||||
$updDirs = wp_upload_dir();
|
||||
|
||||
$result = DUPLICATOR_PRO_SSDIR_URL . '/' . $package->Installer->getInstallerName() . '?dup_folder=dupinst_' . $package->Hash;
|
||||
|
||||
$installerParams = array(
|
||||
'inst_mode' => array('value' => 2 ), // mode restore backup
|
||||
'url_old' => array('formStatus' => "st_skip"),
|
||||
'url_new' => array(
|
||||
'value' => DUP_PRO_Archive::getOriginalUrls('home'),
|
||||
'formStatus' => "st_infoonly",
|
||||
),
|
||||
'path_old' => array('formStatus' => "st_skip"),
|
||||
'path_new' => array(
|
||||
'value' => duplicator_pro_get_home_path(),
|
||||
'formStatus' => "st_infoonly",
|
||||
),
|
||||
'dbaction' => array(
|
||||
'value' => 'empty',
|
||||
'formStatus' => "st_infoonly",
|
||||
),
|
||||
'dbhost' => array(
|
||||
'value' => DB_HOST,
|
||||
'formStatus' => "st_infoonly",
|
||||
),
|
||||
'dbname' => array(
|
||||
'value' => DB_NAME,
|
||||
'formStatus' => "st_infoonly",
|
||||
),
|
||||
'dbuser' => array(
|
||||
'value' => DB_USER,
|
||||
'formStatus' => "st_infoonly",
|
||||
),
|
||||
'dbpass' => array(
|
||||
'value' => DB_PASSWORD,
|
||||
'formStatus' => "st_infoonly",
|
||||
),
|
||||
'dbtest_ok' => array('value' => true),
|
||||
'siteurl_old' => array('formStatus' => "st_skip"),
|
||||
'siteurl' => array(
|
||||
'value' => 'site_url',
|
||||
'formStatus' => "st_skip",
|
||||
),
|
||||
'path_cont_old' => array('formStatus' => "st_skip"),
|
||||
'path_cont_new' => array(
|
||||
'value' => WP_CONTENT_DIR,
|
||||
'formStatus' => "st_skip",
|
||||
),
|
||||
'path_upl_old' => array('formStatus' => "st_skip"),
|
||||
'path_upl_new' => array(
|
||||
'value' => $updDirs['basedir'],
|
||||
'formStatus' => "st_skip",
|
||||
),
|
||||
'url_cont_old' => array('formStatus' => "st_skip"),
|
||||
'url_cont_new' => array(
|
||||
'value' => content_url(),
|
||||
'formStatus' => "st_skip",
|
||||
),
|
||||
'url_upl_old' => array('formStatus' => "st_skip"),
|
||||
'url_upl_new' => array(
|
||||
'value' => $updDirs['baseurl'],
|
||||
'formStatus' => "st_skip",
|
||||
),
|
||||
'exe_safe_mode' => array('formStatus' => "st_skip"),
|
||||
'remove-redundant' => array('formStatus' => "st_skip"),
|
||||
'blogname' => array('formStatus' => "st_infoonly"),
|
||||
'replace_mode' => array('formStatus' => "st_skip"),
|
||||
'empty_schedule_storage' => array(
|
||||
'value' => false,
|
||||
'formStatus' => "st_skip",
|
||||
),
|
||||
'wp_config' => array(
|
||||
'value' => 'original',
|
||||
'formStatus' => "st_infoonly",
|
||||
),
|
||||
'ht_config' => array(
|
||||
'value' => 'original',
|
||||
'formStatus' => "st_infoonly",
|
||||
),
|
||||
'other_config' => array(
|
||||
'value' => 'original',
|
||||
'formStatus' => "st_infoonly",
|
||||
),
|
||||
'zip_filetime' => array(
|
||||
'value' => 'original',
|
||||
'formStatus' => "st_infoonly",
|
||||
),
|
||||
'mode_chunking' => array(
|
||||
'value' => 3,
|
||||
'formStatus' => "st_infoonly",
|
||||
),
|
||||
);
|
||||
$localParamsFile = DUPLICATOR_PRO_SSDIR_PATH . '/' . DUPLICATOR_PRO_LOCAL_OVERWRITE_PARAMS . '_' . $package->get_package_hash() . '.json';
|
||||
file_put_contents($localParamsFile, SnapJson::jsonEncodePPrint($installerParams));
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook ajax restore backup prepare
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function restoreBackupPrepare()
|
||||
{
|
||||
AjaxWrapper::json(
|
||||
array(
|
||||
__CLASS__,
|
||||
'restoreBackupPrepareCallback',
|
||||
),
|
||||
'duplicator_pro_restore_backup_prepare',
|
||||
$_POST['nonce'],
|
||||
CapMng::CAP_BACKUP_RESTORE
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Ajax;
|
||||
|
||||
use DUP_PRO_Handler;
|
||||
use DUP_PRO_Log;
|
||||
use DUP_PRO_Package;
|
||||
use DUP_PRO_Package_Runner;
|
||||
use DUP_PRO_Schedule_Entity;
|
||||
use DUP_PRO_U;
|
||||
use Duplicator\Core\CapMng;
|
||||
use Error;
|
||||
use Exception;
|
||||
use stdClass;
|
||||
|
||||
class ServicesSchedule extends AbstractAjaxService
|
||||
{
|
||||
const SCHEDULE_BULK_DELETE = 1;
|
||||
const SCHEDULE_BULK_ACTIVATE = 2;
|
||||
const SCHEDULE_BULK_DEACTIVATE = 3;
|
||||
|
||||
/**
|
||||
* Init ajax calls
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->addAjaxCall('wp_ajax_duplicator_pro_schedule_bulk_action', 'bulkAction');
|
||||
$this->addAjaxCall('wp_ajax_duplicator_pro_get_schedule_infos', 'getScheduleInfo');
|
||||
$this->addAjaxCall('wp_ajax_duplicator_pro_run_schedule_now', 'runScheduleNow');
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule bulk actions
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function bulkAction()
|
||||
{
|
||||
DUP_PRO_Handler::init_error_handler();
|
||||
check_ajax_referer('duplicator_pro_schedule_bulk_action', 'nonce');
|
||||
|
||||
$isValid = true;
|
||||
$json = array(
|
||||
'success' => true,
|
||||
'message' => '',
|
||||
);
|
||||
$inputData = filter_input_array(INPUT_POST, array(
|
||||
'schedule_ids' => array(
|
||||
'filter' => FILTER_VALIDATE_INT,
|
||||
'flags' => FILTER_REQUIRE_ARRAY,
|
||||
'options' => array('default' => false),
|
||||
),
|
||||
'perform' => array(
|
||||
'filter' => FILTER_VALIDATE_INT,
|
||||
'flags' => FILTER_REQUIRE_SCALAR,
|
||||
'options' => array('default' => false),
|
||||
),
|
||||
));
|
||||
$scheduleIDs = $inputData['schedule_ids'];
|
||||
$action = $inputData['perform'];
|
||||
|
||||
if (empty($scheduleIDs) || in_array(false, $scheduleIDs) || $action === false) {
|
||||
$isValid = false;
|
||||
}
|
||||
|
||||
try {
|
||||
CapMng::can(CapMng::CAP_SCHEDULE);
|
||||
|
||||
if (!$isValid) {
|
||||
throw new Exception(__("Invalid Request.", 'duplicator-pro'));
|
||||
}
|
||||
|
||||
foreach ($scheduleIDs as $id) {
|
||||
switch ($action) {
|
||||
case self::SCHEDULE_BULK_DELETE:
|
||||
DUP_PRO_Schedule_Entity::deleteById($id);
|
||||
break;
|
||||
case self::SCHEDULE_BULK_ACTIVATE:
|
||||
$schedule = DUP_PRO_Schedule_Entity::getById($id);
|
||||
if (count($schedule->storage_ids) === 0) {
|
||||
$json['success'] = false;
|
||||
$json['message'] .= "Could not activate schedule with ID " . $schedule->getId() .
|
||||
" because it has no Storages.<br>";
|
||||
} else {
|
||||
$schedule->active = true;
|
||||
$schedule->save();
|
||||
}
|
||||
break;
|
||||
case self::SCHEDULE_BULK_DEACTIVATE:
|
||||
$schedule = DUP_PRO_Schedule_Entity::getById($id);
|
||||
$schedule->active = false;
|
||||
$schedule->save();
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Invalid schedule bulk action.");
|
||||
}
|
||||
}
|
||||
} catch (Exception $ex) {
|
||||
$json['success'] = false;
|
||||
$json['message'] = $ex->getMessage();
|
||||
}
|
||||
|
||||
die(json_encode($json));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get schedule info action
|
||||
*
|
||||
* { schedule_id, is_running=true|false, last_ran_string}
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getScheduleInfo()
|
||||
{
|
||||
DUP_PRO_Handler::init_error_handler();
|
||||
check_ajax_referer('duplicator_pro_get_schedule_infos', 'nonce');
|
||||
CapMng::can(CapMng::CAP_SCHEDULE);
|
||||
$schedules = DUP_PRO_Schedule_Entity::getAll();
|
||||
$schedule_infos = array();
|
||||
|
||||
if (count($schedules) > 0) {
|
||||
$package = DUP_PRO_Package::get_next_active_package();
|
||||
|
||||
foreach ($schedules as $schedule) {
|
||||
$schedule_info = new stdClass();
|
||||
|
||||
$schedule_info->schedule_id = $schedule->getId();
|
||||
$schedule_info->last_ran_string = $schedule->get_last_ran_string();
|
||||
|
||||
if ($package != null) {
|
||||
$schedule_info->is_running = ($package->schedule_id == $schedule->getId());
|
||||
} else {
|
||||
$schedule_info->is_running = false;
|
||||
}
|
||||
|
||||
array_push($schedule_infos, $schedule_info);
|
||||
}
|
||||
}
|
||||
|
||||
wp_send_json($schedule_infos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run schedule action
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function runScheduleNow()
|
||||
{
|
||||
DUP_PRO_Handler::init_error_handler();
|
||||
check_ajax_referer('duplicator_pro_run_schedule_now', 'nonce');
|
||||
|
||||
$json = array(
|
||||
'success' => false,
|
||||
'message' => '',
|
||||
);
|
||||
|
||||
try {
|
||||
CapMng::can(CapMng::CAP_SCHEDULE);
|
||||
$schedule_id = filter_input(INPUT_POST, 'schedule_id', FILTER_VALIDATE_INT);
|
||||
|
||||
if ($schedule_id === false) {
|
||||
throw new Exception(__("Invalid schedule id", 'duplicator-pro'));
|
||||
}
|
||||
|
||||
$schedule = DUP_PRO_Schedule_Entity::getById($schedule_id);
|
||||
|
||||
if ($schedule == false) {
|
||||
DUP_PRO_Log::trace("Attempted to queue up a job for non existent schedule $schedule_id");
|
||||
throw new Exception(__("Invalid schedule id", 'duplicator-pro'));
|
||||
}
|
||||
|
||||
DUP_PRO_Log::trace("Inserting new package for schedule $schedule->name due to manual request");
|
||||
// Just inserting it is enough since init() will automatically pick it up and schedule a cron in the near future.
|
||||
$schedule->insert_new_package(true);
|
||||
DUP_PRO_Package_Runner::kick_off_worker();
|
||||
|
||||
$json = array(
|
||||
'success' => true,
|
||||
'message' => '',
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
$json['success'] = false;
|
||||
$json['message'] = $e->getMessage();
|
||||
} catch (Error $e) {
|
||||
$json['success'] = false;
|
||||
$json['message'] = $e->getMessage();
|
||||
}
|
||||
|
||||
die(json_encode($json));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,485 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Ajax;
|
||||
|
||||
use DUP_PRO_Constants;
|
||||
use DUP_PRO_Handler;
|
||||
use DUP_PRO_Log;
|
||||
use DUP_PRO_U;
|
||||
use DUP_PRO_Global_Entity;
|
||||
use DUP_PRO_Secure_Global_Entity;
|
||||
use Duplicator\Addons\ProBase\License\License;
|
||||
use Duplicator\Core\CapMng;
|
||||
use Duplicator\Core\MigrationMng;
|
||||
use Duplicator\Libs\Snap\SnapIO;
|
||||
use Duplicator\Libs\Snap\SnapJson;
|
||||
use Duplicator\Libs\Snap\SnapURL;
|
||||
use Duplicator\Libs\Snap\SnapUtil;
|
||||
use Duplicator\Models\SystemGlobalEntity;
|
||||
use Duplicator\Utils\Settings\MigrateSettings;
|
||||
use Duplicator\Utils\ZipArchiveExtended;
|
||||
use Exception;
|
||||
|
||||
class ServicesSettings extends AbstractAjaxService
|
||||
{
|
||||
const USERS_PAGE_SIZE = 10;
|
||||
/**
|
||||
* Init ajax calls
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->addAjaxCall("wp_ajax_duplicator_settings_cap_users_list", "capUsersList");
|
||||
$this->addAjaxCall('wp_ajax_duplicator_pro_get_trace_log', 'getTraceLog');
|
||||
$this->addAjaxCall('wp_ajax_duplicator_pro_delete_trace_log', 'deleteTraceLog');
|
||||
$this->addAjaxCall('wp_ajax_duplicator_pro_export_settings', 'exportSettings');
|
||||
$this->addAjaxCall('wp_ajax_duplicator_pro_quick_fix', 'quickFix');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return user list for capabilites select
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
public static function capUsersListCallback()
|
||||
{
|
||||
$searchStr = SnapUtil::sanitizeNSChars($_POST['search']);
|
||||
$page = SnapUtil::sanitizeIntInput(INPUT_POST, 'page', 1);
|
||||
|
||||
$result = [
|
||||
'results' => [],
|
||||
'pagination' => ['more' => false],
|
||||
];
|
||||
|
||||
if ($page == 1) {
|
||||
foreach (CapMng::getSelectableRoles() as $role => $roleName) {
|
||||
if (stripos($role, $searchStr) !== false) {
|
||||
$result['results'][] = [
|
||||
'id' => $role,
|
||||
'text' => $roleName,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (License::can(License::CAPABILITY_CAPABILITIES_MNG_PLUS)) {
|
||||
$args = array(
|
||||
'search' => '*' . SnapUtil::sanitizeNSChars($_POST['search']) . '*',
|
||||
'search_columns' => array(
|
||||
'user_login',
|
||||
'user_email',
|
||||
),
|
||||
'number' => self::USERS_PAGE_SIZE,
|
||||
'paged' => $page,
|
||||
);
|
||||
|
||||
$users = get_users($args);
|
||||
foreach ($users as $user) {
|
||||
$result['results'][] = [
|
||||
'id' => $user->ID,
|
||||
'text' => $user->user_email,
|
||||
];
|
||||
}
|
||||
$args = array(
|
||||
'search' => '*' . SnapUtil::sanitizeNSChars($_POST['search']) . '*',
|
||||
'search_columns' => array(
|
||||
'user_login',
|
||||
'user_email',
|
||||
),
|
||||
'number' => self::USERS_PAGE_SIZE,
|
||||
'paged' => $page + 1,
|
||||
);
|
||||
$users = get_users($args);
|
||||
|
||||
$result['pagination']['more'] = count($users) > 0;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Import upload action
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function capUsersList()
|
||||
{
|
||||
AjaxWrapper::json(
|
||||
array(
|
||||
__CLASS__,
|
||||
'capUsersListCallback',
|
||||
),
|
||||
'duplicator_settings_cap_users_list',
|
||||
$_POST['nonce'],
|
||||
CapMng::CAP_SETTINGS
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook ajax wp_ajax_duplicator_pro_get_trace_log
|
||||
*
|
||||
* @return never
|
||||
*/
|
||||
public function getTraceLog()
|
||||
{
|
||||
/**
|
||||
* don't init DUP_PRO_Handler::init_error_handler() in get trace
|
||||
*/
|
||||
check_ajax_referer('duplicator_pro_get_trace_log', 'nonce');
|
||||
DUP_PRO_Log::trace("enter");
|
||||
|
||||
$file_path = DUP_PRO_Log::getTraceFilepath();
|
||||
$backup_path = DUP_PRO_Log::getBackupTraceFilepath();
|
||||
$zip_path = DUPLICATOR_PRO_SSDIR_PATH . "/" . DUP_PRO_Constants::ZIPPED_LOG_FILENAME;
|
||||
|
||||
try {
|
||||
CapMng::can(CapMng::CAP_CREATE);
|
||||
|
||||
if (file_exists($zip_path)) {
|
||||
SnapIO::unlink($zip_path);
|
||||
}
|
||||
$zipArchive = new ZipArchiveExtended($zip_path);
|
||||
|
||||
if ($zipArchive->open() == false) {
|
||||
throw new Exception('Can\'t open ZIP archive');
|
||||
}
|
||||
|
||||
if ($zipArchive->addFile($file_path, basename($file_path)) == false) {
|
||||
throw new Exception('Can\'t add ZIP file ');
|
||||
}
|
||||
|
||||
if (file_exists($backup_path) && $zipArchive->addFile($backup_path, basename($backup_path)) == false) {
|
||||
throw new Exception('Can\'t add ZIP file ');
|
||||
}
|
||||
|
||||
$zipArchive->close();
|
||||
|
||||
if (($fp = fopen($zip_path, 'rb')) === false) {
|
||||
throw new Exception('Can\'t open ZIP archive');
|
||||
}
|
||||
|
||||
$zip_filename = basename($zip_path);
|
||||
|
||||
header("Pragma: public");
|
||||
header("Expires: 0");
|
||||
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
|
||||
header("Cache-Control: private", false);
|
||||
header("Content-Transfer-Encoding: binary");
|
||||
header("Content-Type: application/octet-stream");
|
||||
header("Content-Disposition: attachment; filename=\"$zip_filename\";");
|
||||
|
||||
// required or large files wont work
|
||||
if (ob_get_length()) {
|
||||
ob_end_clean();
|
||||
}
|
||||
|
||||
DUP_PRO_Log::trace("streaming $zip_path");
|
||||
fpassthru($fp);
|
||||
fclose($fp);
|
||||
@unlink($zip_path);
|
||||
} catch (Exception $e) {
|
||||
header("Content-Type: text/plain");
|
||||
header("Content-Disposition: attachment; filename=\"error.txt\";");
|
||||
$message = 'Create Log Zip error message: ' . $e->getMessage();
|
||||
DUP_PRO_Log::trace($message);
|
||||
echo esc_html($message);
|
||||
}
|
||||
die();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook ajax wp_ajax_duplicator_pro_delete_trace_log
|
||||
*
|
||||
* @return never
|
||||
*/
|
||||
public function deleteTraceLog()
|
||||
{
|
||||
/**
|
||||
* don't init DUP_PRO_Handler::init_error_handler() in get trace
|
||||
*/
|
||||
check_ajax_referer('duplicator_pro_delete_trace_log', 'nonce');
|
||||
CapMng::can(CapMng::CAP_CREATE);
|
||||
|
||||
$res = DUP_PRO_Log::deleteTraceLog();
|
||||
if ($res) {
|
||||
wp_send_json_success();
|
||||
} else {
|
||||
wp_send_json_error();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook ajax wp_ajax_duplicator_pro_export_settings
|
||||
*
|
||||
* @return never
|
||||
*/
|
||||
public function exportSettings()
|
||||
{
|
||||
DUP_PRO_Handler::init_error_handler();
|
||||
check_ajax_referer('duplicator_pro_import_export_settings', 'nonce');
|
||||
|
||||
try {
|
||||
DUP_PRO_Log::trace("Export settings start");
|
||||
CapMng::can(CapMng::CAP_SETTINGS);
|
||||
|
||||
$message = '';
|
||||
|
||||
if (($filePath = MigrateSettings::export($message)) === false) {
|
||||
throw new Exception($message);
|
||||
}
|
||||
|
||||
DUP_PRO_U::getDownloadAttachment($filePath, 'application/octet-stream');
|
||||
} catch (Exception $ex) {
|
||||
// RSR TODO: set the error message to this $this->message = 'Error processing with export:' . $e->getMessage();
|
||||
header("Content-Type: text/plain");
|
||||
header("Content-Disposition: attachment; filename=\"error.txt\";");
|
||||
$message = $ex->getMessage();
|
||||
DUP_PRO_Log::trace($message);
|
||||
echo esc_html($message);
|
||||
}
|
||||
die();
|
||||
}
|
||||
|
||||
/**
|
||||
* DUPLICATOR_PRO_QUICK_FIX
|
||||
* Set default quick fix values automaticaly to help user
|
||||
*
|
||||
* @return never
|
||||
*/
|
||||
public function quickFix()
|
||||
{
|
||||
DUP_PRO_Handler::init_error_handler();
|
||||
check_ajax_referer('duplicator_pro_quick_fix', 'nonce');
|
||||
|
||||
$json = array(
|
||||
'success' => false,
|
||||
'message' => '',
|
||||
);
|
||||
$isValid = true;
|
||||
$inputData = filter_input_array(INPUT_POST, array(
|
||||
'id' => array(
|
||||
'filter' => FILTER_SANITIZE_SPECIAL_CHARS,
|
||||
'flags' => FILTER_REQUIRE_SCALAR,
|
||||
'options' => array('default' => false),
|
||||
),
|
||||
'setup' => array(
|
||||
'filter' => FILTER_SANITIZE_SPECIAL_CHARS,
|
||||
'flags' => FILTER_REQUIRE_ARRAY,
|
||||
'options' => array('default' => false),
|
||||
),
|
||||
));
|
||||
$setup = $inputData['setup'];
|
||||
$id = $inputData['id'];
|
||||
|
||||
if (!$id || empty($setup)) {
|
||||
$isValid = false;
|
||||
}
|
||||
//END OF VALIDATION
|
||||
|
||||
try {
|
||||
CapMng::can(CapMng::CAP_BASIC);
|
||||
if (!$isValid) {
|
||||
throw new Exception(__("Invalid request.", 'duplicator-pro'));
|
||||
}
|
||||
|
||||
$data = array();
|
||||
$isSpecial = isset($setup['special']) && is_array($setup['special']) && count($setup['special']) > 0;
|
||||
|
||||
/* ****************
|
||||
* GENERAL SETUP
|
||||
* **************** */
|
||||
if (isset($setup['global']) && is_array($setup['global'])) {
|
||||
$global = DUP_PRO_Global_Entity::getInstance();
|
||||
|
||||
foreach ($setup['global'] as $object => $value) {
|
||||
$value = DUP_PRO_U::valType($value);
|
||||
if (isset($global->$object)) {
|
||||
// Get current setup
|
||||
$current = $global->$object;
|
||||
|
||||
// If setup is not the same - fix this
|
||||
if ($current !== $value) {
|
||||
// Set new value
|
||||
$global->$object = $value;
|
||||
// Check value
|
||||
$data[$object] = $global->$object;
|
||||
}
|
||||
}
|
||||
}
|
||||
$global->save();
|
||||
}
|
||||
|
||||
/* ****************
|
||||
* SPECIAL SETUP
|
||||
* **************** */
|
||||
if ($isSpecial) {
|
||||
$special = $setup['special'];
|
||||
$stuck5percent = isset($special['stuck_5percent_pending_fix']) && $special['stuck_5percent_pending_fix'] == 1;
|
||||
$basicAuth = isset($special['set_basic_auth']) && $special['set_basic_auth'] == 1;
|
||||
$removeInstallerFiles = isset($special['remove_installer_files']) && $special['remove_installer_files'] == 1;
|
||||
/**
|
||||
* SPECIAL FIX: Package build stuck at 5% or Pending?
|
||||
* */
|
||||
if ($stuck5percent) {
|
||||
$data = array_merge($data, $this->quickFixStuck5Percent());
|
||||
}
|
||||
|
||||
/**
|
||||
* SPECIAL FIX: Set basic auth username & password
|
||||
* */
|
||||
if ($basicAuth) {
|
||||
$data = array_merge($data, $this->quickFixBasicAuth());
|
||||
}
|
||||
|
||||
/**
|
||||
* SPECIAL FIX: Remove installer files
|
||||
* */
|
||||
if ($removeInstallerFiles) {
|
||||
$data = array_merge($data, $this->quickFixRemoveInstallerFiles());
|
||||
}
|
||||
}
|
||||
|
||||
// Save new property
|
||||
$find = count($data);
|
||||
if ($find > 0) {
|
||||
$system_global = SystemGlobalEntity::getInstance();
|
||||
if (strlen($id) > 0) {
|
||||
$system_global->removeFixById($id);
|
||||
$json['id'] = $id;
|
||||
}
|
||||
|
||||
$json['success'] = true;
|
||||
$json['setup'] = $data;
|
||||
$json['fixed'] = $find;
|
||||
$json['recommended_fixes'] = count($system_global->recommended_fixes);
|
||||
}
|
||||
} catch (Exception $ex) {
|
||||
$json['message'] = $ex->getMessage();
|
||||
DUP_PRO_Log::trace("Error while implementing quick fix: " . $ex->getMessage());
|
||||
}
|
||||
|
||||
die(SnapJson::jsonEncode($json));
|
||||
}
|
||||
|
||||
/**
|
||||
* Quick fix for removing installer files
|
||||
*
|
||||
* @return array{removed_installer_files:bool} $data
|
||||
*/
|
||||
private function quickFixRemoveInstallerFiles()
|
||||
{
|
||||
$data = array();
|
||||
$fileRemoved = MigrationMng::cleanMigrationFiles();
|
||||
$removeError = false;
|
||||
if (count($fileRemoved) > 0) {
|
||||
$data['removed_installer_files'] = true;
|
||||
} else {
|
||||
throw new Exception(esc_html__("Unable to remove installer files.", 'duplicator-pro'));
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Quick fix for stuck at 5% or pending
|
||||
*
|
||||
* @return array<string, mixed> $data
|
||||
*/
|
||||
private function quickFixStuck5Percent()
|
||||
{
|
||||
$global = DUP_PRO_Global_Entity::getInstance();
|
||||
|
||||
$data = array();
|
||||
$kickoff = true;
|
||||
$custom = false;
|
||||
|
||||
if ($global->ajax_protocol === 'custom') {
|
||||
$custom = true;
|
||||
}
|
||||
|
||||
// Do things if SSL is active
|
||||
if (SnapURL::isCurrentUrlSSL()) {
|
||||
if ($custom) {
|
||||
// Set default admin ajax
|
||||
$custom_ajax_url = admin_url('admin-ajax.php', 'https');
|
||||
if ($global->custom_ajax_url != $custom_ajax_url) {
|
||||
$global->custom_ajax_url = $custom_ajax_url;
|
||||
$data['custom_ajax_url'] = $global->custom_ajax_url;
|
||||
$kickoff = false;
|
||||
}
|
||||
} else {
|
||||
// Set HTTPS protocol
|
||||
if ($global->ajax_protocol === 'http') {
|
||||
$global->ajax_protocol = 'https';
|
||||
$data['ajax_protocol'] = $global->ajax_protocol;
|
||||
$kickoff = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// SSL is OFF and we must handle that
|
||||
if ($custom) {
|
||||
// Set default admin ajax
|
||||
$custom_ajax_url = admin_url('admin-ajax.php', 'http');
|
||||
if ($global->custom_ajax_url != $custom_ajax_url) {
|
||||
$global->custom_ajax_url = $custom_ajax_url;
|
||||
$data['custom_ajax_url'] = $global->custom_ajax_url;
|
||||
$kickoff = false;
|
||||
}
|
||||
} else {
|
||||
// Set HTTP protocol
|
||||
if ($global->ajax_protocol === 'https') {
|
||||
$global->ajax_protocol = 'http';
|
||||
$data['ajax_protocol'] = $global->ajax_protocol;
|
||||
$kickoff = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set KickOff true if all setups are gone
|
||||
if ($kickoff) {
|
||||
if ($global->clientside_kickoff !== true) {
|
||||
$global->clientside_kickoff = true;
|
||||
$data['clientside_kickoff'] = $global->clientside_kickoff;
|
||||
}
|
||||
}
|
||||
|
||||
$global->save();
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Quick fix for basic auth
|
||||
*
|
||||
* @return array{basic_auth_enabled:bool,basic_auth_user:string,basic_auth_password:string}
|
||||
*/
|
||||
private function quickFixBasicAuth()
|
||||
{
|
||||
$global = DUP_PRO_Global_Entity::getInstance();
|
||||
$sglobal = DUP_PRO_Secure_Global_Entity::getInstance();
|
||||
$username = isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : false;
|
||||
$password = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : false;
|
||||
if ($username === false || $password === false) {
|
||||
throw new Exception(esc_html__("Username or password were not set.", 'duplicator-pro'));
|
||||
}
|
||||
|
||||
$data = array();
|
||||
$global->basic_auth_enabled = true;
|
||||
$data['basic_auth_enabled'] = true;
|
||||
|
||||
$global->basic_auth_user = $username;
|
||||
$data['basic_auth_user'] = $username;
|
||||
|
||||
$sglobal->basic_auth_password = $password;
|
||||
$data['basic_auth_password'] = "**Secure Info**";
|
||||
|
||||
$global->save();
|
||||
$sglobal->save();
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,406 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Ajax;
|
||||
|
||||
use DUP_PRO_Handler;
|
||||
use DUP_PRO_Log;
|
||||
use DUP_PRO_Package;
|
||||
use DUP_PRO_Package_File_Type;
|
||||
use DUP_PRO_Schedule_Entity;
|
||||
use Duplicator\Addons\OneDriveAddon\Models\OneDriveStorage;
|
||||
use Duplicator\Controllers\SchedulePageController;
|
||||
use Duplicator\Core\CapMng;
|
||||
use Duplicator\Libs\Snap\SnapLog;
|
||||
use Duplicator\Libs\Snap\SnapUtil;
|
||||
use Duplicator\Models\Storages\AbstractStorageEntity;
|
||||
use Duplicator\Models\Storages\StorageAuthInterface;
|
||||
use Duplicator\Models\Storages\UnknownStorage;
|
||||
use Exception;
|
||||
|
||||
class ServicesStorage extends AbstractAjaxService
|
||||
{
|
||||
const STORAGE_BULK_DELETE = 1;
|
||||
const STORAGE_GET_SCHEDULES = 5;
|
||||
|
||||
/**
|
||||
* Init ajax calls
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->addAjaxCall("wp_ajax_duplicator_pro_storage_bulk_actions", "bulkActions");
|
||||
$this->addAjaxCall('wp_ajax_duplicator_pro_get_storage_details', 'packageStoragesDetails');
|
||||
$this->addAjaxCall("wp_ajax_duplicator_pro_storage_test", "testStorage");
|
||||
$this->addAjaxCall("wp_ajax_duplicator_pro_auth_storage", "authorizeStorage");
|
||||
$this->addAjaxCall("wp_ajax_duplicator_pro_revoke_storage", "revokeStorage");
|
||||
|
||||
$this->addAjaxCall("wp_ajax_duplicator_pro_onedrive_all_perms_update", "onedriveAllPermsUpdate");
|
||||
}
|
||||
|
||||
/**
|
||||
* Storage bulk actions handler
|
||||
*
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function bulkActions()
|
||||
{
|
||||
DUP_PRO_Handler::init_error_handler();
|
||||
check_ajax_referer('duplicator_pro_storage_bulk_actions', 'nonce');
|
||||
|
||||
$json = array(
|
||||
'success' => false,
|
||||
'message' => '',
|
||||
'schedules' => array(),
|
||||
);
|
||||
$isValid = true;
|
||||
$inputData = filter_input_array(INPUT_POST, array(
|
||||
'storage_ids' => array(
|
||||
'filter' => FILTER_VALIDATE_INT,
|
||||
'flags' => FILTER_REQUIRE_ARRAY,
|
||||
'options' => array('default' => false),
|
||||
),
|
||||
'perform' => array(
|
||||
'filter' => FILTER_VALIDATE_INT,
|
||||
'flags' => FILTER_REQUIRE_SCALAR,
|
||||
'options' => array('default' => false),
|
||||
),
|
||||
));
|
||||
$storageIDs = $inputData['storage_ids'];
|
||||
$action = $inputData['perform'];
|
||||
|
||||
if (empty($storageIDs) || in_array(false, $storageIDs) || $action === false) {
|
||||
$isValid = false;
|
||||
}
|
||||
|
||||
try {
|
||||
CapMng::can(CapMng::CAP_STORAGE);
|
||||
|
||||
if (!$isValid) {
|
||||
throw new \Exception(__("Invalid Request.", 'duplicator-pro'));
|
||||
}
|
||||
|
||||
foreach ($storageIDs as $id) {
|
||||
switch ($action) {
|
||||
case self::STORAGE_BULK_DELETE:
|
||||
AbstractStorageEntity::deleteById($id);
|
||||
break;
|
||||
case self::STORAGE_GET_SCHEDULES:
|
||||
foreach (DUP_PRO_Schedule_Entity::get_schedules_by_storage_id($id) as $schedule) {
|
||||
$json["schedules"][] = array(
|
||||
"id" => $schedule->getId(),
|
||||
"name" => $schedule->name,
|
||||
"hasOneStorage" => count($schedule->storage_ids) <= 1,
|
||||
"editURL" => SchedulePageController::getInstance()->getEditUrl($schedule->getId()),
|
||||
);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new \Exception("Invalid action.");
|
||||
}
|
||||
}
|
||||
//SORT_REGULAR allows to do array_unique on multidimensional arrays
|
||||
$json["schedules"] = array_unique($json["schedules"], SORT_REGULAR);
|
||||
$json["success"] = true;
|
||||
} catch (\Exception $ex) {
|
||||
$json['message'] = $ex->getMessage();
|
||||
}
|
||||
|
||||
die(json_encode($json));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test storage connection
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function packageStoragesDetails()
|
||||
{
|
||||
AjaxWrapper::json(
|
||||
array(
|
||||
__CLASS__,
|
||||
'packageStoragesDetailsCallback',
|
||||
),
|
||||
'duplicator_pro_get_storage_details',
|
||||
$_POST['nonce'],
|
||||
CapMng::CAP_CREATE
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook ajax wp_ajax_duplicator_pro_get_storage_details
|
||||
*
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
public static function packageStoragesDetailsCallback()
|
||||
{
|
||||
$result = array(
|
||||
'success' => false,
|
||||
'message' => '',
|
||||
'logURL' => '',
|
||||
'storage_providers' => array(),
|
||||
);
|
||||
|
||||
try {
|
||||
if (($package_id = SnapUtil::sanitizeIntInput(INPUT_POST, 'package_id', -1)) < 0) {
|
||||
throw new Exception(__("Invalid Request.", 'duplicator-pro'));
|
||||
}
|
||||
|
||||
$package = DUP_PRO_Package::get_by_id($package_id);
|
||||
if ($package == false) {
|
||||
throw new Exception(sprintf(__('Unknown package %1$d', 'duplicator-pro'), $package_id));
|
||||
}
|
||||
|
||||
$providers = array();
|
||||
foreach ($package->upload_infos as $upload_info) {
|
||||
if (($storage = AbstractStorageEntity::getById($upload_info->getStorageId())) === false) {
|
||||
continue;
|
||||
}
|
||||
$storageInfo = [];
|
||||
$storageInfo["failed"] = $upload_info->isFailed();
|
||||
$storageInfo["cancelled"] = $upload_info->cancelled;
|
||||
$storageInfo["infoHTML"] = $storage->renderRemoteLocationInfo(
|
||||
$upload_info->isFailed(),
|
||||
$upload_info->cancelled,
|
||||
false
|
||||
);
|
||||
// Newest storage upload infos will supercede earlier attempts to the same storage
|
||||
$providers[$upload_info->getStorageId()] = $storageInfo;
|
||||
}
|
||||
|
||||
$result['success'] = true;
|
||||
$result['message'] = __('Retrieved storage information', 'duplicator-pro');
|
||||
$result['logURL'] = $package->getLocalPackageFileURL(DUP_PRO_Package_File_Type::Log);
|
||||
$result['storage_providers'] = $providers;
|
||||
} catch (Exception $ex) {
|
||||
$result['success'] = false;
|
||||
$result['message'] = $ex->getMessage();
|
||||
DUP_PRO_Log::traceError($ex->getMessage());
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test storage connection
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testStorage()
|
||||
{
|
||||
AjaxWrapper::json(
|
||||
array(
|
||||
__CLASS__,
|
||||
'testStorageCallback',
|
||||
),
|
||||
'duplicator_pro_storage_test',
|
||||
$_POST['nonce'],
|
||||
CapMng::CAP_STORAGE
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test storage callback
|
||||
*
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
public static function testStorageCallback()
|
||||
{
|
||||
$result = array(
|
||||
'success' => false,
|
||||
'message' => '',
|
||||
'status_msgs' => '',
|
||||
);
|
||||
|
||||
$storageId = SnapUtil::sanitizeIntInput(SnapUtil::INPUT_REQUEST, 'storage_id', -1);
|
||||
if ($storageId < 0 || ($storage = AbstractStorageEntity::getById($storageId)) === false) {
|
||||
$result['message'] = __('Invalid storage', 'duplicator-pro');
|
||||
$result['status_msgs'] = __('Invalid storage', 'duplicator-pro');
|
||||
} else {
|
||||
$result['success'] = $storage->test($result['message']);
|
||||
$result['status_msgs'] = $storage->getTestLog();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Authorize storage
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function authorizeStorage()
|
||||
{
|
||||
AjaxWrapper::json(
|
||||
array(
|
||||
__CLASS__,
|
||||
'authorizeStorageCallback',
|
||||
),
|
||||
'duplicator_pro_auth_storage',
|
||||
$_POST['nonce'],
|
||||
CapMng::CAP_STORAGE
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Authorize storage callback
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
public static function authorizeStorageCallback()
|
||||
{
|
||||
$result = array(
|
||||
'success' => false,
|
||||
'storage_id' => -1,
|
||||
'message' => '',
|
||||
);
|
||||
|
||||
$storageId = SnapUtil::sanitizeIntInput(SnapUtil::INPUT_REQUEST, 'storage_id', -1);
|
||||
if ($storageId < 0) {
|
||||
// New storage
|
||||
$intMin = (PHP_INT_MAX * -1 - 1); // On php 5.6 PHP_INT_MIN don't exists
|
||||
$storageType = SnapUtil::sanitizeIntInput(SnapUtil::INPUT_REQUEST, 'storage_type', $intMin);
|
||||
$storage = AbstractStorageEntity::getNewStorageByType($storageType);
|
||||
if ($storage instanceof UnknownStorage) {
|
||||
$result['message'] = __('Invalid storage type', 'duplicator-pro');
|
||||
return $result;
|
||||
}
|
||||
} elseif (($storage = AbstractStorageEntity::getById($storageId)) === false) {
|
||||
$result['message'] = __('Invalid storage', 'duplicator-pro');
|
||||
return $result;
|
||||
} else {
|
||||
$result['storage_id'] = $storage->getId();
|
||||
}
|
||||
|
||||
DUP_PRO_Log::trace("Auth storage: " . $storage->getName() . "[ID:" . $storage->getId() . "] type: " . $storage->getStypeName());
|
||||
if (!$storage instanceof StorageAuthInterface) {
|
||||
$result['message'] = __('Storage does not support authorization', 'duplicator-pro');
|
||||
return $result;
|
||||
}
|
||||
|
||||
if ($storage->authorizeFromRequest($result['message'])) {
|
||||
if (($result['success'] = $storage->save()) == false) {
|
||||
$result['message'] = __('Failed to update storage', 'duplicator-pro');
|
||||
}
|
||||
}
|
||||
|
||||
// Make suge storage id is set for new storage
|
||||
$result['storage_id'] = $storage->getId();
|
||||
DUP_PRO_Log::trace('Auth result: ' . SnapLog::v2str($result['success']) . ' msg: ' . $result['message']);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Revoke storage
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function revokeStorage()
|
||||
{
|
||||
AjaxWrapper::json(
|
||||
array(
|
||||
__CLASS__,
|
||||
'revokeStorageCallback',
|
||||
),
|
||||
'duplicator_pro_revoke_storage',
|
||||
$_POST['nonce'],
|
||||
CapMng::CAP_STORAGE
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Revoke storage callback
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
public static function revokeStorageCallback()
|
||||
{
|
||||
$result = array(
|
||||
'success' => false,
|
||||
'message' => '',
|
||||
);
|
||||
|
||||
$storageId = SnapUtil::sanitizeIntInput(SnapUtil::INPUT_REQUEST, 'storage_id', -1);
|
||||
if ($storageId < 0 || ($storage = AbstractStorageEntity::getById($storageId)) === false) {
|
||||
$result['message'] = __('Invalid storage', 'duplicator-pro');
|
||||
return $result;
|
||||
}
|
||||
|
||||
DUP_PRO_Log::trace("Revoke storage: " . $storage->getName() . "[ID:" . $storage->getId() . "] type: " . $storage->getStypeName());
|
||||
if (!$storage instanceof StorageAuthInterface) {
|
||||
$result['message'] = __('Storage does not support authorization', 'duplicator-pro');
|
||||
DUP_PRO_Log::trace($result['message']);
|
||||
return $result;
|
||||
}
|
||||
|
||||
if ($storage->revokeAuthorization($result['message'])) {
|
||||
if (($result['success'] = $storage->save()) == false) {
|
||||
$result['message'] = __('Failed to update storage', 'duplicator-pro');
|
||||
}
|
||||
}
|
||||
DUP_PRO_Log::trace('Revoke result: ' . SnapLog::v2str($result['success']) . ' msg: ' . $result['message']);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update OneDrive permissions
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function onedriveAllPermsUpdate()
|
||||
{
|
||||
AjaxWrapper::json(
|
||||
array(
|
||||
__CLASS__,
|
||||
'onedriveAllPermsUpdateCallback',
|
||||
),
|
||||
'duplicator_pro_onedrive_all_perms_update',
|
||||
$_POST['nonce'],
|
||||
CapMng::CAP_STORAGE
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update OneDrive permissions callback
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
public static function onedriveAllPermsUpdateCallback()
|
||||
{
|
||||
$result = array(
|
||||
'success' => false,
|
||||
'message' => '',
|
||||
'auth_url' => '',
|
||||
);
|
||||
|
||||
$storageId = SnapUtil::sanitizeIntInput(SnapUtil::INPUT_REQUEST, 'storage_id', -1);
|
||||
if ($storageId < 0 || ($storage = AbstractStorageEntity::getById($storageId)) === false) {
|
||||
$result['message'] = __('Invalid storage', 'duplicator-pro');
|
||||
return $result;
|
||||
}
|
||||
|
||||
DUP_PRO_Log::trace("Update OneDrive permissions: " . $storage->getName() . " [ID:" . $storage->getId() . "] type: " . $storage->getStypeName());
|
||||
if (!$storage instanceof OneDriveStorage) {
|
||||
$result['message'] = __('Stroage isn\'t OneDrive storage', 'duplicator-pro');
|
||||
DUP_PRO_Log::trace($result['message']);
|
||||
return $result;
|
||||
}
|
||||
|
||||
$allPerm = SnapUtil::sanitizeBoolInput(SnapUtil::INPUT_REQUEST, 'all_perms', false);
|
||||
|
||||
if ($storage->setAllPermissions($allPerm) == false) {
|
||||
$result['message'] = __('Failed to set all permissions', 'duplicator-pro');
|
||||
return $result;
|
||||
}
|
||||
|
||||
$result['success'] = true;
|
||||
$result['auth_url'] = $storage->getAuthorizationUrl();
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Duplicator
|
||||
* @copyright (c) 2022, Snap Creek LLC
|
||||
*/
|
||||
|
||||
namespace Duplicator\Ajax;
|
||||
|
||||
use DUP_PRO_Archive;
|
||||
use DUP_PRO_Handler;
|
||||
use DUP_PRO_ScanValidator;
|
||||
use Duplicator\Addons\ProBase\License\License;
|
||||
use Duplicator\Libs\Snap\SnapUtil;
|
||||
use Exception;
|
||||
|
||||
class ServicesTools extends AbstractAjaxService
|
||||
{
|
||||
/**
|
||||
* Init ajax calls
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
if (!License::can(License::CAPABILITY_PRO_BASE)) {
|
||||
return;
|
||||
}
|
||||
$this->addAjaxCall('wp_ajax_DUP_PRO_CTRL_Tools_runScanValidator', 'runScanValidator');
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the ScanValidator and returns display JSON result
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function runScanValidator()
|
||||
{
|
||||
DUP_PRO_Handler::init_error_handler();
|
||||
check_ajax_referer('DUP_PRO_CTRL_Tools_runScanValidator', 'nonce');
|
||||
|
||||
// Let's setup execution time on proper way (multiserver supported)
|
||||
try {
|
||||
if (function_exists('set_time_limit')) {
|
||||
set_time_limit(0); // unlimited
|
||||
} else {
|
||||
if (function_exists('ini_set') && SnapUtil::isIniValChangeable('max_execution_time')) {
|
||||
ini_set('max_execution_time', '0'); // unlimited
|
||||
}
|
||||
}
|
||||
|
||||
// there is error inside PHP because of PHP versions and server setup,
|
||||
// let's try to made small hack and set some "normal" value if is possible
|
||||
} catch (Exception $ex) {
|
||||
if (function_exists('set_time_limit')) {
|
||||
@set_time_limit(3600); // 60 minutes
|
||||
} else {
|
||||
if (function_exists('ini_set') && SnapUtil::isIniValChangeable('max_execution_time')) {
|
||||
@ini_set('max_execution_time', '3600'); // 60 minutes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//scan-recursive
|
||||
$isValid = true;
|
||||
$inputData = filter_input_array(INPUT_POST, array(
|
||||
'scan-recursive' => array(
|
||||
'filter' => FILTER_VALIDATE_BOOLEAN,
|
||||
'flags' => FILTER_NULL_ON_FAILURE,
|
||||
),
|
||||
));
|
||||
|
||||
if (is_null($inputData['scan-recursive'])) {
|
||||
$isValid = false;
|
||||
}
|
||||
|
||||
$result = [
|
||||
'success' => false,
|
||||
'message' => '',
|
||||
'scanData' => null,
|
||||
];
|
||||
|
||||
try {
|
||||
if (!$isValid) {
|
||||
throw new Exception(__("Invalid Request.", 'duplicator-pro'));
|
||||
}
|
||||
|
||||
$scanner = new DUP_PRO_ScanValidator();
|
||||
$scanner->recursion = $inputData['scan-recursive'];
|
||||
$result['scanData'] = $scanner->run(DUP_PRO_Archive::getScanPaths());
|
||||
$result['success'] = ($result['scanData']->fileCount > 0);
|
||||
} catch (Exception $exc) {
|
||||
$result['success'] = false;
|
||||
$result['message'] = $exc->getMessage();
|
||||
}
|
||||
|
||||
wp_send_json($result);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user