Add upstream
This commit is contained in:
336
wp-content/plugins/updraftplus/methods/addon-base-v2.php
Normal file
336
wp-content/plugins/updraftplus/methods/addon-base-v2.php
Normal file
@@ -0,0 +1,336 @@
|
||||
<?php
|
||||
|
||||
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed');
|
||||
|
||||
/*
|
||||
Methods to define when extending this class (can use $this->storage and $this->options where relevant):
|
||||
do_bootstrap($possible_options_array) # Return a WP_Error object if something goes wrong
|
||||
do_upload($file, $sourcefile) # Return true/false
|
||||
do_listfiles($match)
|
||||
do_delete($file) - return true/false
|
||||
do_download($file, $fullpath, $start_offset) - return true/false
|
||||
do_config_print()
|
||||
get_credentials_test_required_parameters() - return an array: keys = required _POST parameters; values = description of each
|
||||
do_credentials_test($testfile, $posted_settings) - return true/false; or alternatively an array with keys 'result' (true/false) and 'data' (arbitrary debug data)
|
||||
do_credentials_test_deletefile($testfile, $posted_settings)
|
||||
*/
|
||||
|
||||
// Uses job options: Yes
|
||||
// Uses single-array storage: Yes
|
||||
|
||||
if (!class_exists('UpdraftPlus_BackupModule')) require_once(UPDRAFTPLUS_DIR.'/methods/backup-module.php');
|
||||
|
||||
/**
|
||||
* Note that the naming of this class is historical. There is nothing inherent which restricts it to add-ons, or requires add-ons to use it. It is just an abstraction layer that results in needing to write less code for the storage module.
|
||||
*/
|
||||
abstract class UpdraftPlus_RemoteStorage_Addons_Base_v2 extends UpdraftPlus_BackupModule {
|
||||
|
||||
protected $method;
|
||||
|
||||
protected $description;
|
||||
|
||||
protected $options;
|
||||
|
||||
private $chunked;
|
||||
|
||||
public function __construct($method, $description, $chunked = true, $test_button = true) {
|
||||
|
||||
$this->method = $method;
|
||||
$this->description = $description;
|
||||
$this->chunked = $chunked;
|
||||
$this->test_button = $test_button;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* download method: takes a file name (base name), and removes it from the cloud storage
|
||||
*
|
||||
* @param string $file specific file for being removed from cloud storage
|
||||
* @return array
|
||||
*/
|
||||
public function download($file) {
|
||||
return $this->download_file(false, $file);
|
||||
}
|
||||
|
||||
public function backup($backup_array) {
|
||||
return $this->upload_files(null, $backup_array);
|
||||
}
|
||||
|
||||
public function delete($files, $method_obj = false, $sizeinfo = array()) {
|
||||
return $this->delete_files(false, $files, $method_obj, $sizeinfo);
|
||||
}
|
||||
|
||||
protected function required_configuration_keys() {
|
||||
}
|
||||
|
||||
public function upload_files($ret, $backup_array) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
$this->options = $this->get_options();
|
||||
|
||||
if (!$this->options_exist($this->options)) {
|
||||
$updraftplus->log('No '.$this->method.' settings were found');
|
||||
$updraftplus->log(sprintf(__('No %s settings were found', 'updraftplus'), $this->description), 'error');
|
||||
return false;
|
||||
}
|
||||
|
||||
$storage = $this->bootstrap();
|
||||
if (is_wp_error($storage)) return $updraftplus->log_wp_error($storage, false, true);
|
||||
|
||||
$this->set_storage($storage);
|
||||
|
||||
$updraft_dir = trailingslashit($updraftplus->backups_dir_location());
|
||||
|
||||
foreach ($backup_array as $file) {
|
||||
$updraftplus->log($this->method." upload ".((!empty($this->options['ownername'])) ? '(account owner: '.$this->options['ownername'].')' : '').": attempt: $file");
|
||||
try {
|
||||
if ($this->do_upload($file, $updraft_dir.$file)) {
|
||||
$updraftplus->uploaded_file($file);
|
||||
} else {
|
||||
$any_failures = true;
|
||||
$updraftplus->log('ERROR: '.$this->method.': Failed to upload file: '.$file);
|
||||
$updraftplus->log(__('Error', 'updraftplus').': '.$this->description.': '.sprintf(__('Failed to upload %s', 'updraftplus'), $file), 'error');
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$any_failures = true;
|
||||
$updraftplus->log('ERROR ('.get_class($e).'): '.$this->method.": $file: Failed to upload file: ".$e->getMessage().' (code: '.$e->getCode().', line: '.$e->getLine().', file: '.$e->getFile().')');
|
||||
$updraftplus->log(__('Error', 'updraftplus').': '.$this->description.': '.sprintf(__('Failed to upload %s', 'updraftplus'), $file), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
return (!empty($any_failures)) ? null : true;
|
||||
|
||||
}
|
||||
|
||||
public function listfiles($match = 'backup_') {
|
||||
|
||||
try {
|
||||
|
||||
if (!method_exists($this, 'do_listfiles')) {
|
||||
return new WP_Error('no_listing', 'This remote storage method does not support file listing');
|
||||
}
|
||||
|
||||
$this->options = $this->get_options();
|
||||
if (!$this->options_exist($this->options)) return new WP_Error('no_settings', sprintf(__('No %s settings were found', 'updraftplus'), $this->description));
|
||||
|
||||
$storage = $this->bootstrap();
|
||||
if (is_wp_error($storage)) return $storage;
|
||||
|
||||
return $this->do_listfiles($match);
|
||||
|
||||
} catch (Exception $e) {
|
||||
global $updraftplus;
|
||||
$updraftplus->log('ERROR: '.$this->method.": $file: Failed to list files: ".$e->getMessage().' (code: '.$e->getCode().', line: '.$e->getLine().', file: '.$e->getFile().')');
|
||||
return new WP_Error('list_failed', $this->description.': '.__('failed to list files', 'updraftplus'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function delete_files($ret, $files, $ignore_it = false) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
if (is_string($files)) $files = array($files);
|
||||
|
||||
if (empty($files)) return true;
|
||||
if (!method_exists($this, 'do_delete')) {
|
||||
$updraftplus->log($this->method.": Delete failed: this storage method does not allow deletions");
|
||||
return false;
|
||||
}
|
||||
|
||||
$storage = $this->get_storage();
|
||||
|
||||
if (empty($storage)) {
|
||||
|
||||
$this->options = $this->get_options();
|
||||
if (!$this->options_exist($this->options)) {
|
||||
$updraftplus->log('No '.$this->method.' settings were found');
|
||||
$updraftplus->log(sprintf(__('No %s settings were found', 'updraftplus'), $this->description), 'error');
|
||||
return false;
|
||||
}
|
||||
|
||||
$storage = $this->bootstrap();
|
||||
if (is_wp_error($storage)) return $storage;
|
||||
|
||||
}
|
||||
|
||||
$ret = true;
|
||||
|
||||
foreach ($files as $file) {
|
||||
$updraftplus->log($this->method.": Delete remote: $file");
|
||||
try {
|
||||
if (!$this->do_delete($file)) {
|
||||
$ret = false;
|
||||
$updraftplus->log($this->method.": Delete failed");
|
||||
} else {
|
||||
$updraftplus->log($this->method.": $file: Delete succeeded");
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$updraftplus->log('ERROR: '.$this->method.": $file: Failed to delete file: ".$e->getMessage().' (code: '.$e->getCode().', line: '.$e->getLine().', file: '.$e->getFile().')');
|
||||
$ret = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $ret;
|
||||
|
||||
}
|
||||
|
||||
public function download_file($ret, $files) {
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
if (is_string($files)) $files = array($files);
|
||||
|
||||
if (empty($files)) return true;
|
||||
if (!method_exists($this, 'do_download')) {
|
||||
$updraftplus->log($this->method.": Download failed: this storage method does not allow downloading");
|
||||
$updraftplus->log($this->description.': '.__('This storage method does not allow downloading', 'updraftplus'), 'error');
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->options = $this->get_options();
|
||||
if (!$this->options_exist($this->options)) {
|
||||
$updraftplus->log('No '.$this->method.' settings were found');
|
||||
$updraftplus->log(sprintf(__('No %s settings were found', 'updraftplus'), $this->description), 'error');
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
$storage = $this->bootstrap();
|
||||
if (is_wp_error($storage)) return $updraftplus->log_wp_error($storage, false, true);
|
||||
} catch (Exception $e) {
|
||||
$ret = false;
|
||||
$updraftplus->log('ERROR: '.$this->method.": $files[0]: Failed to download file: ".$e->getMessage().' (code: '.$e->getCode().', line: '.$e->getLine().', file: '.$e->getFile().')');
|
||||
$updraftplus->log(__('Error', 'updraftplus').': '.$this->description.': '.sprintf(__('Failed to download %s', 'updraftplus'), $files[0]), 'error');
|
||||
}
|
||||
|
||||
$ret = true;
|
||||
$updraft_dir = untrailingslashit($updraftplus->backups_dir_location());
|
||||
|
||||
foreach ($files as $file) {
|
||||
try {
|
||||
$fullpath = $updraft_dir.'/'.$file;
|
||||
$start_offset = file_exists($fullpath) ? filesize($fullpath) : 0;
|
||||
|
||||
if (false == $this->do_download($file, $fullpath, $start_offset)) {
|
||||
$ret = false;
|
||||
$updraftplus->log($this->method." error: failed to download: $file");
|
||||
$updraftplus->log("$file: ".sprintf(__("%s Error", 'updraftplus'), $this->description).": ".__('Failed to download', 'updraftplus'), 'error');
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
$ret = false;
|
||||
$updraftplus->log('ERROR: '.$this->method.": $file: Failed to download file: ".$e->getMessage().' (code: '.$e->getCode().', line: '.$e->getLine().', file: '.$e->getFile().')');
|
||||
$updraftplus->log(__('Error', 'updraftplus').': '.$this->description.': '.sprintf(__('Failed to download %s', 'updraftplus'), $file), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configuration template
|
||||
*
|
||||
* @return String - the template, ready for substitutions to be carried out
|
||||
*/
|
||||
public function get_configuration_template() {
|
||||
$classes = $this->get_css_classes();
|
||||
$template_str = '';
|
||||
|
||||
if (method_exists($this, 'do_get_configuration_template')) {
|
||||
$template_str .= $this->do_get_configuration_template();
|
||||
}
|
||||
if (!$this->test_button || (method_exists($this, 'should_print_test_button') && !$this->should_print_test_button())) return $template_str;
|
||||
$template_str .= $this->get_test_button_html($this->description);
|
||||
return $template_str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies handerbar template options
|
||||
*
|
||||
* @param array $opts
|
||||
* @return array - Modified handerbar template options
|
||||
*/
|
||||
public function transform_options_for_template($opts) {
|
||||
if (method_exists($this, 'do_transform_options_for_template')) {
|
||||
$opts = $this->do_transform_options_for_template($opts);
|
||||
}
|
||||
return $opts;
|
||||
}
|
||||
|
||||
public function config_print_javascript_onready() {
|
||||
$this->do_config_javascript();
|
||||
}
|
||||
|
||||
protected function do_config_javascript() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyse the passed-in options to indicate whether something is configured or not.
|
||||
*
|
||||
* @param Array $opts - options to examine
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
public function options_exist($opts) {
|
||||
if (is_array($opts) && !empty($opts)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public function bootstrap($opts = false, $connect = true) {
|
||||
if (false === $opts) $opts = $this->options;
|
||||
$storage = $this->get_storage();
|
||||
// Be careful of checking empty($opts) here - some storage methods may have no options until the OAuth token has been obtained
|
||||
if ($connect && !$this->options_exist($opts)) return new WP_Error('no_settings', sprintf(__('No %s settings were found', 'updraftplus'), $this->description));
|
||||
if (!empty($storage) && !is_wp_error($storage)) return $storage;
|
||||
return $this->do_bootstrap($opts, $connect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a credentials test. Output can be echoed.
|
||||
*
|
||||
* @param Array $posted_settings - settings to use
|
||||
*
|
||||
* @return Mixed - any data to return (gets logged in the browser eventually)
|
||||
*/
|
||||
public function credentials_test($posted_settings) {
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
$required_test_parameters = $this->get_credentials_test_required_parameters();
|
||||
|
||||
foreach ($required_test_parameters as $param => $descrip) {
|
||||
if (empty($posted_settings[$param])) {
|
||||
printf(__("Failure: No %s was given.", 'updraftplus'), $descrip)."\n";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$storage = $this->bootstrap($posted_settings);
|
||||
|
||||
if (is_wp_error($storage)) {
|
||||
echo __("Failed", 'updraftplus').": ";
|
||||
foreach ($storage->get_error_messages() as $key => $msg) {
|
||||
echo "$msg\n";
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$testfile = md5(time().rand()).'.txt';
|
||||
|
||||
$test_results = $this->do_credentials_test($testfile, $posted_settings);
|
||||
|
||||
$data = (is_array($test_results) && isset($test_results['data'])) ? $test_results['data'] : null;
|
||||
|
||||
if ((is_array($test_results) && $test_results['result']) || (!is_array($test_results) && $test_results)) {
|
||||
_e('Success', 'updraftplus');
|
||||
$this->do_credentials_test_deletefile($testfile, $posted_settings);
|
||||
} else {
|
||||
_e("Failed: We were not able to place a file in that directory - please check your credentials.", 'updraftplus');
|
||||
}
|
||||
|
||||
return $data;
|
||||
|
||||
}
|
||||
}
|
||||
106
wp-content/plugins/updraftplus/methods/addon-not-yet-present.php
Normal file
106
wp-content/plugins/updraftplus/methods/addon-not-yet-present.php
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed.');
|
||||
|
||||
if (!class_exists('UpdraftPlus_BackupModule')) require_once(UPDRAFTPLUS_DIR.'/methods/backup-module.php');
|
||||
|
||||
class UpdraftPlus_BackupModule_AddonNotYetPresent extends UpdraftPlus_BackupModule {
|
||||
|
||||
private $method;
|
||||
|
||||
private $description;
|
||||
|
||||
public function __construct($method, $description, $required_php = false, $image = null) {
|
||||
$this->method = $method;
|
||||
$this->description = $description;
|
||||
$this->required_php = $required_php;
|
||||
$this->image = $image;
|
||||
$this->error_msg = 'This remote storage method ('.$this->description.') requires PHP '.$this->required_php.' or later';
|
||||
$this->error_msg_trans = sprintf(__('This remote storage method (%s) requires PHP %s or later.', 'updraftplus'), $this->description, $this->required_php);
|
||||
}
|
||||
|
||||
public function backup($backup_array) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
$this->log("You do not have the UpdraftPlus ".$this->method.' add-on installed - get it from '.apply_filters("updraftplus_com_link", "https://updraftplus.com/shop/").'');
|
||||
|
||||
$this->log(sprintf(__('You do not have the UpdraftPlus %s add-on installed - get it from %s', 'updraftplus'), $this->description, ''.apply_filters("updraftplus_com_link", "https://updraftplus.com/shop/").''), 'error', 'missingaddon-'.$this->method);
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a list of supported features for this storage method
|
||||
*
|
||||
* Currently known features:
|
||||
*
|
||||
* - multi_options : indicates that the remote storage module
|
||||
* can handle its options being in the Feb-2017 multi-options
|
||||
* format. N.B. This only indicates options handling, not any
|
||||
* other multi-destination options.
|
||||
*
|
||||
* - multi_servers : not implemented yet: indicates that the
|
||||
* remote storage module can handle multiple servers at backup
|
||||
* time. This should not be specified without multi_options.
|
||||
* multi_options without multi_servers is fine - it will just
|
||||
* cause only the first entry in the options array to be used.
|
||||
*
|
||||
* - config_templates : not implemented yet: indicates that
|
||||
* the remote storage module can output its configuration in
|
||||
* Handlebars format via the get_configuration_template() method.
|
||||
*
|
||||
* @return Array - an array of supported features (any features not
|
||||
* mentioned are assumed to not be supported)
|
||||
*/
|
||||
public function get_supported_features() {
|
||||
// The 'multi_options' options format is handled via only accessing options via $this->get_options()
|
||||
return array('multi_options', 'config_templates');
|
||||
}
|
||||
|
||||
public function delete($files, $method_obj = false, $sizeinfo = array()) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
$this->log('You do not have the UpdraftPlus '.$this->method.' add-on installed - get it from '.apply_filters("updraftplus_com_link", "https://updraftplus.com/shop/").'');
|
||||
|
||||
$this->log(sprintf(__('You do not have the UpdraftPlus %s add-on installed - get it from %s', 'updraftplus'), $this->description, ''.apply_filters("updraftplus_com_link", "https://updraftplus.com/shop/").''), 'error', 'missingaddon-'.$this->method);
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
public function listfiles($match = 'backup_') {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
|
||||
return new WP_Error('no_addon', sprintf(__('You do not have the UpdraftPlus %s add-on installed - get it from %s', 'updraftplus'), $this->description, ''.apply_filters("updraftplus_com_link", "https://updraftplus.com/shop/")));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configuration template
|
||||
*
|
||||
* @return String - the template, ready for substitutions to be carried out
|
||||
*/
|
||||
public function get_configuration_template() {
|
||||
$link = sprintf(__('%s support is available as an add-on', 'updraftplus'), $this->description).' - <a href="'.apply_filters("updraftplus_com_link", "https://updraftplus.com/shop/".$this->method."/").'" target="_blank">'.__('follow this link to get it', 'updraftplus');
|
||||
|
||||
$default = '
|
||||
<tr class="updraftplusmethod '.$this->method.'">
|
||||
<th>'.$this->description.':</th>
|
||||
<td>'.((!empty($this->image)) ? '<p><img src="'.UPDRAFTPLUS_URL.'/images/'.$this->image.'"></p>' : '').$link.'</a></td>
|
||||
</tr>';
|
||||
|
||||
if (version_compare(phpversion(), $this->required_php, '<')) {
|
||||
$default .= '<tr class="updraftplusmethod '.$this->method.'">
|
||||
<th></th>
|
||||
<td>
|
||||
<em>
|
||||
'.htmlspecialchars($this->error_msg_trans).'
|
||||
'.htmlspecialchars(__('You will need to ask your web hosting company to upgrade.', 'updraftplus')).'
|
||||
'.sprintf(__('Your %s version: %s.', 'updraftplus'), 'PHP', phpversion()).'
|
||||
</em>
|
||||
</td>
|
||||
</tr>';
|
||||
}
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
40
wp-content/plugins/updraftplus/methods/azure.php
Normal file
40
wp-content/plugins/updraftplus/methods/azure.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access.');
|
||||
|
||||
if (version_compare(phpversion(), '5.3.3', '>=')) {
|
||||
|
||||
if (class_exists('UpdraftPlus_Addons_RemoteStorage_azure')) {
|
||||
class UpdraftPlus_BackupModule_azure extends UpdraftPlus_Addons_RemoteStorage_azure {
|
||||
public function __construct() {
|
||||
parent::__construct('azure', 'Microsoft Azure', true, true);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
include_once(UPDRAFTPLUS_DIR.'/methods/addon-not-yet-present.php');
|
||||
/**
|
||||
* N.B. UpdraftPlus_BackupModule_AddonNotYetPresent extends UpdraftPlus_BackupModule
|
||||
*/
|
||||
class UpdraftPlus_BackupModule_azure extends UpdraftPlus_BackupModule_AddonNotYetPresent {
|
||||
public function __construct() {
|
||||
parent::__construct('azure', 'Microsoft Azure', '5.3.3', 'azure.png');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
include_once(UPDRAFTPLUS_DIR.'/methods/insufficient.php');
|
||||
/**
|
||||
* N.B. UpdraftPlus_BackupModule_insufficientphp extends UpdraftPlus_BackupModule
|
||||
*/
|
||||
class UpdraftPlus_BackupModule_azure extends UpdraftPlus_BackupModule_insufficientphp {
|
||||
public function __construct() {
|
||||
parent::__construct('azure', 'Microsoft Azure', '5.3.3', 'azure.png');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
40
wp-content/plugins/updraftplus/methods/backblaze.php
Normal file
40
wp-content/plugins/updraftplus/methods/backblaze.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access.');
|
||||
|
||||
if (version_compare(phpversion(), '5.3.3', '>=')) {
|
||||
|
||||
if (class_exists('UpdraftPlus_Addons_RemoteStorage_backblaze')) {
|
||||
class UpdraftPlus_BackupModule_backblaze extends UpdraftPlus_Addons_RemoteStorage_backblaze {
|
||||
public function __construct() {
|
||||
parent::__construct('backblaze', 'Backblaze', true, true);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
include_once(UPDRAFTPLUS_DIR.'/methods/addon-not-yet-present.php');
|
||||
/**
|
||||
* N.B. UpdraftPlus_BackupModule_AddonNotYetPresent extends UpdraftPlus_BackupModule
|
||||
*/
|
||||
class UpdraftPlus_BackupModule_backblaze extends UpdraftPlus_BackupModule_AddonNotYetPresent {
|
||||
public function __construct() {
|
||||
parent::__construct('backblaze', 'Backblaze', '5.3.3', 'backblaze.png');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
include_once(UPDRAFTPLUS_DIR.'/methods/insufficient.php');
|
||||
/**
|
||||
* N.B. UpdraftPlus_BackupModule_insufficientphp extends UpdraftPlus_BackupModule
|
||||
*/
|
||||
class UpdraftPlus_BackupModule_backblaze extends UpdraftPlus_BackupModule_insufficientphp {
|
||||
public function __construct() {
|
||||
parent::__construct('backblaze', 'Backblaze', '5.3.3', 'backblaze.png');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
673
wp-content/plugins/updraftplus/methods/backup-module.php
Normal file
673
wp-content/plugins/updraftplus/methods/backup-module.php
Normal file
@@ -0,0 +1,673 @@
|
||||
<?php
|
||||
|
||||
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed.');
|
||||
|
||||
abstract class UpdraftPlus_BackupModule {
|
||||
|
||||
private $_options;
|
||||
|
||||
private $_instance_id;
|
||||
|
||||
private $_storage;
|
||||
|
||||
/**
|
||||
* Store options (within this class) for this remote storage module. There is also a parameter for saving to the permanent storage (i.e. database).
|
||||
*
|
||||
* @param array $options array of options to store
|
||||
* @param boolean $save whether or not to also save the options to the database
|
||||
* @param null|String $instance_id optionally set the instance ID for this instance at the same time. This is required if you have not already set an instance ID with set_instance_id()
|
||||
* @return void|Boolean If saving to DB, then the result of the DB save operation is returned.
|
||||
*/
|
||||
public function set_options($options, $save = false, $instance_id = null) {
|
||||
|
||||
$this->_options = $options;
|
||||
|
||||
// Remove any previously-stored storage object, because this is usually tied to the options
|
||||
if (!empty($this->_storage)) unset($this->_storage);
|
||||
|
||||
if ($instance_id) $this->set_instance_id($instance_id);
|
||||
|
||||
if ($save) return $this->save_options();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the current options to the database. This is a private function; external callers should use set_options().
|
||||
*
|
||||
* @throws Exception if trying to save options without indicating an instance_id, or if the remote storage module does not have the multi-option capability
|
||||
*/
|
||||
private function save_options() {
|
||||
|
||||
if (!$this->supports_feature('multi_options')) {
|
||||
throw new Exception('save_options() can only be called on a storage method which supports multi_options (this module, '.$this->get_id().', does not)');
|
||||
}
|
||||
|
||||
if (!$this->_instance_id) {
|
||||
throw new Exception('save_options() requires an instance ID, but was called without setting one (either directly or via set_instance_id())');
|
||||
}
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
$current_db_options = UpdraftPlus_Storage_Methods_Interface::update_remote_storage_options_format($this->get_id());
|
||||
|
||||
if (is_wp_error($current_db_options)) {
|
||||
throw new Exception('save_options(): options fetch/update failed ('.$current_db_options->get_error_code().': '.$current_db_options->get_error_message().')');
|
||||
}
|
||||
|
||||
$current_db_options['settings'][$this->_instance_id] = $this->_options;
|
||||
|
||||
return UpdraftPlus_Options::update_updraft_option('updraft_'.$this->get_id(), $current_db_options);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve default options for this remote storage module.
|
||||
* This method would normally be over-ridden by the child.
|
||||
*
|
||||
* @return Array - an array of options
|
||||
*/
|
||||
public function get_default_options() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a list of supported features for this storage method
|
||||
* This method should be over-ridden by methods supporting new
|
||||
* features.
|
||||
*
|
||||
* Keys are strings, and values are booleans.
|
||||
*
|
||||
* Currently known features:
|
||||
*
|
||||
* - multi_options : indicates that the remote storage module
|
||||
* can handle its options being in the Feb-2017 multi-options
|
||||
* format. N.B. This only indicates options handling, not any
|
||||
* other multi-destination options.
|
||||
*
|
||||
* - multi_servers : not implemented yet: indicates that the
|
||||
* remote storage module can handle multiple servers at backup
|
||||
* time. This should not be specified without multi_options.
|
||||
* multi_options without multi_servers is fine - it will just
|
||||
* cause only the first entry in the options array to be used.
|
||||
*
|
||||
* - config_templates : not implemented yet: indicates that
|
||||
* the remote storage module can output its configuration in
|
||||
* Handlebars format via the get_configuration_template() method.
|
||||
*
|
||||
* @return Array - an array of supported features (any features not
|
||||
* mentioned are assumed to not be supported)
|
||||
*/
|
||||
public function get_supported_features() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method should only be called if the feature 'multi storage' is supported. In that case, it returns a template with information about the remote storage. The code below is a placeholder, and methods supporting the feature should always over-ride it.
|
||||
*
|
||||
* @return String - HTML template
|
||||
*/
|
||||
public function get_pre_configuration_template() {
|
||||
return $this->get_id().": called, but not implemented in the child class (coding error)";
|
||||
}
|
||||
|
||||
/**
|
||||
* This method should only be called if the feature 'config templates' is supported. In that case, it returns a template with appropriate placeholders for specific settings. The code below is a placeholder, and methods supporting the feature should always over-ride it.
|
||||
*
|
||||
* @return String - HTML template
|
||||
*/
|
||||
public function get_configuration_template() {
|
||||
return $this->get_id().": called, but not implemented in the child class (coding error)";
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will set the stored storage object to that indicated
|
||||
*
|
||||
* @param Object $storage - the storage client
|
||||
*/
|
||||
public function set_storage($storage) {
|
||||
$this->_storage = $storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will return the stored storage client
|
||||
*
|
||||
* @return Object - the stored remote storage client
|
||||
*/
|
||||
public function get_storage() {
|
||||
if (!empty($this->_storage)) return $this->_storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs id and name fields, as if currently within an input tag
|
||||
*
|
||||
* This assumes standardised options handling (i.e. that the options array is updraft_(method-id))
|
||||
*
|
||||
* @param Array|String $field - the field identifiers
|
||||
* @param Boolean $return_instead_of_echo - tells the method if it should return the output or echo it to page
|
||||
*/
|
||||
public function output_settings_field_name_and_id($field, $return_instead_of_echo = false) {
|
||||
|
||||
$method_id = $this->get_id();
|
||||
|
||||
$instance_id = $this->supports_feature('config_templates') ? '{{instance_id}}' : $this->_instance_id;
|
||||
|
||||
$id = '';
|
||||
$name = '';
|
||||
|
||||
if (is_array($field)) {
|
||||
foreach ($field as $key => $value) {
|
||||
$id .= '_'.$value;
|
||||
$name .= '['.$value.']';
|
||||
}
|
||||
} else {
|
||||
$id = '_'.$field;
|
||||
$name = '['.$field.']';
|
||||
}
|
||||
|
||||
$output = "id=\"updraft_${method_id}${id}_${instance_id}\" name=\"updraft_${method_id}[settings][${instance_id}]${name}\" ";
|
||||
|
||||
if ($return_instead_of_echo) {
|
||||
return $output;
|
||||
} else {
|
||||
echo $output;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the CSS ID
|
||||
*
|
||||
* @param String $field - the field identifier to return a CSS ID for
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public function get_css_id($field) {
|
||||
$method_id = $this->get_id();
|
||||
$instance_id = $this->supports_feature('config_templates') ? '{{instance_id}}' : $this->_instance_id;
|
||||
return "updraft_${method_id}_${field}_${instance_id}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get handlebarsjs template
|
||||
* This deals with any boiler-plate, prior to calling config_print()
|
||||
*
|
||||
* @uses self::config_print()
|
||||
* @uses self::get_configuration_template()
|
||||
*
|
||||
* return handlebarsjs template or html
|
||||
*/
|
||||
public function get_template() {
|
||||
ob_start();
|
||||
// Allow methods to not use this hidden field, if they do not output any settings (to prevent their saved settings being over-written by just this hidden field)
|
||||
if ($this->print_shared_settings_fields()) {
|
||||
?><tr class="<?php echo $this->get_css_classes(); ?>"><input type="hidden" name="updraft_<?php echo $this->get_id();?>[version]" value="1"></tr><?php
|
||||
}
|
||||
|
||||
if ($this->supports_feature('config_templates')) {
|
||||
?>
|
||||
{{#if first_instance}}
|
||||
<?php
|
||||
|
||||
$this->get_pre_configuration_template();
|
||||
|
||||
if ($this->supports_feature('multi_storage')) {
|
||||
do_action('updraftplus_config_print_add_multi_storage', $this->get_id(), $this);
|
||||
}
|
||||
|
||||
?>
|
||||
{{/if}}
|
||||
<?php
|
||||
do_action('updraftplus_config_print_before_storage', $this->get_id(), $this);
|
||||
|
||||
if ($this->supports_feature('multi_storage')) {
|
||||
do_action('updraftplus_config_print_add_instance_label', $this->get_id(), $this);
|
||||
}
|
||||
|
||||
$template = ob_get_clean();
|
||||
$template .= $this->get_configuration_template();
|
||||
} else {
|
||||
do_action('updraftplus_config_print_before_storage', $this->get_id(), $this);
|
||||
// N.B. These are mutually exclusive: config_print() is not used if config_templates is supported. So, even during transition, the UpdraftPlus_BackupModule instance only needs to support one of the two, not both.
|
||||
$this->config_print();
|
||||
$template = ob_get_clean();
|
||||
}
|
||||
return $template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies handerbar template options. Other child class can extend it.
|
||||
*
|
||||
* @param array $opts
|
||||
* @return array - Modified handerbar template options
|
||||
*/
|
||||
public function transform_options_for_template($opts) {
|
||||
return $opts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives settings keys which values should not passed to handlebarsjs context.
|
||||
* The settings stored in UD in the database sometimes also include internal information that it would be best not to send to the front-end (so that it can't be stolen by a man-in-the-middle attacker)
|
||||
*
|
||||
* @return array - Settings array keys which should be filtered
|
||||
*/
|
||||
public function filter_frontend_settings_keys() {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Over-ride this to allow methods to not use the hidden version field, if they do not output any settings (to prevent their saved settings being over-written by just this hidden field
|
||||
*
|
||||
* @return [boolean] - return true to output the version field or false to not output the field
|
||||
*/
|
||||
public function print_shared_settings_fields() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints out the configuration section for a particular module. This is now (Sep 2017) considered deprecated; things are being ported over to get_configuration_template(), indicated via the feature 'config_templates'.
|
||||
*/
|
||||
public function config_print() {
|
||||
echo $this->get_id().": module neither declares config_templates support, nor has a config_print() method (coding bug)";
|
||||
}
|
||||
|
||||
/**
|
||||
* Supplies the list of keys for options to be saved in the backup job.
|
||||
*
|
||||
* @return Array
|
||||
*/
|
||||
public function get_credentials() {
|
||||
$keys = array('updraft_ssl_disableverify', 'updraft_ssl_nossl', 'updraft_ssl_useservercerts');
|
||||
if (!$this->supports_feature('multi_servers')) $keys[] = 'updraft_'.$this->get_id();
|
||||
return $keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a space-separated list of CSS classes suitable for rows in the configuration section
|
||||
*
|
||||
* @param Boolean $include_instance - a boolean value to indicate if we want to include the instance_id in the css class, we may not want to include the instance if it's for a UI element that we don't want to be removed along with other UI elements that do include a instance id.
|
||||
*
|
||||
* @returns String - the list of CSS classes
|
||||
*/
|
||||
public function get_css_classes($include_instance = true) {
|
||||
$classes = 'updraftplusmethod '.$this->get_id();
|
||||
if (!$include_instance) return $classes;
|
||||
if ($this->supports_feature('multi_options')) {
|
||||
if ($this->supports_feature('config_templates')) {
|
||||
$classes .= ' '.$this->get_id().'-{{instance_id}}';
|
||||
} else {
|
||||
$classes .= ' '.$this->get_id().'-'.$this->_instance_id;
|
||||
}
|
||||
}
|
||||
return $classes;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Returns HTML for a row for a test button
|
||||
*
|
||||
* @param String $title - The text to be used in the button
|
||||
*
|
||||
* @returns String - The HTML to be inserted into the settings page
|
||||
*/
|
||||
protected function get_test_button_html($title) {
|
||||
ob_start();
|
||||
$instance_id = $this->supports_feature('config_templates') ? '{{instance_id}}' : $this->_instance_id;
|
||||
?>
|
||||
<tr class="<?php echo $this->get_css_classes(); ?>">
|
||||
<th></th>
|
||||
<td><p><button id="updraft-<?php echo $this->get_id();?>-test-<?php echo $instance_id;?>" type="button" class="button-primary updraft-test-button updraft-<?php echo $this->get_id();?>-test" data-instance_id="<?php echo $instance_id;?>" data-method="<?php echo $this->get_id();?>" data-method_label="<?php echo esc_attr($title);?>"><?php printf(__('Test %s Settings', 'updraftplus'), $title);?></button></p></td>
|
||||
</tr>
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the backup method identifier for this class
|
||||
*
|
||||
* @return String - the identifier
|
||||
*/
|
||||
public function get_id() {
|
||||
$class = get_class($this);
|
||||
// UpdraftPlus_BackupModule_
|
||||
return substr($class, 25);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the backup method description for this class
|
||||
*
|
||||
* @return String - the identifier
|
||||
*/
|
||||
public function get_description() {
|
||||
global $updraftplus;
|
||||
|
||||
$methods = $updraftplus->backup_methods;
|
||||
|
||||
$id = $this->get_id();
|
||||
|
||||
return isset($methods[$id]) ? $methods[$id] : $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the instance ID - for supporting multi_options
|
||||
*
|
||||
* @param String $instance_id - the instance ID
|
||||
*/
|
||||
public function set_instance_id($instance_id) {
|
||||
$this->_instance_id = $instance_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the instance ID - for supporting multi_options
|
||||
*
|
||||
* @returns String the instance ID
|
||||
*/
|
||||
public function get_instance_id() {
|
||||
return $this->_instance_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this storage module supports a mentioned feature
|
||||
*
|
||||
* @param String $feature - the feature concerned
|
||||
*
|
||||
* @returns Boolean
|
||||
*/
|
||||
public function supports_feature($feature) {
|
||||
return in_array($feature, $this->get_supported_features());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve options for this remote storage module.
|
||||
* N.B. The option name instance_id is reserved and should not be used.
|
||||
*
|
||||
* @uses get_default_options
|
||||
*
|
||||
* @return Array - array of options. This will include default values for any options not set.
|
||||
*/
|
||||
public function get_options() {
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
$supports_multi_options = $this->supports_feature('multi_options');
|
||||
|
||||
if (is_array($this->_options)) {
|
||||
// First, prioritise any options that were explicitly set. This is the eventual goal for all storage modules.
|
||||
$options = $this->_options;
|
||||
|
||||
} elseif (is_callable(array($this, 'get_opts'))) {
|
||||
// Next, get any options available via a legacy / over-ride method.
|
||||
|
||||
if ($supports_multi_options) {
|
||||
// This is forbidden, because get_opts() is legacy and is for methods that do not support multi-options. Supporting multi-options leads to the array format being updated, which will then break get_opts().
|
||||
die('Fatal error: method '.$this->get_id().' both supports multi_options and provides a get_opts method');
|
||||
}
|
||||
|
||||
$options = $this->get_opts();
|
||||
|
||||
} else {
|
||||
|
||||
// Next, look for job options (which in turn, falls back to saved settings if no job options were set)
|
||||
|
||||
$options = $updraftplus->get_job_option('updraft_'.$this->get_id());
|
||||
if (!is_array($options)) $options = array();
|
||||
|
||||
if ($supports_multi_options) {
|
||||
|
||||
if (!isset($options['version'])) {
|
||||
$options_full = UpdraftPlus_Storage_Methods_Interface::update_remote_storage_options_format($this->get_id());
|
||||
|
||||
if (is_wp_error($options_full)) {
|
||||
$updraftplus->log("Options retrieval failure: ".$options_full->get_error_code().": ".$options_full->get_error_message()." (".json_encode($options_full->get_error_data()).")");
|
||||
return array();
|
||||
}
|
||||
|
||||
} else {
|
||||
$options_full = $options;
|
||||
}
|
||||
|
||||
// UpdraftPlus_BackupModule::get_options() is for getting the current instance's options. So, this branch (going via the job option) is a legacy route, and hence we just give back the first one. The non-legacy route is to call the set_options() method externally.
|
||||
$options = reset($options_full['settings']);
|
||||
|
||||
if (false === $options) {
|
||||
$updraftplus->log("Options retrieval failure (no options set)");
|
||||
return array();
|
||||
}
|
||||
$instance_id = key($options_full['settings']);
|
||||
$this->set_options($options, false, $instance_id);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$options = apply_filters(
|
||||
'updraftplus_backupmodule_get_options',
|
||||
wp_parse_args($options, $this->get_default_options()),
|
||||
$this
|
||||
);
|
||||
|
||||
return $options;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Set job data that is local to this storage instance
|
||||
* (i.e. the key does not need to be unique across instances)
|
||||
*
|
||||
* @uses UpdraftPlus::jobdata_set()
|
||||
*
|
||||
* @param String $key - the key for the job data
|
||||
* @param Mixed $value - the data to be stored
|
||||
*/
|
||||
public function jobdata_set($key, $value) {
|
||||
|
||||
$instance_key = $this->get_id().'-'.($this->_instance_id ? $this->_instance_id : 'no_instance');
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
$instance_data = $updraftplus->jobdata_get($instance_key);
|
||||
|
||||
if (!is_array($instance_data)) $instance_data = array();
|
||||
|
||||
$instance_data[$key] = $value;
|
||||
|
||||
$updraftplus->jobdata_set($instance_key, $instance_data);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get job data that is local to this storage instance
|
||||
* (i.e. the key does not need to be unique across instances)
|
||||
*
|
||||
* @uses UpdraftPlus::jobdata_get()
|
||||
*
|
||||
* @param String $key - the key for the job data
|
||||
* @param Mixed $default - the default to return if nothing was set
|
||||
* @param String|Null $legacy_key - the previous name of the key, prior to instance-specific job data (so that upgrades across versions whilst a backup is in progress can still find its data). In future, support for this can be removed.
|
||||
*/
|
||||
public function jobdata_get($key, $default = null, $legacy_key = null) {
|
||||
|
||||
$instance_key = $this->get_id().'-'.($this->_instance_id ? $this->_instance_id : 'no_instance');
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
$instance_data = $updraftplus->jobdata_get($instance_key);
|
||||
|
||||
if (is_array($instance_data) && isset($instance_data[$key])) return $instance_data[$key];
|
||||
|
||||
return is_string($legacy_key) ? $updraftplus->jobdata_get($legacy_key, $default) : $default;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete job data that is local to this storage instance
|
||||
* (i.e. the key does not need to be unique across instances)
|
||||
*
|
||||
* @uses UpdraftPlus::jobdata_set()
|
||||
*
|
||||
* @param String $key - the key for the job data
|
||||
* @param String|Null $legacy_key - the previous name of the key, prior to instance-specific job data (so that upgrades across versions whilst a backup is in progress can still find its data)
|
||||
*/
|
||||
public function jobdata_delete($key, $legacy_key = null) {
|
||||
|
||||
$instance_key = $this->get_id().'-'.($this->_instance_id ? $this->_instance_id : 'no_instance');
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
$instance_data = $updraftplus->jobdata_get($instance_key);
|
||||
|
||||
if (is_array($instance_data) && isset($instance_data[$key])) {
|
||||
unset($instance_data[$key]);
|
||||
$updraftplus->jobdata_set($instance_key, $instance_data);
|
||||
}
|
||||
|
||||
if (is_string($legacy_key)) $updraftplus->jobdata_delete($legacy_key);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will either return or echo the constructed auth link for the remote storage method
|
||||
*
|
||||
* @param Boolean $echo_instead_of_return - a boolean to indicate if the authentication link should be echo or returned
|
||||
* @param Boolean $template_instead_of_notice - a boolean to indicate if the authentication link is for a template or a notice
|
||||
* @return Void|String - returns a string or nothing depending on the parameters
|
||||
*/
|
||||
public function get_authentication_link($echo_instead_of_return = true, $template_instead_of_notice = true) {
|
||||
if (!$echo_instead_of_return) {
|
||||
ob_start();
|
||||
}
|
||||
|
||||
$account_warning = '';
|
||||
$id = $this->get_id();
|
||||
$description = $this->get_description();
|
||||
|
||||
if ($this->output_account_warning()) {
|
||||
$account_warning = __('Ensure you are logged into the correct account before continuing.', 'updraftplus');
|
||||
}
|
||||
|
||||
if ($template_instead_of_notice) {
|
||||
$instance_id = "{{instance_id}}";
|
||||
$text = sprintf(__("<strong>After</strong> you have saved your settings (by clicking 'Save Changes' below), then come back here once and click this link to complete authentication with %s.", 'updraftplus'), $description);
|
||||
} else {
|
||||
$instance_id = $this->get_instance_id();
|
||||
$text = sprintf(__('Follow this link to authorize access to your %s account (you will not be able to backup to %s without it).', 'updraftplus'), $description, $description);
|
||||
}
|
||||
|
||||
echo $account_warning . ' <a class="updraft_authlink" href="'.UpdraftPlus_Options::admin_page_url().'?&action=updraftmethod-'.$id.'-auth&page=updraftplus&updraftplus_'.$id.'auth=doit&updraftplus_instance='.$instance_id.'" data-instance_id="'.$instance_id.'" data-remote_method="'.$id.'">'.$text.'</a>';
|
||||
|
||||
if (!$echo_instead_of_return) {
|
||||
return ob_get_clean();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the authentication is valid before proceeding to call the authentication method
|
||||
*/
|
||||
public function action_authenticate_storage() {
|
||||
if (isset($_GET['updraftplus_'.$this->get_id().'auth']) && 'doit' == $_GET['updraftplus_'.$this->get_id().'auth'] && !empty($_GET['updraftplus_instance'])) {
|
||||
$this->authenticate_storage((string) $_GET['updraftplus_instance']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate the remote storage and save settings
|
||||
*
|
||||
* @param String $instance_id - The remote storage instance id
|
||||
*/
|
||||
public function authenticate_storage($instance_id) {
|
||||
if (method_exists($this, 'do_authenticate_storage')) {
|
||||
$this->do_authenticate_storage($instance_id);
|
||||
} else {
|
||||
error_log($this->get_id().": module does not have an authenticate storage method (coding bug)");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will either return or echo the constructed deauth link for the remote storage method
|
||||
*
|
||||
* @param boolean $echo_instead_of_return - a boolean to indicate if the deauthentication link should be echo or returned
|
||||
* @return Void|String - returns a string or nothing depending on the parameters
|
||||
*/
|
||||
public function get_deauthentication_link($echo_instead_of_return = true) {
|
||||
if (!$echo_instead_of_return) {
|
||||
ob_start();
|
||||
}
|
||||
|
||||
$id = $this->get_id();
|
||||
$description = $this->get_description();
|
||||
|
||||
echo ' <a class="updraft_deauthlink" href="'.UpdraftPlus_Options::admin_page_url().'?action=updraftmethod-'.$id.'-auth&page=updraftplus&updraftplus_'.$id.'auth=deauth&nonce='.wp_create_nonce($id.'_deauth_nonce').'&updraftplus_instance={{instance_id}}" data-instance_id="{{instance_id}}" data-remote_method="'.$id.'">'.sprintf(__("Follow this link to remove these settings for %s.", 'updraftplus'), $description).'</a>';
|
||||
|
||||
if (!$echo_instead_of_return) {
|
||||
return ob_get_clean();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the deauthentication is valid before proceeding to call the deauthentication method
|
||||
*/
|
||||
public function action_deauthenticate_storage() {
|
||||
if (isset($_GET['updraftplus_'.$this->get_id().'auth']) && 'deauth' == $_GET['updraftplus_'.$this->get_id().'auth'] && !empty($_GET['nonce']) && !empty($_GET['updraftplus_instance']) && wp_verify_nonce($_GET['nonce'], $this->get_id().'_deauth_nonce')) {
|
||||
$this->deauthenticate_storage($_GET['updraftplus_instance']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deauthenticate the remote storage and remove the saved settings
|
||||
*
|
||||
* @param String $instance_id - The remote storage instance id
|
||||
*/
|
||||
public function deauthenticate_storage($instance_id) {
|
||||
if (method_exists($this, 'do_deauthenticate_storage')) {
|
||||
$this->do_deauthenticate_storage($instance_id);
|
||||
}
|
||||
$opts = $this->get_default_options();
|
||||
$this->set_options($opts, true, $instance_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Over-ride this to allow methods to output extra information about using the correct account for OAuth storage methods
|
||||
*
|
||||
* @return Boolean - return false so that no extra information is output
|
||||
*/
|
||||
public function output_account_warning() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is a wrapper and will call $updraftplus->log(), the backup modules should use this so we can add information to the log lines to do with the remote storage and instance settings.
|
||||
*
|
||||
* @param string $line - the log line
|
||||
* @param string $level - the log level: notice, warning, error. If suffixed with a hypen and a destination, then the default destination is changed too.
|
||||
* @param boolean $uniq_id - each of these will only be logged once
|
||||
* @param boolean $skip_dblog - if true, then do not write to the database
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function log($line, $level = 'notice', $uniq_id = false, $skip_dblog = false) {
|
||||
global $updraftplus;
|
||||
|
||||
$prefix = $this->get_storage_label();
|
||||
|
||||
$updraftplus->log("$prefix: $line", $level, $uniq_id = false, $skip_dblog = false);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will build and return the remote storage instance label
|
||||
*
|
||||
* @return string - the remote storage instance label
|
||||
*/
|
||||
private function get_storage_label() {
|
||||
|
||||
$opts = $this->get_options();
|
||||
$label = isset($opts['instance_label']) ? $opts['instance_label'] : '';
|
||||
|
||||
$description = $this->get_description();
|
||||
|
||||
if (!empty($label)) {
|
||||
$prefix = (false !== strpos($label, $description)) ? $label : "$description: $label";
|
||||
} else {
|
||||
$prefix = $description;
|
||||
}
|
||||
|
||||
return $prefix;
|
||||
}
|
||||
}
|
||||
214
wp-content/plugins/updraftplus/methods/cloudfiles-new.php
Normal file
214
wp-content/plugins/updraftplus/methods/cloudfiles-new.php
Normal file
@@ -0,0 +1,214 @@
|
||||
<?php
|
||||
|
||||
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed.');
|
||||
|
||||
// SDK uses namespacing - requires PHP 5.3 (actually the SDK states its requirements as 5.3.3)
|
||||
use OpenCloud\Rackspace;
|
||||
|
||||
// New SDK - https://github.com/rackspace/php-opencloud and http://docs.rackspace.com/sdks/guide/content/php.html
|
||||
// Uploading: https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/ObjectStore/Storage/Object.md
|
||||
|
||||
require_once(UPDRAFTPLUS_DIR.'/methods/openstack-base.php');
|
||||
|
||||
class UpdraftPlus_BackupModule_cloudfiles_opencloudsdk extends UpdraftPlus_BackupModule_openstack_base {
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct('cloudfiles', 'Cloud Files', 'Rackspace Cloud Files', '/images/rackspacecloud-logo.png');
|
||||
}
|
||||
|
||||
public function get_client() {
|
||||
return $this->client;
|
||||
}
|
||||
|
||||
public function get_openstack_service($opts, $useservercerts = false, $disablesslverify = null) {
|
||||
|
||||
$user = $opts['user'];
|
||||
$apikey = $opts['apikey'];
|
||||
$authurl = $opts['authurl'];
|
||||
$region = (!empty($opts['region'])) ? $opts['region'] : null;
|
||||
|
||||
include_once(UPDRAFTPLUS_DIR.'/vendor/autoload.php');
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
// The new authentication APIs don't match the values we were storing before
|
||||
$new_authurl = ('https://lon.auth.api.rackspacecloud.com' == $authurl || 'uk' == $authurl) ? Rackspace::UK_IDENTITY_ENDPOINT : Rackspace::US_IDENTITY_ENDPOINT;
|
||||
|
||||
if (null === $disablesslverify) $disablesslverify = UpdraftPlus_Options::get_updraft_option('updraft_ssl_disableverify');
|
||||
|
||||
if (empty($user) || empty($apikey)) throw new Exception(__('Authorisation failed (check your credentials)', 'updraftplus'));
|
||||
|
||||
$this->log("authentication URL: ".$new_authurl);
|
||||
|
||||
$client = new Rackspace($new_authurl, array(
|
||||
'username' => $user,
|
||||
'apiKey' => $apikey
|
||||
));
|
||||
$this->client = $client;
|
||||
|
||||
if ($disablesslverify) {
|
||||
$client->setSslVerification(false);
|
||||
} else {
|
||||
if ($useservercerts) {
|
||||
$client->setConfig(array($client::SSL_CERT_AUTHORITY, 'system'));
|
||||
} else {
|
||||
$client->setSslVerification(UPDRAFTPLUS_DIR.'/includes/cacert.pem', true, 2);
|
||||
}
|
||||
}
|
||||
|
||||
return $client->objectStoreService('cloudFiles', $region);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method overrides the parent method and lists the supported features of this remote storage option.
|
||||
*
|
||||
* @return Array - an array of supported features (any features not
|
||||
* mentioned are assumed to not be supported)
|
||||
*/
|
||||
public function get_supported_features() {
|
||||
// This options format is handled via only accessing options via $this->get_options()
|
||||
return array('multi_options', 'config_templates', 'multi_storage');
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve default options for this remote storage module.
|
||||
*
|
||||
* @return Array - an array of options
|
||||
*/
|
||||
public function get_default_options() {
|
||||
return array(
|
||||
'user' => '',
|
||||
'authurl' => 'https://auth.api.rackspacecloud.com',
|
||||
'apikey' => '',
|
||||
'path' => '',
|
||||
'region' => null
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the pre configuration template
|
||||
*
|
||||
* @return String - the template
|
||||
*/
|
||||
public function get_pre_configuration_middlesection_template() {
|
||||
|
||||
global $updraftplus_admin;
|
||||
|
||||
$classes = $this->get_css_classes();
|
||||
|
||||
if (!function_exists('json_last_error')) {
|
||||
$updraftplus_admin->show_double_warning('<strong>'.__('Warning', 'updraftplus').':</strong> '.sprintf(__('Your web server\'s PHP installation does not included a required module (%s). Please contact your web hosting provider\'s support.', 'updraftplus'), 'json').' '.sprintf(__("UpdraftPlus's %s module <strong>requires</strong> %s. Please do not file any support requests; there is no alternative.", 'updraftplus'), 'Cloud Files', 'json'), 'cloudfiles', false);
|
||||
}
|
||||
echo '<p>' . __('Get your API key <a href="https://mycloud.rackspace.com/" target="_blank">from your Rackspace Cloud console</a> (read instructions <a href="http://www.rackspace.com/knowledge_center/article/rackspace-cloud-essentials-1-generating-your-api-key" target="_blank">here</a>), then pick a container name to use for storage. This container will be created for you if it does not already exist.', 'updraftplus').' <a href="'.apply_filters("updraftplus_com_link", "https://updraftplus.com/faqs/there-appear-to-be-lots-of-extra-files-in-my-rackspace-cloud-files-container/").'" target="_blank">'.__('Also, you should read this important FAQ.', 'updraftplus').'</a></p>';
|
||||
}
|
||||
|
||||
/**
|
||||
* This gives the partial template string to the settings page for the CloudFiles settings.
|
||||
*
|
||||
* @return String - the partial template, ready for substitutions to be carried out
|
||||
*/
|
||||
public function get_configuration_middlesection_template() {
|
||||
global $updraftplus_admin;
|
||||
$classes = $this->get_css_classes();
|
||||
$template_str = '
|
||||
<tr class="'.$classes.'">
|
||||
<th title="'.__('Accounts created at rackspacecloud.com are US accounts; accounts created at rackspace.co.uk are UK accounts.', 'updraftplus').'">'.__('US or UK-based Rackspace Account', 'updraftplus').':</th>
|
||||
<td>
|
||||
<select data-updraft_settings_test="authurl" '.$this->output_settings_field_name_and_id('authurl', true).' title="'.__('Accounts created at rackspacecloud.com are US-accounts; accounts created at rackspace.co.uk are UK-based', 'updraftplus').'">
|
||||
<option {{#ifeq "https://auth.api.rackspacecloud.com" authurl}}selected="selected"{{/ifeq}} value="https://auth.api.rackspacecloud.com">'.__('US (default)', 'updraftplus').'</option>
|
||||
<option {{#ifeq "https://lon.auth.api.rackspacecloud.com" authurl}}selected="selected"{{/ifeq}} value="https://lon.auth.api.rackspacecloud.com">'.__('UK', 'updraftplus').'</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="'.$classes.'">
|
||||
<th>'.__('Cloud Files Storage Region', 'updraftplus').':</th>
|
||||
<td>
|
||||
<select data-updraft_settings_test="region" '.$this->output_settings_field_name_and_id('region', true).'>
|
||||
{{#each regions as |desc reg|}}
|
||||
<option {{#ifeq ../region reg}}selected="selected"{{/ifeq}} value="{{reg}}">{{desc}}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="'.$classes.'">
|
||||
<th>'.__('Cloud Files Username', 'updraftplus').':</th>
|
||||
<td><input data-updraft_settings_test="user" type="text" autocomplete="off" class="updraft_input--wide" '.$this->output_settings_field_name_and_id('user', true).' value="{{user}}" />
|
||||
<div style="clear:both;">
|
||||
'.apply_filters('updraft_cloudfiles_apikeysetting', '<a href="'.apply_filters("updraftplus_com_link", "https://updraftplus.com/shop/cloudfiles-enhanced/").'" target="_blank"><em>'.__('To create a new Rackspace API sub-user and API key that has access only to this Rackspace container, use this add-on.', 'updraftplus').'</em></a>').'
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="'.$classes.'">
|
||||
<th>'.__('Cloud Files API Key', 'updraftplus').':</th>
|
||||
<td><input data-updraft_settings_test="apikey" type="'.apply_filters('updraftplus_admin_secret_field_type', 'password').'" autocomplete="off" class="updraft_input--wide" '.$this->output_settings_field_name_and_id('apikey', true).' value="{{apikey}}" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="'.$classes.'">
|
||||
<th>'.apply_filters('updraftplus_cloudfiles_location_description', __('Cloud Files Container', 'updraftplus')).':</th>
|
||||
<td><input data-updraft_settings_test="path" type="text" class="updraft_input--wide" '.$this->output_settings_field_name_and_id('path', true).' value="{{path}}" /></td>
|
||||
</tr>';
|
||||
return $template_str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies handerbar template options
|
||||
*
|
||||
* @param array $opts handerbar template options
|
||||
* @return array - Modified handerbar template options
|
||||
*/
|
||||
public function transform_options_for_template($opts) {
|
||||
$opts['regions'] = array(
|
||||
'DFW' => __('Dallas (DFW) (default)', 'updraftplus'),
|
||||
'SYD' => __('Sydney (SYD)', 'updraftplus'),
|
||||
'ORD' => __('Chicago (ORD)', 'updraftplus'),
|
||||
'IAD' => __('Northern Virginia (IAD)', 'updraftplus'),
|
||||
'HKG' => __('Hong Kong (HKG)', 'updraftplus'),
|
||||
'LON' => __('London (LON)', 'updraftplus')
|
||||
);
|
||||
$opts['region'] = (empty($opts['region'])) ? 'DFW' : $opts['region'];
|
||||
if (isset($opts['apikey'])) {
|
||||
$opts['apikey'] = trim($opts['apikey']);
|
||||
}
|
||||
$opts['authurl'] = !empty($opts['authurl']) ? $opts['authurl'] : '';
|
||||
return $opts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a test of user-supplied credentials, and echo the result
|
||||
*
|
||||
* @param Array $posted_settings - settings to test
|
||||
*/
|
||||
public function credentials_test($posted_settings) {
|
||||
|
||||
if (empty($posted_settings['apikey'])) {
|
||||
printf(__("Failure: No %s was given.", 'updraftplus'), __('API key', 'updraftplus'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($posted_settings['user'])) {
|
||||
printf(__("Failure: No %s was given.", 'updraftplus'), __('Username', 'updraftplus'));
|
||||
return;
|
||||
}
|
||||
|
||||
$opts = array(
|
||||
'user' => $posted_settings['user'],
|
||||
'apikey' => $posted_settings['apikey'],
|
||||
'authurl' => $posted_settings['authurl'],
|
||||
'region' => (empty($posted_settings['region'])) ? null : $posted_settings['region']
|
||||
);
|
||||
|
||||
$this->credentials_test_go($opts, $posted_settings['path'], $posted_settings['useservercerts'], $posted_settings['disableverify']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether options have been set up by the user, or not
|
||||
*
|
||||
* @param Array $opts - the potential options
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
public function options_exist($opts) {
|
||||
if (is_array($opts) && isset($opts['user']) && '' != $opts['user'] && !empty($opts['apikey'])) return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
589
wp-content/plugins/updraftplus/methods/cloudfiles.php
Normal file
589
wp-content/plugins/updraftplus/methods/cloudfiles.php
Normal file
@@ -0,0 +1,589 @@
|
||||
<?php
|
||||
|
||||
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access.');
|
||||
|
||||
/**
|
||||
* Converted to job_options: yes
|
||||
* Converted to array options: yes
|
||||
* Migration code for "new"-style options removed: Feb 2017 (created: Dec 2013)
|
||||
*/
|
||||
|
||||
if (!class_exists('UpdraftPlus_BackupModule')) require_once(UPDRAFTPLUS_DIR.'/methods/backup-module.php');
|
||||
|
||||
/**
|
||||
* Old SDK
|
||||
*/
|
||||
class UpdraftPlus_BackupModule_cloudfiles_oldsdk extends UpdraftPlus_BackupModule {
|
||||
|
||||
/**
|
||||
* This function does not catch any exceptions - that should be done by the caller
|
||||
*
|
||||
* @param string $user
|
||||
* @param string $apikey
|
||||
* @param string $authurl
|
||||
* @param boolean $useservercerts
|
||||
* @return array
|
||||
*/
|
||||
private function getCF($user, $apikey, $authurl, $useservercerts = false) {
|
||||
|
||||
$storage = $this->get_storage();
|
||||
if (!empty($storage)) return $storage;
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
if (!class_exists('UpdraftPlus_CF_Authentication')) include_once(UPDRAFTPLUS_DIR.'/includes/cloudfiles/cloudfiles.php');
|
||||
|
||||
if (!defined('UPDRAFTPLUS_SSL_DISABLEVERIFY')) define('UPDRAFTPLUS_SSL_DISABLEVERIFY', UpdraftPlus_Options::get_updraft_option('updraft_ssl_disableverify'));
|
||||
|
||||
$auth = new UpdraftPlus_CF_Authentication($user, trim($apikey), null, $authurl);
|
||||
|
||||
$this->log("authentication URL: $authurl");
|
||||
|
||||
$auth->authenticate();
|
||||
|
||||
$storage = new UpdraftPlus_CF_Connection($auth);
|
||||
|
||||
if (!$useservercerts) $storage->ssl_use_cabundle(UPDRAFTPLUS_DIR.'/includes/cacert.pem');
|
||||
|
||||
$this->set_storage($storage);
|
||||
|
||||
return $storage;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method overrides the parent method and lists the supported features of this remote storage option.
|
||||
*
|
||||
* @return Array - an array of supported features (any features not
|
||||
* mentioned are assumed to not be supported)
|
||||
*/
|
||||
public function get_supported_features() {
|
||||
// This options format is handled via only accessing options via $this->get_options()
|
||||
return array('multi_options', 'config_templates', 'multi_storage');
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve default options for this remote storage module.
|
||||
*
|
||||
* @return Array - an array of options
|
||||
*/
|
||||
public function get_default_options() {
|
||||
return array(
|
||||
'user' => '',
|
||||
'authurl' => 'https://auth.api.rackspacecloud.com',
|
||||
'apikey' => '',
|
||||
'path' => '',
|
||||
'region' => null
|
||||
);
|
||||
}
|
||||
|
||||
public function backup($backup_array) {
|
||||
|
||||
global $updraftplus, $updraftplus_backup;
|
||||
|
||||
$opts = $this->get_options();
|
||||
|
||||
$updraft_dir = $updraftplus->backups_dir_location().'/';
|
||||
|
||||
$container = $opts['path'];
|
||||
|
||||
try {
|
||||
$storage = $this->getCF($opts['user'], $opts['apikey'], $opts['authurl'], UpdraftPlus_Options::get_updraft_option('updraft_ssl_useservercerts'));
|
||||
$container_object = $storage->create_container($container);
|
||||
} catch (AuthenticationException $e) {
|
||||
$this->log('authentication failed ('.$e->getMessage().')');
|
||||
$this->log(__('authentication failed', 'updraftplus').' ('.$e->getMessage().')', 'error');
|
||||
return false;
|
||||
} catch (NoSuchAccountException $s) {
|
||||
$this->log('authentication failed ('.$e->getMessage().')');
|
||||
$this->log(__('authentication failed', 'updraftplus').' ('.$e->getMessage().')', 'error');
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
$this->log('error - failed to create and access the container ('.$e->getMessage().')');
|
||||
$this->log(__('error - failed to create and access the container', 'updraftplus').' ('.$e->getMessage().')', 'error');
|
||||
return false;
|
||||
}
|
||||
|
||||
$chunk_size = 5*1024*1024;
|
||||
|
||||
foreach ($backup_array as $key => $file) {
|
||||
|
||||
$fullpath = $updraft_dir.$file;
|
||||
$orig_file_size = filesize($fullpath);
|
||||
|
||||
// $cfpath = ($path == '') ? $file : "$path/$file";
|
||||
// $chunk_path = ($path == '') ? "chunk-do-not-delete-$file" : "$path/chunk-do-not-delete-$file";
|
||||
$cfpath = $file;
|
||||
$chunk_path = "chunk-do-not-delete-$file";
|
||||
|
||||
try {
|
||||
$object = new UpdraftPlus_CF_Object($container_object, $cfpath);
|
||||
$object->content_type = "application/zip";
|
||||
|
||||
$uploaded_size = (isset($object->content_length)) ? $object->content_length : 0;
|
||||
|
||||
if ($uploaded_size <= $orig_file_size) {
|
||||
|
||||
$fp = @fopen($fullpath, "rb");
|
||||
if (!$fp) {
|
||||
$this->log("failed to open file: $fullpath");
|
||||
$this->log("$file: ".__('Error: Failed to open local file', 'updraftplus'), 'error');
|
||||
return false;
|
||||
}
|
||||
|
||||
$chunks = floor($orig_file_size / $chunk_size);
|
||||
// There will be a remnant unless the file size was exactly on a 5MB boundary
|
||||
if ($orig_file_size % $chunk_size > 0) $chunks++;
|
||||
|
||||
$this->log("upload: $file (chunks: $chunks) -> cloudfiles://$container/$cfpath ($uploaded_size)");
|
||||
|
||||
if ($chunks < 2) {
|
||||
try {
|
||||
$object->load_from_filename($fullpath);
|
||||
$this->log("regular upload: success");
|
||||
$updraftplus->uploaded_file($file);
|
||||
} catch (Exception $e) {
|
||||
$this->log("regular upload: failed ($file) (".$e->getMessage().")");
|
||||
$this->log("$file: ".__('Error: Failed to upload', 'updraftplus'), 'error');
|
||||
}
|
||||
} else {
|
||||
$errors_so_far = 0;
|
||||
for ($i = 1; $i <= $chunks; $i++) {
|
||||
$upload_start = ($i-1)*$chunk_size;
|
||||
// The file size -1 equals the byte offset of the final byte
|
||||
$upload_end = min($i*$chunk_size-1, $orig_file_size-1);
|
||||
$upload_remotepath = $chunk_path."_$i";
|
||||
// Don't forget the +1; otherwise the last byte is omitted
|
||||
$upload_size = $upload_end - $upload_start + 1;
|
||||
$chunk_object = new UpdraftPlus_CF_Object($container_object, $upload_remotepath);
|
||||
$chunk_object->content_type = "application/zip";
|
||||
// Without this, some versions of Curl add Expect: 100-continue, which results in Curl then giving this back: curl error: 55) select/poll returned error
|
||||
// Didn't make the difference - instead we just check below for actual success even when Curl reports an error
|
||||
// $chunk_object->headers = array('Expect' => '');
|
||||
|
||||
$remote_size = (isset($chunk_object->content_length)) ? $chunk_object->content_length : 0;
|
||||
|
||||
if ($remote_size >= $upload_size) {
|
||||
$this->log("Chunk $i ($upload_start - $upload_end): already uploaded");
|
||||
} else {
|
||||
$this->log("Chunk $i ($upload_start - $upload_end): begin upload");
|
||||
// Upload the chunk
|
||||
fseek($fp, $upload_start);
|
||||
try {
|
||||
$chunk_object->write($fp, $upload_size, false);
|
||||
$updraftplus->record_uploaded_chunk(round(100*$i/$chunks, 1), $i, $fullpath);
|
||||
} catch (Exception $e) {
|
||||
$this->log("chunk upload: error: ($file / $i) (".$e->getMessage().")");
|
||||
// Experience shows that Curl sometimes returns a select/poll error (curl error 55) even when everything succeeded. Google seems to indicate that this is a known bug.
|
||||
|
||||
$chunk_object = new UpdraftPlus_CF_Object($container_object, $upload_remotepath);
|
||||
$chunk_object->content_type = "application/zip";
|
||||
$remote_size = (isset($chunk_object->content_length)) ? $chunk_object->content_length : 0;
|
||||
|
||||
if ($remote_size >= $upload_size) {
|
||||
|
||||
$this->log("$file: Chunk now exists; ignoring error (presuming it was an apparently known curl bug)");
|
||||
|
||||
} else {
|
||||
|
||||
$this->log("$file: ".__('Error: Failed to upload', 'updraftplus'), 'error');
|
||||
$errors_so_far++;
|
||||
if ($errors_so_far >=3) return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($errors_so_far) return false;
|
||||
// All chunks are uploaded - now upload the manifest
|
||||
|
||||
try {
|
||||
$object->manifest = $container."/".$chunk_path."_";
|
||||
// Put a zero-length file
|
||||
$object->write("", 0, false);
|
||||
$object->sync_manifest();
|
||||
$this->log("upload: success");
|
||||
$updraftplus->uploaded_file($file);
|
||||
// } catch (InvalidResponseException $e) {
|
||||
} catch (Exception $e) {
|
||||
$this->log('error - failed to re-assemble chunks ('.$e->getMessage().')');
|
||||
$this->log(__('error - failed to re-assemble chunks', 'updraftplus').' ('.$e->getMessage().')', 'error');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
$this->log(__('error - failed to upload file', 'updraftplus').' ('.$e->getMessage().')');
|
||||
$this->log(__('error - failed to upload file', 'updraftplus').' ('.$e->getMessage().')', 'error');
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return array('cloudfiles_object' => $container_object, 'cloudfiles_orig_path' => $opts['path'], 'cloudfiles_container' => $container);
|
||||
|
||||
}
|
||||
|
||||
public function listfiles($match = 'backup_') {
|
||||
|
||||
$opts = $this->get_options();
|
||||
$container = $opts['path'];
|
||||
|
||||
if (empty($opts['user']) || empty($opts['apikey'])) new WP_Error('no_settings', __('No settings were found', 'updraftplus'));
|
||||
|
||||
try {
|
||||
$storage = $this->getCF($opts['user'], $opts['apikey'], $opts['authurl'], UpdraftPlus_Options::get_updraft_option('updraft_ssl_useservercerts'));
|
||||
$container_object = $storage->create_container($container);
|
||||
} catch (Exception $e) {
|
||||
return new WP_Error('no_access', sprintf(__('%s authentication failed', 'updraftplus'), 'Cloud Files').' ('.$e->getMessage().')');
|
||||
}
|
||||
|
||||
$results = array();
|
||||
|
||||
try {
|
||||
$objects = $container_object->list_objects(0, null, $match);
|
||||
foreach ($objects as $name) {
|
||||
$result = array('name' => $name);
|
||||
try {
|
||||
$object = new UpdraftPlus_CF_Object($container_object, $name, true);
|
||||
if (0 == $object->content_length) {
|
||||
$result = false;
|
||||
} else {
|
||||
$result['size'] = $object->content_length;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// Catch
|
||||
}
|
||||
if (is_array($result)) $results[] = $result;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
return new WP_Error('cf_error', 'Cloud Files error ('.$e->getMessage().')');
|
||||
}
|
||||
|
||||
return $results;
|
||||
|
||||
}
|
||||
|
||||
public function delete($files, $cloudfilesarr = false, $sizeinfo = array()) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
|
||||
|
||||
global $updraftplus;
|
||||
if (is_string($files)) $files =array($files);
|
||||
|
||||
if ($cloudfilesarr) {
|
||||
$container_object = $cloudfilesarr['cloudfiles_object'];
|
||||
$container = $cloudfilesarr['cloudfiles_container'];
|
||||
$path = $cloudfilesarr['cloudfiles_orig_path'];
|
||||
} else {
|
||||
try {
|
||||
$opts = $this->get_options();
|
||||
$container = $opts['path'];
|
||||
$storage = $this->getCF($opts['user'], $opts['apikey'], $opts['authurl'], UpdraftPlus_Options::get_updraft_option('updraft_ssl_useservercerts'));
|
||||
$container_object = $storage->create_container($container);
|
||||
} catch (Exception $e) {
|
||||
$this->log('authentication failed ('.$e->getMessage().')');
|
||||
$this->log(__('authentication failed', 'updraftplus').' ('.$e->getMessage().')', 'error');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$ret = true;
|
||||
foreach ($files as $file) {
|
||||
|
||||
$fpath = $file;
|
||||
|
||||
$this->log("Delete remote: container=$container, path=$fpath");
|
||||
|
||||
// We need to search for chunks
|
||||
// $chunk_path = ($path == '') ? "chunk-do-not-delete-$file_" : "$path/chunk-do-not-delete-$file_";
|
||||
$chunk_path = "chunk-do-not-delete-$file";
|
||||
|
||||
try {
|
||||
$objects = $container_object->list_objects(0, null, $chunk_path.'_');
|
||||
foreach ($objects as $chunk) {
|
||||
$this->log('Chunk to delete: '.$chunk);
|
||||
$container_object->delete_object($chunk);
|
||||
$this->log('Chunk deleted: '.$chunk);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->log('chunk delete failed: '.$e->getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
$container_object->delete_object($fpath);
|
||||
$this->log('Deleted: '.$fpath);
|
||||
} catch (Exception $e) {
|
||||
$this->log('delete failed: '.$e->getMessage());
|
||||
$ret = false;
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function download($file) {
|
||||
|
||||
global $updraftplus;
|
||||
$updraft_dir = $updraftplus->backups_dir_location();
|
||||
|
||||
$opts = $this->get_options();
|
||||
|
||||
try {
|
||||
$storage = $this->getCF($opts['user'], $opts['apikey'], $opts['authurl'], UpdraftPlus_Options::get_updraft_option('updraft_ssl_useservercerts'));
|
||||
} catch (AuthenticationException $e) {
|
||||
$this->log('authentication failed ('.$e->getMessage().')');
|
||||
$this->log(__('authentication failed', 'updraftplus').' ('.$e->getMessage().')', 'error');
|
||||
return false;
|
||||
} catch (NoSuchAccountException $s) {
|
||||
$this->log('authentication failed ('.$e->getMessage().')');
|
||||
$this->log(__('authentication failed', 'updraftplus').' ('.$e->getMessage().')', 'error');
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
$this->log('error - failed to create and access the container ('.$e->getMessage().')');
|
||||
$this->log(__('error - failed to create and access the container', 'updraftplus').' ('.$e->getMessage().')', 'error');
|
||||
return false;
|
||||
}
|
||||
|
||||
$path = untrailingslashit($opts['path']);
|
||||
|
||||
$container = $path;
|
||||
|
||||
try {
|
||||
$container_object = $storage->create_container($container);
|
||||
} catch (Exception $e) {
|
||||
$this->log('error - failed to create and access the container ('.$e->getMessage().')');
|
||||
$this->log(__('error - failed to create and access the container', 'updraftplus').' ('.$e->getMessage().')', 'error');
|
||||
return false;
|
||||
}
|
||||
|
||||
$path = $file;
|
||||
|
||||
$this->log("download: cloudfiles://$container/$path");
|
||||
|
||||
try {
|
||||
// The third parameter causes an exception to be thrown if the object does not exist remotely
|
||||
$object = new UpdraftPlus_CF_Object($container_object, $path, true);
|
||||
|
||||
$fullpath = $updraft_dir.'/'.$file;
|
||||
|
||||
$start_offset = (file_exists($fullpath)) ? filesize($fullpath) : 0;
|
||||
|
||||
// Get file size from remote - see if we've already finished
|
||||
|
||||
$remote_size = $object->content_length;
|
||||
|
||||
if ($start_offset >= $remote_size) {
|
||||
$this->log("file is already completely downloaded ($start_offset/$remote_size)");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Some more remains to download - so let's do it
|
||||
if (!$fh = fopen($fullpath, 'a')) {
|
||||
$this->log("Error opening local file: $fullpath");
|
||||
$this->log("$file: ".__('Error opening local file: Failed to download', 'updraftplus'), 'error');
|
||||
return false;
|
||||
}
|
||||
|
||||
$headers = array();
|
||||
// If resuming, then move to the end of the file
|
||||
if ($start_offset) {
|
||||
$this->log("local file is already partially downloaded ($start_offset/$remote_size)");
|
||||
fseek($fh, $start_offset);
|
||||
$headers['Range'] = "bytes=$start_offset-";
|
||||
}
|
||||
|
||||
// Now send the request itself
|
||||
try {
|
||||
$object->stream($fh, $headers);
|
||||
} catch (Exception $e) {
|
||||
$this->log("Failed to download: $file (".$e->getMessage().")");
|
||||
$this->log("$file: ".__('Error downloading remote file: Failed to download', 'updraftplus').' ('.$e->getMessage().")", 'error');
|
||||
return false;
|
||||
}
|
||||
|
||||
// All-in-one-go method:
|
||||
// $object->save_to_filename($fullpath);
|
||||
|
||||
} catch (NoSuchObjectException $e) {
|
||||
$this->log('error - no such file exists. ('.$e->getMessage().')');
|
||||
$this->log(__('Error - no such file exists.', 'updraftplus').' ('.$e->getMessage().')', 'error');
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
$this->log('error - failed to download the file ('.$e->getMessage().')');
|
||||
$this->log(__('Error - failed to download the file', 'updraftplus').' ('.$e->getMessage().')', 'error');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the pre configuration template
|
||||
*
|
||||
* @return String - the template
|
||||
*/
|
||||
public function get_pre_configuration_template() {
|
||||
|
||||
global $updraftplus_admin;
|
||||
|
||||
$classes = $this->get_css_classes(false);
|
||||
|
||||
?>
|
||||
<tr class="<?php echo $classes . ' ' . 'cloudfiles_pre_config_container';?>">
|
||||
<td colspan="2">
|
||||
<img alt="Rackspace Cloud Files" src="<?php echo UPDRAFTPLUS_URL;?>/images/rackspacecloud-logo.png"><br>
|
||||
<?php
|
||||
// Check requirements.
|
||||
global $updraftplus_admin;
|
||||
if (!function_exists('mb_substr')) {
|
||||
$updraftplus_admin->show_double_warning('<strong>'.__('Warning', 'updraftplus').':</strong> '.sprintf(__('Your web server\'s PHP installation does not included a required module (%s). Please contact your web hosting provider\'s support.', 'updraftplus'), 'mbstring').' '.sprintf(__("UpdraftPlus's %s module <strong>requires</strong> %s. Please do not file any support requests; there is no alternative.", 'updraftplus'), 'Cloud Files', 'mbstring'), 'cloudfiles', false);
|
||||
}
|
||||
$updraftplus_admin->curl_check('Rackspace Cloud Files', false, 'cloudfiles', false);
|
||||
?>
|
||||
|
||||
<?php
|
||||
echo '<p>' . __('Get your API key <a href="https://mycloud.rackspace.com/" target="_blank">from your Rackspace Cloud console</a> (read instructions <a href="http://www.rackspace.com/knowledge_center/article/rackspace-cloud-essentials-1-generating-your-api-key" target="_blank">here</a>), then pick a container name to use for storage. This container will be created for you if it does not already exist.', 'updraftplus').' <a href="https://updraftplus.com/faqs/there-appear-to-be-lots-of-extra-files-in-my-rackspace-cloud-files-container/" target="_blank">'.__('Also, you should read this important FAQ.', 'updraftplus').'</a></p>';
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configuration template
|
||||
*
|
||||
* @return String - the template, ready for substitutions to be carried out
|
||||
*/
|
||||
public function get_configuration_template() {
|
||||
$classes = $this->get_css_classes();
|
||||
$template_str = '
|
||||
<tr class="'.$classes.'">
|
||||
<th>'.__('US or UK Cloud', 'updraftplus').':</th>
|
||||
<td>
|
||||
<select data-updraft_settings_test="authurl" '.$this->output_settings_field_name_and_id('authurl', true).'>
|
||||
<option {{#ifeq "https://auth.api.rackspacecloud.com" authurl}}selected="selected"{{/ifeq}} value="https://auth.api.rackspacecloud.com">'.__('US (default)', 'updraftplus').'</option>
|
||||
<option {{#ifeq "https://lon.auth.api.rackspacecloud.com" authurl}}selected="selected"{{/ifeq}} value="https://lon.auth.api.rackspacecloud.com">'.__('UK', 'updraftplus').'</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<input type="hidden" data-updraft_settings_test="region" '.$this->output_settings_field_name_and_id('region', true).' value="">';
|
||||
|
||||
/*
|
||||
// Can put a message here if someone asks why region storage is not available (only available on new SDK)
|
||||
<tr class="updraftplusmethod cloudfiles">
|
||||
<th><?php _e('Rackspace Storage Region','updraftplus');?>:</th>
|
||||
<td>
|
||||
</td>
|
||||
</tr>
|
||||
*/
|
||||
$template_str .= '
|
||||
<tr class="'.$classes.'">
|
||||
<th>'.__('Cloud Files username', 'updraftplus').':</th>
|
||||
<td><input data-updraft_settings_test="user" type="text" autocomplete="off" class="updraft_input--wide" '.$this->output_settings_field_name_and_id('user', true).' value="{{user}}" /></td>
|
||||
</tr>
|
||||
<tr class="'.$classes.'">
|
||||
<th>'.__('Cloud Files API Key', 'updraftplus').':</th>
|
||||
<td><input data-updraft_settings_test="apikey" type="'.apply_filters('updraftplus_admin_secret_field_type', 'password').'" autocomplete="off" class="updraft_input--wide" '.$this->output_settings_field_name_and_id('apikey', true).' value="{{apikey}}" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="'.$classes.'">
|
||||
<th>'.apply_filters('updraftplus_cloudfiles_location_description', __('Cloud Files Container', 'updraftplus')).':</th>
|
||||
<td><input data-updraft_settings_test="path" type="text" class="updraft_input--wide" '.$this->output_settings_field_name_and_id('path', true).' value="{{path}}" /></td>
|
||||
</tr>';
|
||||
$template_str .= $this->get_test_button_html(__('Cloud Files', 'updraftplus'));
|
||||
return $template_str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies handerbar template options
|
||||
*
|
||||
* @param array $opts handerbar template options
|
||||
* @return array - Modified handerbar template options
|
||||
*/
|
||||
public function transform_options_for_template($opts) {
|
||||
$opts['apikey'] = trim($opts['apikey']);
|
||||
$opts['authurl'] = isset($opts['authurl']) ? $opts['authurl'] : '';
|
||||
return $opts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a test of user-supplied credentials, and echo the result
|
||||
*
|
||||
* @param Array $posted_settings - settings to test
|
||||
*/
|
||||
public function credentials_test($posted_settings) {
|
||||
|
||||
if (empty($posted_settings['apikey'])) {
|
||||
printf(__("Failure: No %s was given.", 'updraftplus'), __('API key', 'updraftplus'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($posted_settings['user'])) {
|
||||
printf(__("Failure: No %s was given.", 'updraftplus'), __('Username', 'updraftplus'));
|
||||
return;
|
||||
}
|
||||
|
||||
$key = $posted_settings['apikey'];
|
||||
$user = $posted_settings['user'];
|
||||
$path = $posted_settings['path'];
|
||||
$authurl = $posted_settings['authurl'];
|
||||
$useservercerts = $posted_settings['useservercerts'];
|
||||
$disableverify = $posted_settings['disableverify'];
|
||||
|
||||
if (preg_match("#^([^/]+)/(.*)$#", $path, $bmatches)) {
|
||||
$container = $bmatches[1];
|
||||
$path = $bmatches[2];
|
||||
} else {
|
||||
$container = $path;
|
||||
$path = "";
|
||||
}
|
||||
|
||||
if (empty($container)) {
|
||||
_e("Failure: No container details were given.", 'updraftplus');
|
||||
return;
|
||||
}
|
||||
|
||||
define('UPDRAFTPLUS_SSL_DISABLEVERIFY', $disableverify);
|
||||
|
||||
try {
|
||||
$storage = $this->getCF($user, $key, $authurl, $useservercerts);
|
||||
$container_object = $storage->create_container($container);
|
||||
} catch (AuthenticationException $e) {
|
||||
echo __('Cloud Files authentication failed', 'updraftplus').' ('.$e->getMessage().')';
|
||||
return;
|
||||
} catch (NoSuchAccountException $s) {
|
||||
echo __('Cloud Files authentication failed', 'updraftplus').' ('.$e->getMessage().')';
|
||||
return;
|
||||
} catch (Exception $e) {
|
||||
echo __('Cloud Files authentication failed', 'updraftplus').' ('.$e->getMessage().')';
|
||||
return;
|
||||
}
|
||||
|
||||
$try_file = md5(rand()).'.txt';
|
||||
|
||||
try {
|
||||
$object = $container_object->create_object($try_file);
|
||||
$object->content_type = "text/plain";
|
||||
$object->write('UpdraftPlus test file');
|
||||
} catch (Exception $e) {
|
||||
echo __('Cloud Files error - we accessed the container, but failed to create a file within it', 'updraftplus').' ('.$e->getMessage().')';
|
||||
return;
|
||||
}
|
||||
|
||||
echo __('Success', 'updraftplus').": ".__('We accessed the container, and were able to create files within it.', 'updraftplus');
|
||||
|
||||
@$container_object->delete_object($try_file);
|
||||
}
|
||||
}
|
||||
|
||||
// Moved to the bottom to fix a bug in some version or install of PHP which required UpdraftPlus_BackupModule_cloudfiles_oldsdk to be defined earlier in the file (despite the conditionality) - see HS#19911
|
||||
if (version_compare(PHP_VERSION, '5.3.3', '>=') && (!defined('UPDRAFTPLUS_CLOUDFILES_USEOLDSDK') || UPDRAFTPLUS_CLOUDFILES_USEOLDSDK != true)) {
|
||||
include_once(UPDRAFTPLUS_DIR.'/methods/cloudfiles-new.php');
|
||||
class UpdraftPlus_BackupModule_cloudfiles extends UpdraftPlus_BackupModule_cloudfiles_opencloudsdk {
|
||||
}
|
||||
} else {
|
||||
class UpdraftPlus_BackupModule_cloudfiles extends UpdraftPlus_BackupModule_cloudfiles_oldsdk {
|
||||
}
|
||||
}
|
||||
141
wp-content/plugins/updraftplus/methods/dreamobjects.php
Normal file
141
wp-content/plugins/updraftplus/methods/dreamobjects.php
Normal file
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
|
||||
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed.');
|
||||
|
||||
require_once(UPDRAFTPLUS_DIR.'/methods/s3.php');
|
||||
|
||||
/**
|
||||
* Converted to multi-options (Feb 2017-) and previous options conversion removed: Yes
|
||||
*/
|
||||
class UpdraftPlus_BackupModule_dreamobjects extends UpdraftPlus_BackupModule_s3 {
|
||||
|
||||
private $dreamobjects_endpoints = array();
|
||||
|
||||
public function __construct() {
|
||||
// When new endpoint introduced in future, Please add it here and also add it as hard coded option for endpoint dropdown in self::get_partial_configuration_template_for_endpoint()
|
||||
// Put the default first
|
||||
$this->dreamobjects_endpoints = array(
|
||||
// Endpoint, then the label
|
||||
'objects-us-east-1.dream.io' => 'objects-us-east-1.dream.io',
|
||||
'objects-us-west-1.dream.io' => 'objects-us-west-1.dream.io ('.__('Closing 1st October 2018', 'updraftplus').')',
|
||||
);
|
||||
}
|
||||
|
||||
protected $use_v4 = false;
|
||||
|
||||
protected function set_region($obj, $region = '', $bucket_name = '') {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
|
||||
$config = $this->get_config();
|
||||
$endpoint = ('' != $region && 'n/a' != $region) ? $region : $config['endpoint'];
|
||||
global $updraftplus;
|
||||
if ($updraftplus->backup_time) {
|
||||
$updraftplus->log("Set endpoint: $endpoint");
|
||||
|
||||
// Warning for objects-us-west-1 shutdown in Oct 2018
|
||||
if ('objects-us-west-1.dream.io' == $endpoint) {
|
||||
// Are we after the shutdown date?
|
||||
if (time() >= 1538438400) {
|
||||
$updraftplus->log("The objects-us-west-1.dream.io endpoint shut down on the 1st October 2018. The upload is expected to fail. Please see the following article for more information https://help.dreamhost.com/hc/en-us/articles/360002135871-Cluster-migration-procedure", 'warning', 'dreamobjects_west_shutdown');
|
||||
} else {
|
||||
$updraftplus->log("The objects-us-west-1.dream.io endpoint is scheduled to shut down on the 1st October 2018. You will need to switch to a different end-point and migrate your data before that date. Please see the following article for more information https://help.dreamhost.com/hc/en-us/articles/360002135871-Cluster-migration-procedure", 'warning', 'dreamobjects_west_shutdown');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$obj->setEndpoint($endpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method overrides the parent method and lists the supported features of this remote storage option.
|
||||
*
|
||||
* @return Array - an array of supported features (any features not mentioned are asuumed to not be supported)
|
||||
*/
|
||||
public function get_supported_features() {
|
||||
// This options format is handled via only accessing options via $this->get_options()
|
||||
return array('multi_options', 'config_templates', 'multi_storage');
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve default options for this remote storage module.
|
||||
*
|
||||
* @return Array - an array of options
|
||||
*/
|
||||
public function get_default_options() {
|
||||
return array(
|
||||
'accesskey' => '',
|
||||
'secretkey' => '',
|
||||
'path' => '',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve specific options for this remote storage module
|
||||
*
|
||||
* @param Boolean $force_refresh - if set, and if relevant, don't use cached credentials, but get them afresh
|
||||
*
|
||||
* @return Array - an array of options
|
||||
*/
|
||||
protected function get_config($force_refresh = false) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
|
||||
$opts = $this->get_options();
|
||||
$opts['whoweare'] = 'DreamObjects';
|
||||
$opts['whoweare_long'] = 'DreamObjects';
|
||||
$opts['key'] = 'dreamobjects';
|
||||
if (empty($opts['endpoint'])) {
|
||||
$endpoints = array_keys($this->dreamobjects_endpoints);
|
||||
$opts['endpoint'] = $endpoints[0];
|
||||
}
|
||||
return $opts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the pre configuration template
|
||||
*
|
||||
* @return String - the template
|
||||
*/
|
||||
public function get_pre_configuration_template() {
|
||||
$this->get_pre_configuration_template_engine('dreamobjects', 'DreamObjects', 'DreamObjects', 'DreamObjects', 'https://panel.dreamhost.com/index.cgi?tree=storage.dreamhostobjects', '<a href="https://dreamhost.com/cloud/dreamobjects/" target="_blank"><img alt="DreamObjects" src="'.UPDRAFTPLUS_URL.'/images/dreamobjects_logo-horiz-2013.png"></a>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configuration template
|
||||
*
|
||||
* @return String - the template, ready for substitutions to be carried out
|
||||
*/
|
||||
public function get_configuration_template() {
|
||||
return $this->get_configuration_template_engine('dreamobjects', 'DreamObjects', 'DreamObjects', 'DreamObjects', 'https://panel.dreamhost.com/index.cgi?tree=storage.dreamhostobjects', '<a href="https://dreamhost.com/cloud/dreamobjects/" target="_blank"><img alt="DreamObjects" src="'.UPDRAFTPLUS_URL.'/images/dreamobjects_logo-horiz-2013.png"></a>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get handlebar partial template string for endpoint of s3 compatible remote storage method. Other child class can extend it.
|
||||
*
|
||||
* @return string the partial template string
|
||||
*/
|
||||
protected function get_partial_configuration_template_for_endpoint() {
|
||||
// When new endpoint introduced in future, Please add it as hard coded option for below endpoint dropdown and also add as array value in private $dreamobjects_endpoints variable
|
||||
return '<tr class="'.$this->get_css_classes().'">
|
||||
<th>'.sprintf(__('%s end-point', 'updraftplus'), 'DreamObjects').'</th>
|
||||
<td>
|
||||
<select data-updraft_settings_test="endpoint" '.$this->output_settings_field_name_and_id('endpoint', true).' style="width: 360px">
|
||||
{{#each dreamobjects_endpoints as |description endpoint|}}
|
||||
<option value="{{endpoint}}" {{#ifeq ../endpoint endpoint}}selected="selected"{{/ifeq}}>{{description}}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</td>
|
||||
</tr>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies handerbar template options
|
||||
*
|
||||
* @param array $opts
|
||||
* @return array - Modified handerbar template options
|
||||
*/
|
||||
public function transform_options_for_template($opts) {
|
||||
$opts['endpoint'] = empty($opts['endpoint']) ? '' : $opts['endpoint'];
|
||||
$opts['dreamobjects_endpoints'] = $this->dreamobjects_endpoints;
|
||||
return $opts;
|
||||
}
|
||||
|
||||
public function credentials_test($posted_settings) {
|
||||
$this->credentials_test_engine($this->get_config(), $posted_settings);
|
||||
}
|
||||
}
|
||||
959
wp-content/plugins/updraftplus/methods/dropbox.php
Normal file
959
wp-content/plugins/updraftplus/methods/dropbox.php
Normal file
@@ -0,0 +1,959 @@
|
||||
<?php
|
||||
/**
|
||||
* https://www.dropbox.com/developers/apply?cont=/developers/apps
|
||||
*/
|
||||
|
||||
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed.');
|
||||
|
||||
// Converted to multi-options (Feb 2017-) and previous options conversion removed: Yes
|
||||
|
||||
if (!class_exists('UpdraftPlus_BackupModule')) require_once(UPDRAFTPLUS_DIR.'/methods/backup-module.php');
|
||||
|
||||
// Fix a potential problem for users who had the short-lived 1.12.35-1.12.38 free versions (see: https://wordpress.org/support/topic/1-12-37-dropbox-auth-broken/page/2/#post-8981457)
|
||||
// Can be removed after a few months
|
||||
$potential_options = UpdraftPlus_Options::get_updraft_option('updraft_dropbox');
|
||||
if (is_array($potential_options) && isset($potential_options['version']) && isset($potential_options['settings']) && array() === $potential_options['settings']) {
|
||||
// Wipe it, which will force its re-creation in proper format
|
||||
UpdraftPlus_Options::delete_updraft_option('updraft_dropbox');
|
||||
}
|
||||
|
||||
class UpdraftPlus_BackupModule_dropbox extends UpdraftPlus_BackupModule {
|
||||
|
||||
private $current_file_hash;
|
||||
|
||||
private $current_file_size;
|
||||
|
||||
private $uploaded_offset;
|
||||
|
||||
private $upload_tick;
|
||||
|
||||
/**
|
||||
* This callback is called as upload progress is made
|
||||
*
|
||||
* @param Integer $offset - the byte offset
|
||||
* @param String $uploadid - identifier for the upload in progress
|
||||
* @param Boolean|String $fullpath - optional full path to the file being uploaded
|
||||
*/
|
||||
public function chunked_callback($offset, $uploadid, $fullpath = false) {
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
$storage = $this->get_storage();
|
||||
|
||||
// Update upload ID
|
||||
$this->jobdata_set('upload_id_'.$this->current_file_hash, $uploadid);
|
||||
$this->jobdata_set('upload_offset_'.$this->current_file_hash, $offset);
|
||||
|
||||
$time_now = microtime(true);
|
||||
|
||||
$time_since_last_tick = $time_now - $this->upload_tick;
|
||||
$data_since_last_tick = $offset - $this->uploaded_offset;
|
||||
|
||||
$this->upload_tick = $time_now;
|
||||
$this->uploaded_offset = $offset;
|
||||
|
||||
// Here we use job-wide data, because we don't expect wildly different performance for different Dropbox accounts
|
||||
$chunk_size = $updraftplus->jobdata_get('dropbox_chunk_size', 1048576);
|
||||
// Don't go beyond 10MB, or change the chunk size after the last segment
|
||||
if ($chunk_size < 10485760 && $this->current_file_size > 0 && $offset < $this->current_file_size) {
|
||||
$job_run_time = $time_now - $updraftplus->job_time_ms;
|
||||
if ($time_since_last_tick < 10) {
|
||||
$upload_rate = $data_since_last_tick / max($time_since_last_tick, 1);
|
||||
$upload_secs = min(floor($job_run_time), 10);
|
||||
if ($job_run_time < 15) $upload_secs = max(6, $job_run_time*0.6);
|
||||
$new_chunk = max(min($upload_secs * $upload_rate * 0.9, 10485760), 1048576);
|
||||
$new_chunk = $new_chunk - ($new_chunk % 524288);
|
||||
$chunk_size = (int) $new_chunk;
|
||||
$storage->setChunkSize($chunk_size);
|
||||
$updraftplus->jobdata_set('dropbox_chunk_size', $chunk_size);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->current_file_size > 0) {
|
||||
$percent = round(100*($offset/$this->current_file_size), 1);
|
||||
$updraftplus->record_uploaded_chunk($percent, "$uploadid, $offset, ".round($chunk_size/1024, 1)." KB", $fullpath);
|
||||
} else {
|
||||
$this->log("Chunked Upload: $offset bytes uploaded");
|
||||
// This act is done by record_uploaded_chunk, and helps prevent overlapping runs
|
||||
if ($fullpath) touch($fullpath);
|
||||
}
|
||||
}
|
||||
|
||||
public function get_supported_features() {
|
||||
// This options format is handled via only accessing options via $this->get_options()
|
||||
return array('multi_options', 'config_templates', 'multi_storage');
|
||||
}
|
||||
|
||||
public function get_default_options() {
|
||||
return array(
|
||||
'appkey' => '',
|
||||
'secret' => '',
|
||||
'folder' => '',
|
||||
'tk_access_token' => '',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Acts as a WordPress options filter
|
||||
*
|
||||
* @param Array $dropbox - An array of Dropbox options
|
||||
* @return Array - the returned array can either be the set of updated Dropbox settings or a WordPress error array
|
||||
*/
|
||||
public function options_filter($dropbox) {
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
// Get the current options (and possibly update them to the new format)
|
||||
$opts = UpdraftPlus_Storage_Methods_Interface::update_remote_storage_options_format('dropbox');
|
||||
|
||||
if (is_wp_error($opts)) {
|
||||
if ('recursion' !== $opts->get_error_code()) {
|
||||
$msg = "(".$opts->get_error_code()."): ".$opts->get_error_message();
|
||||
$this->log($msg);
|
||||
error_log("UpdraftPlus: $msg");
|
||||
}
|
||||
// The saved options had a problem; so, return the new ones
|
||||
return $dropbox;
|
||||
}
|
||||
|
||||
// If the input is not as expected, then return the current options
|
||||
if (!is_array($dropbox)) return $opts;
|
||||
|
||||
// Remove instances that no longer exist
|
||||
foreach ($opts['settings'] as $instance_id => $storage_options) {
|
||||
if (!isset($dropbox['settings'][$instance_id])) unset($opts['settings'][$instance_id]);
|
||||
}
|
||||
|
||||
// Dropbox has a special case where the settings could be empty so we should check for this before
|
||||
if (!empty($dropbox['settings'])) {
|
||||
|
||||
foreach ($dropbox['settings'] as $instance_id => $storage_options) {
|
||||
if (!empty($opts['settings'][$instance_id]['tk_access_token'])) {
|
||||
|
||||
$current_app_key = empty($opts['settings'][$instance_id]['appkey']) ? false : $opts['settings'][$instance_id]['appkey'];
|
||||
$new_app_key = empty($storage_options['appkey']) ? false : $storage_options['appkey'];
|
||||
|
||||
// If a different app key is being used, then wipe the stored token as it cannot belong to the new app
|
||||
if ($current_app_key !== $new_app_key) {
|
||||
unset($opts['settings'][$instance_id]['tk_access_token']);
|
||||
unset($opts['settings'][$instance_id]['ownername']);
|
||||
unset($opts['settings'][$instance_id]['CSRF']);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Now loop over the new options, and replace old options with them
|
||||
foreach ($storage_options as $key => $value) {
|
||||
if (null === $value) {
|
||||
unset($opts['settings'][$instance_id][$key]);
|
||||
} else {
|
||||
if (!isset($opts['settings'][$instance_id])) $opts['settings'][$instance_id] = array();
|
||||
$opts['settings'][$instance_id][$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($opts['settings'][$instance_id]['folder']) && preg_match('#^https?://(www.)dropbox\.com/home/Apps/UpdraftPlus(.Com)?([^/]*)/(.*)$#i', $opts['settings'][$instance_id]['folder'], $matches)) $opts['settings'][$instance_id]['folder'] = $matches[3];
|
||||
|
||||
// check if we have the dummy nosave option and remove it so that it doesn't get saved
|
||||
if (isset($opts['settings'][$instance_id]['dummy-nosave'])) unset($opts['settings'][$instance_id]['dummy-nosave']);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $opts;
|
||||
}
|
||||
|
||||
public function backup($backup_array) {
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
$opts = $this->get_options();
|
||||
|
||||
if (empty($opts['tk_access_token'])) {
|
||||
$this->log('You do not appear to be authenticated with Dropbox (1)');
|
||||
$this->log(__('You do not appear to be authenticated with Dropbox', 'updraftplus'), 'error');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 28 September 2017: APIv1 is gone. We'll keep the variable to make life easier if there's ever an APIv3.
|
||||
$use_api_ver = 2;
|
||||
|
||||
if (empty($opts['tk_request_token'])) {
|
||||
$this->log("begin cloud upload (using API version $use_api_ver with OAuth v2 token)");
|
||||
} else {
|
||||
$this->log("begin cloud upload (using API version $use_api_ver with OAuth v1 token)");
|
||||
}
|
||||
|
||||
$chunk_size = $updraftplus->jobdata_get('dropbox_chunk_size', 1048576);
|
||||
|
||||
try {
|
||||
$dropbox = $this->bootstrap();
|
||||
if (false === $dropbox) throw new Exception(__('You do not appear to be authenticated with Dropbox', 'updraftplus'));
|
||||
$this->log("access gained; setting chunk size to: ".round($chunk_size/1024, 1)." KB");
|
||||
$dropbox->setChunkSize($chunk_size);
|
||||
} catch (Exception $e) {
|
||||
$this->log('error when trying to gain access: '.$e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
|
||||
$this->log(sprintf(__('error: %s (see log file for more)', 'updraftplus'), $e->getMessage()), 'error');
|
||||
return false;
|
||||
}
|
||||
|
||||
$updraft_dir = $updraftplus->backups_dir_location();
|
||||
$dropbox_folder = trailingslashit($opts['folder']);
|
||||
|
||||
foreach ($backup_array as $file) {
|
||||
|
||||
$available_quota = -1;
|
||||
|
||||
// If we experience any failures collecting account info, then carry on anyway
|
||||
try {
|
||||
|
||||
/*
|
||||
Quota information is no longer provided with account information a new call to quotaInfo must be made to get this information.
|
||||
*/
|
||||
$quota_info = $dropbox->quotaInfo();
|
||||
|
||||
if ("200" != $quota_info['code']) {
|
||||
$message = "account/info did not return HTTP 200; returned: ". $quota_info['code'];
|
||||
} elseif (!isset($quota_info['body'])) {
|
||||
$message = "account/info did not return the expected data";
|
||||
} else {
|
||||
$body = $quota_info['body'];
|
||||
if (isset($body->quota_info)) {
|
||||
$quota_info = $body->quota_info;
|
||||
$total_quota = $quota_info->quota;
|
||||
$normal_quota = $quota_info->normal;
|
||||
$shared_quota = $quota_info->shared;
|
||||
$available_quota = $total_quota - ($normal_quota + $shared_quota);
|
||||
$message = "quota usage: normal=".round($normal_quota/1048576, 1)." MB, shared=".round($shared_quota/1048576, 1)." MB, total=".round($total_quota/1048576, 1)." MB, available=".round($available_quota/1048576, 1)." MB";
|
||||
} else {
|
||||
$total_quota = max($body->allocation->allocated, 1);
|
||||
$used = $body->used;
|
||||
/* check here to see if the account is a team account and if so use the other used value
|
||||
This will give us their total usage including their individual account and team account */
|
||||
if (isset($body->allocation->used)) $used = $body->allocation->used;
|
||||
$available_quota = $total_quota - $used;
|
||||
$message = "quota usage: used=".round($used/1048576, 1)." MB, total=".round($total_quota/1048576, 1)." MB, available=".round($available_quota/1048576, 1)." MB";
|
||||
}
|
||||
}
|
||||
$this->log($message);
|
||||
} catch (Exception $e) {
|
||||
$this->log("exception (".get_class($e).") occurred whilst getting account info: ".$e->getMessage());
|
||||
// $this->log(sprintf(__("%s error: %s", 'updraftplus'), 'Dropbox', $e->getMessage()).' ('.$e->getCode().')', 'warning', md5($e->getMessage()));
|
||||
}
|
||||
|
||||
$file_success = 1;
|
||||
|
||||
$hash = md5($file);
|
||||
$this->current_file_hash = $hash;
|
||||
|
||||
$filesize = filesize($updraft_dir.'/'.$file);
|
||||
$this->current_file_size = $filesize;
|
||||
|
||||
// Into KB
|
||||
$filesize = $filesize/1024;
|
||||
$microtime = microtime(true);
|
||||
|
||||
if ($upload_id = $this->jobdata_get('upload_id_'.$hash, null, 'updraf_dbid_'.$hash)) {
|
||||
// Resume
|
||||
$offset = $this->jobdata_get('upload_offset_'.$hash, null, 'updraf_dbof_'.$hash);
|
||||
$this->log("This is a resumption: $offset bytes had already been uploaded");
|
||||
} else {
|
||||
$offset = 0;
|
||||
$upload_id = null;
|
||||
}
|
||||
|
||||
// We don't actually abort now - there's no harm in letting it try and then fail
|
||||
if (-1 != $available_quota && $available_quota < ($filesize-$offset)) {
|
||||
$this->log("File upload expected to fail: file data remaining to upload ($file) size is ".($filesize-$offset)." b (overall file size; .".($filesize*1024)." b), whereas available quota is only $available_quota b");
|
||||
// $this->log(sprintf(__("Account full: your %s account has only %d bytes left, but the file to be uploaded has %d bytes remaining (total size: %d bytes)",'updraftplus'),'Dropbox', $available_quota, $filesize-$offset, $filesize), 'warning');
|
||||
}
|
||||
|
||||
// Old-style, single file put: $put = $dropbox->putFile($updraft_dir.'/'.$file, $dropbox_folder.$file);
|
||||
|
||||
$ufile = apply_filters('updraftplus_dropbox_modpath', $file, $this);
|
||||
|
||||
$this->log("Attempt to upload: $file to: $ufile");
|
||||
|
||||
$this->upload_tick = microtime(true);
|
||||
$this->uploaded_offset = $offset;
|
||||
|
||||
try {
|
||||
$response = $dropbox->chunkedUpload($updraft_dir.'/'.$file, '', $ufile, true, $offset, $upload_id, array($this, 'chunked_callback'));
|
||||
if (empty($response['code']) || "200" != $response['code']) {
|
||||
$this->log('Unexpected HTTP code returned from Dropbox: '.$response['code']." (".serialize($response).")");
|
||||
if ($response['code'] >= 400) {
|
||||
$this->log(sprintf(__('error: failed to upload file to %s (see log file for more)', 'updraftplus'), $file), 'error');
|
||||
$file_success = 0;
|
||||
} else {
|
||||
$this->log(__('did not return the expected response - check your log file for more details', 'updraftplus'), 'warning');
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->log("chunked upload exception (".get_class($e)."): ".$e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
|
||||
if (preg_match("/Submitted input out of alignment: got \[(\d+)\] expected \[(\d+)\]/i", $e->getMessage(), $matches)) {
|
||||
// Try the indicated offset
|
||||
$we_tried = $matches[1];
|
||||
$dropbox_wanted = (int) $matches[2];
|
||||
$this->log("not yet aligned: tried=$we_tried, wanted=$dropbox_wanted; will attempt recovery");
|
||||
$this->uploaded_offset = $dropbox_wanted;
|
||||
try {
|
||||
$dropbox->chunkedUpload($updraft_dir.'/'.$file, '', $ufile, true, $dropbox_wanted, $upload_id, array($this, 'chunked_callback'));
|
||||
} catch (Exception $e) {
|
||||
$msg = $e->getMessage();
|
||||
if (preg_match('/Upload with upload_id .* already completed/', $msg)) {
|
||||
$this->log('returned an error, but apparently indicating previous success: '.$msg);
|
||||
} else {
|
||||
$this->log($msg.' (line: '.$e->getLine().', file: '.$e->getFile().')');
|
||||
$this->log(sprintf(__('failed to upload file to %s (see log file for more)', 'updraftplus'), $ufile), 'error');
|
||||
$file_success = 0;
|
||||
if (strpos($msg, 'select/poll returned error') !== false && $this->upload_tick > 0 && time() - $this->upload_tick > 800) {
|
||||
UpdraftPlus_Job_Scheduler::reschedule(60);
|
||||
$this->log("Select/poll returned after a long time: scheduling a resumption and terminating for now");
|
||||
UpdraftPlus_Job_Scheduler::record_still_alive();
|
||||
die;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$msg = $e->getMessage();
|
||||
if (preg_match('/Upload with upload_id .* already completed/', $msg)) {
|
||||
$this->log('returned an error, but apparently indicating previous success: '.$msg);
|
||||
} else {
|
||||
$this->log(sprintf(__('failed to upload file to %s (see log file for more)', 'updraftplus'), $ufile), 'error');
|
||||
$file_success = 0;
|
||||
if (strpos($msg, 'select/poll returned error') !== false && $this->upload_tick > 0 && time() - $this->upload_tick > 800) {
|
||||
UpdraftPlus_Job_Scheduler::reschedule(60);
|
||||
$this->log("Select/poll returned after a long time: scheduling a resumption and terminating for now");
|
||||
UpdraftPlus_Job_Scheduler::record_still_alive();
|
||||
die;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($file_success) {
|
||||
$updraftplus->uploaded_file($file);
|
||||
$microtime_elapsed = microtime(true)-$microtime;
|
||||
$speedps = ($microtime_elapsed > 0) ? $filesize/$microtime_elapsed : 0;
|
||||
$speed = sprintf("%.2d", $filesize)." KB in ".sprintf("%.2d", $microtime_elapsed)."s (".sprintf("%.2d", $speedps)." KB/s)";
|
||||
$this->log("File upload success (".$file."): $speed");
|
||||
$this->jobdata_delete('upload_id_'.$hash, 'updraf_dbid_'.$hash);
|
||||
$this->jobdata_delete('upload_offset_'.$hash, 'updraf_dbof_'.$hash);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method gets a list of files from the remote stoage that match the string passed in and returns an array of backups
|
||||
*
|
||||
* @param string $match a substring to require (tested via strpos() !== false)
|
||||
* @return array
|
||||
*/
|
||||
public function listfiles($match = 'backup_') {
|
||||
|
||||
$opts = $this->get_options();
|
||||
|
||||
if (empty($opts['tk_access_token'])) return new WP_Error('no_settings', __('No settings were found', 'updraftplus').' (dropbox)');
|
||||
|
||||
global $updraftplus;
|
||||
try {
|
||||
$dropbox = $this->bootstrap();
|
||||
} catch (Exception $e) {
|
||||
$this->log('access error: '.$e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
|
||||
return new WP_Error('access_error', $e->getMessage());
|
||||
}
|
||||
|
||||
$searchpath = '/'.untrailingslashit(apply_filters('updraftplus_dropbox_modpath', '', $this));
|
||||
|
||||
try {
|
||||
/* Some users could have a large amount of backups, the max search is 1000 entries we should continue to search until there are no more entries to bring back. */
|
||||
$start = 0;
|
||||
$matches = array();
|
||||
|
||||
while (true) {
|
||||
$search = $dropbox->search($match, $searchpath, 1000, $start);
|
||||
if (empty($search['code']) || 200 != $search['code']) return new WP_Error('response_error', sprintf(__('%s returned an unexpected HTTP response: %s', 'updraftplus'), 'Dropbox', $search['code']), $search['body']);
|
||||
|
||||
if (empty($search['body'])) return array();
|
||||
|
||||
if (isset($search['body']->matches) && is_array($search['body']->matches)) {
|
||||
$matches = array_merge($matches, $search['body']->matches);
|
||||
} elseif (is_array($search['body'])) {
|
||||
$matches = $search['body'];
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
if (isset($search['body']->more) && true == $search['body']->more && isset($search['body']->start)) {
|
||||
$start = $search['body']->start;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
$this->log($e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
|
||||
// The most likely cause of a search_error is specifying a non-existent path, which should just result in an empty result set.
|
||||
// return new WP_Error('search_error', $e->getMessage());
|
||||
return array();
|
||||
}
|
||||
|
||||
$results = array();
|
||||
|
||||
foreach ($matches as $item) {
|
||||
$item = $item->metadata;
|
||||
if (!is_object($item)) continue;
|
||||
|
||||
if ((!isset($item->size) || $item->size > 0) && 'folder' != $item->{'.tag'} && !empty($item->path_display) && 0 === strpos($item->path_display, $searchpath)) {
|
||||
|
||||
$path = substr($item->path_display, strlen($searchpath));
|
||||
if ('/' == substr($path, 0, 1)) $path = substr($path, 1);
|
||||
|
||||
// Ones in subfolders are not wanted
|
||||
if (false !== strpos($path, '/')) continue;
|
||||
|
||||
$result = array('name' => $path);
|
||||
if (!empty($item->size)) $result['size'] = $item->size;
|
||||
|
||||
$results[] = $result;
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
public function defaults() {
|
||||
return apply_filters('updraftplus_dropbox_defaults', array('Z3Q3ZmkwbnplNHA0Zzlx', 'bTY0bm9iNmY4eWhjODRt'));
|
||||
}
|
||||
|
||||
public function delete($files, $data = null, $sizeinfo = array()) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
|
||||
|
||||
global $updraftplus;
|
||||
if (is_string($files)) $files = array($files);
|
||||
|
||||
$opts = $this->get_options();
|
||||
|
||||
if (empty($opts['tk_access_token'])) {
|
||||
$this->log('You do not appear to be authenticated with Dropbox (3)');
|
||||
$this->log(sprintf(__('You do not appear to be authenticated with %s (whilst deleting)', 'updraftplus'), 'Dropbox'), 'warning');
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
$dropbox = $this->bootstrap();
|
||||
} catch (Exception $e) {
|
||||
$this->log($e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
|
||||
$this->log(sprintf(__('Failed to access %s when deleting (see log file for more)', 'updraftplus'), 'Dropbox'), 'warning');
|
||||
return false;
|
||||
}
|
||||
if (false === $dropbox) return false;
|
||||
|
||||
foreach ($files as $file) {
|
||||
$ufile = apply_filters('updraftplus_dropbox_modpath', $file, $this);
|
||||
$this->log("request deletion: $ufile");
|
||||
|
||||
try {
|
||||
$dropbox->delete($ufile);
|
||||
$file_success = 1;
|
||||
} catch (Exception $e) {
|
||||
$this->log($e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
|
||||
}
|
||||
|
||||
if (isset($file_success)) {
|
||||
$this->log('delete succeeded');
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function download($file) {
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
$opts = $this->get_options();
|
||||
|
||||
if (empty($opts['tk_access_token'])) {
|
||||
$this->log('You do not appear to be authenticated with Dropbox (4)');
|
||||
$this->log(sprintf(__('You do not appear to be authenticated with %s', 'updraftplus'), 'Dropbox'), 'error');
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
$dropbox = $this->bootstrap();
|
||||
} catch (Exception $e) {
|
||||
$this->log($e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
|
||||
$this->log($e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')', 'error');
|
||||
return false;
|
||||
}
|
||||
if (false === $dropbox) return false;
|
||||
|
||||
$remote_files = $this->listfiles($file);
|
||||
|
||||
foreach ($remote_files as $file_info) {
|
||||
if ($file_info['name'] == $file) {
|
||||
return $updraftplus->chunked_download($file, $this, $file_info['size'], true, null, 2*1048576);
|
||||
}
|
||||
}
|
||||
|
||||
$this->log("$file: file not found in listing of remote directory");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback used by by chunked downloading API
|
||||
*
|
||||
* @param String $file - the file (basename) to be downloaded
|
||||
* @param Array $headers - supplied headers
|
||||
* @param Mixed $data - pass-back from our call to the API (which we don't use)
|
||||
* @param resource $fh - the local file handle
|
||||
*
|
||||
* @return String - the data downloaded
|
||||
*/
|
||||
public function chunked_download($file, $headers, $data, $fh) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
$opts = $this->get_options();
|
||||
$storage = $this->get_storage();
|
||||
|
||||
$updraft_dir = $updraftplus->backups_dir_location();
|
||||
$microtime = microtime(true);
|
||||
|
||||
$try_the_other_one = false;
|
||||
|
||||
$ufile = apply_filters('updraftplus_dropbox_modpath', $file, $this);
|
||||
|
||||
$options = array();
|
||||
|
||||
if (!empty($headers)) $options['headers'] = $headers;
|
||||
|
||||
try {
|
||||
$get = $storage->download($ufile, $fh, $options);
|
||||
} catch (Exception $e) {
|
||||
// TODO: Remove this October 2013 (we stored in the wrong place for a while...)
|
||||
$try_the_other_one = true;
|
||||
$possible_error = $e->getMessage();
|
||||
$this->log($e);
|
||||
$get = false;
|
||||
}
|
||||
|
||||
// TODO: Remove this October 2013 (we stored files in the wrong place for a while...)
|
||||
if ($try_the_other_one) {
|
||||
$dropbox_folder = trailingslashit($opts['folder']);
|
||||
try {
|
||||
$get = $storage->download($dropbox_folder.'/'.$file, $fh, $options);
|
||||
if (isset($get['response']['body'])) {
|
||||
$this->log("downloaded ".round(strlen($get['response']['body'])/1024, 1).' KB');
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->log($possible_error, 'error');
|
||||
$this->log($e->getMessage(), 'error');
|
||||
$get = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $get;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the pre configuration template
|
||||
*
|
||||
* @return String - the template
|
||||
*/
|
||||
public function get_pre_configuration_template() {
|
||||
|
||||
global $updraftplus_admin;
|
||||
|
||||
$classes = $this->get_css_classes(false);
|
||||
|
||||
?>
|
||||
<tr class="<?php echo $classes . ' ' . 'dropbox_pre_config_container';?>">
|
||||
<td colspan="2">
|
||||
<img alt="<?php _e(sprintf(__('%s logo', 'updraftplus'), 'Dropbox')); ?>" src="<?php echo UPDRAFTPLUS_URL.'/images/dropbox-logo.png'; ?>">
|
||||
<br>
|
||||
<p>
|
||||
<?php
|
||||
global $updraftplus_admin;
|
||||
$updraftplus_admin->curl_check('Dropbox', false, 'dropbox');
|
||||
?>
|
||||
</p>
|
||||
<p>
|
||||
<?php echo sprintf(__('Please read %s for use of our %s authorization app (none of your backup data is sent to us).', 'updraftplus'), '<a target="_blank" href="https://updraftplus.com/faqs/what-is-your-privacy-policy-for-the-use-of-your-dropbox-app/">'.__('this privacy policy', 'updraftplus').'</a>', 'Dropbox');?>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configuration template
|
||||
*
|
||||
* @return String - the template, ready for substitutions to be carried out
|
||||
*/
|
||||
public function get_configuration_template() {
|
||||
ob_start();
|
||||
$classes = $this->get_css_classes();
|
||||
|
||||
$defmsg = '<tr class="'.$classes.'"><td></td><td><strong>'.__('Need to use sub-folders?', 'updraftplus').'</strong> '.__('Backups are saved in', 'updraftplus').' apps/UpdraftPlus. '.__('If you backup several sites into the same Dropbox and want to organize with sub-folders, then ', 'updraftplus').'<a href="https://updraftplus.com/shop/" target="_blank">'.__("there's an add-on for that.", 'updraftplus').'</a></td></tr>';
|
||||
|
||||
$defmsg = '<tr class="'.$classes.'"><td></td><td><strong>'.__('Need to use sub-folders?', 'updraftplus').'</strong> '.__('Backups are saved in', 'updraftplus').' apps/UpdraftPlus. '.__('If you backup several sites into the same Dropbox and want to organize with sub-folders, then ', 'updraftplus').'<a href="'.apply_filters("updraftplus_com_link", "https://updraftplus.com/shop/").'" target="_blank">'.__("there's an add-on for that.", 'updraftplus').'</a></td></tr>';
|
||||
|
||||
$extra_config = apply_filters('updraftplus_dropbox_extra_config_template', $defmsg, $this);
|
||||
echo $extra_config;
|
||||
?>
|
||||
<tr class="<?php echo $classes;?>">
|
||||
<th><?php echo sprintf(__('Authenticate with %s', 'updraftplus'), __('Dropbox', 'updraftplus'));?>:</th>
|
||||
<td>
|
||||
{{#if is_authenticated}}
|
||||
<?php
|
||||
echo "<p><strong>".__('(You appear to be already authenticated).', 'updraftplus')."</strong>";
|
||||
$this->get_deauthentication_link();
|
||||
echo '</p>';
|
||||
?>
|
||||
{{/if}}
|
||||
{{#if ownername_sentence}}
|
||||
<br/>
|
||||
{{ownername_sentence}}
|
||||
{{/if}}
|
||||
<?php
|
||||
echo '<p>';
|
||||
$this->get_authentication_link();
|
||||
echo '</p>';
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
{{!-- Legacy: only show this next setting to old users who had a setting stored --}}
|
||||
{{#if old_user_settings}}
|
||||
<tr class="<?php echo $classes;?>">
|
||||
<th></th>
|
||||
<td>
|
||||
<?php echo '<p>'.htmlspecialchars(__('You must add the following as the authorised redirect URI in your Dropbox console (under "API Settings") when asked', 'updraftplus')).': <kbd>'.UpdraftPlus_Options::admin_page_url().'?page=updraftplus&action=updraftmethod-dropbox-auth</kbd></p>'; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="<?php echo $classes;?>">
|
||||
<th>Your Dropbox App Key:</th>
|
||||
<td><input type="text" autocomplete="off" style="width:332px" <?php $this->output_settings_field_name_and_id('appkey');?> value="{{appkey}}" /></td>
|
||||
</tr>
|
||||
<tr class="<?php echo $classes;?>">
|
||||
<th>Your Dropbox App Secret:</th>
|
||||
<td><input type="text" style="width:332px" <?php $this->output_settings_field_name_and_id('secret');?> value="{{secret}}" /></td>
|
||||
</tr>
|
||||
{{else}}
|
||||
<?php if (false === strpos($extra_config, '<input')) {
|
||||
// We need to make sure that it is not the case that the module has no settings whatsoever - this can result in the module being effectively invisible.
|
||||
?>
|
||||
<input type="hidden" <?php $this->output_settings_field_name_and_id('dummy-nosave');?> value="0">
|
||||
<?php } ?>
|
||||
{{/if}}
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies handerbar template options
|
||||
*
|
||||
* @param array $opts
|
||||
* @return array - Modified handerbar template options
|
||||
*/
|
||||
public function transform_options_for_template($opts) {
|
||||
if (!empty($opts['tk_access_token'])) {
|
||||
$opts['ownername'] = empty($opts['ownername']) ? '' : $opts['ownername'];
|
||||
if ($opts['ownername']) {
|
||||
$opts['ownername_sentence'] = sprintf(__("Account holder's name: %s.", 'updraftplus'), $opts['ownername']).' ';
|
||||
}
|
||||
$opts['is_authenticated'] = true;
|
||||
}
|
||||
$opts['old_user_settings'] = (!empty($opts['appkey']) || (defined('UPDRAFTPLUS_CUSTOM_DROPBOX_APP') && UPDRAFTPLUS_CUSTOM_DROPBOX_APP));
|
||||
if ($opts['old_user_settings']) {
|
||||
$opts['appkey'] = empty($opts['appkey']) ? '' : $opts['appkey'];
|
||||
$opts['secret'] = empty($opts['secret']) ? '' : $opts['secret'];
|
||||
}
|
||||
$opts = apply_filters("updraftplus_options_dropbox_options", $opts);
|
||||
return $opts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives settings keys which values should not passed to handlebarsjs context.
|
||||
* The settings stored in UD in the database sometimes also include internal information that it would be best not to send to the front-end (so that it can't be stolen by a man-in-the-middle attacker)
|
||||
*
|
||||
* @return array - Settings array keys which should be filtered
|
||||
*/
|
||||
public function filter_frontend_settings_keys() {
|
||||
return array(
|
||||
'CSRF',
|
||||
'code',
|
||||
'ownername',
|
||||
'tk_access_token',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Over-rides the parent to allow this method to output extra information about using the correct account for OAuth authentication
|
||||
*
|
||||
* @return [boolean] - return false so that no extra information is output
|
||||
*/
|
||||
public function output_account_warning() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles various URL actions, as indicated by the updraftplus_dropboxauth URL parameter
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
public function action_auth() {
|
||||
if (isset($_GET['updraftplus_dropboxauth'])) {
|
||||
if ('doit' == $_GET['updraftplus_dropboxauth']) {
|
||||
$this->action_authenticate_storage();
|
||||
return;
|
||||
} elseif ('deauth' == $_GET['updraftplus_dropboxauth']) {
|
||||
$this->action_deauthenticate_storage();
|
||||
return;
|
||||
}
|
||||
} elseif (isset($_REQUEST['state'])) {
|
||||
|
||||
if ('POST' == $_SERVER['REQUEST_METHOD']) {
|
||||
$raw_state = urldecode($_POST['state']);
|
||||
if (isset($_POST['code'])) $raw_code = urldecode($_POST['code']);
|
||||
} else {
|
||||
$raw_state = $_GET['state'];
|
||||
if (isset($_GET['code'])) $raw_code = $_GET['code'];
|
||||
}
|
||||
|
||||
// Get the CSRF from setting and check it matches the one returned if it does no CSRF attack has happened
|
||||
$opts = $this->get_options();
|
||||
$csrf = $opts['CSRF'];
|
||||
$state = stripslashes($raw_state);
|
||||
// Check the state to see if an instance_id has been attached and if it has then extract the state
|
||||
$parts = explode(':', $state);
|
||||
$state = $parts[0];
|
||||
|
||||
if (strcmp($csrf, $state) == 0) {
|
||||
$opts['CSRF'] = '';
|
||||
if (isset($raw_code)) {
|
||||
// set code so it can be accessed in the next authentication step
|
||||
$opts['code'] = stripslashes($raw_code);
|
||||
$this->set_options($opts, true);
|
||||
$this->auth_token();
|
||||
}
|
||||
} else {
|
||||
error_log("UpdraftPlus: CSRF comparison failure: $csrf != $state");
|
||||
}
|
||||
}
|
||||
try {
|
||||
$this->auth_request();
|
||||
} catch (Exception $e) {
|
||||
global $updraftplus;
|
||||
$this->log(sprintf(__("%s error: %s", 'updraftplus'), sprintf(__("%s authentication", 'updraftplus'), 'Dropbox'), $e->getMessage()), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will reset any saved options and start the bootstrap process for an authentication
|
||||
*
|
||||
* @param String $instance_id - the instance id of the settings we want to authenticate
|
||||
*/
|
||||
public function do_authenticate_storage($instance_id) {
|
||||
try {
|
||||
// Clear out the existing credentials
|
||||
$opts = $this->get_options();
|
||||
$opts['tk_access_token'] = '';
|
||||
unset($opts['tk_request_token']);
|
||||
$opts['ownername'] = '';
|
||||
$this->set_options($opts, true);
|
||||
|
||||
$this->set_instance_id($instance_id);
|
||||
$this->bootstrap(false);
|
||||
} catch (Exception $e) {
|
||||
global $updraftplus;
|
||||
$this->log(sprintf(__("%s error: %s", 'updraftplus'), sprintf(__("%s authentication", 'updraftplus'), 'Dropbox'), $e->getMessage()), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will start the bootstrap process for a de-authentication
|
||||
*
|
||||
* @param String $instance_id - the instance id of the settings we want to de-authenticate
|
||||
*/
|
||||
public function do_deauthenticate_storage($instance_id) {
|
||||
try {
|
||||
$this->set_instance_id($instance_id);
|
||||
$this->bootstrap(true);
|
||||
} catch (Exception $e) {
|
||||
global $updraftplus;
|
||||
$this->log(sprintf(__("%s error: %s", 'updraftplus'), sprintf(__("%s de-authentication", 'updraftplus'), 'Dropbox'), $e->getMessage()), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
public function show_authed_admin_warning() {
|
||||
global $updraftplus_admin, $updraftplus;
|
||||
|
||||
$dropbox = $this->bootstrap();
|
||||
if (false === $dropbox) return false;
|
||||
|
||||
try {
|
||||
$account_info = $dropbox->accountInfo();
|
||||
} catch (Exception $e) {
|
||||
$accountinfo_err = sprintf(__("%s error: %s", 'updraftplus'), 'Dropbox', $e->getMessage()).' ('.$e->getCode().')';
|
||||
}
|
||||
|
||||
$message = "<strong>".__('Success:', 'updraftplus').'</strong> '.sprintf(__('you have authenticated your %s account', 'updraftplus'), 'Dropbox');
|
||||
// We log, because otherwise people get confused by the most recent log message of 'Parameter not found: oauth_token' and raise support requests
|
||||
$this->log(__('Success:', 'updraftplus').' '.sprintf(__('you have authenticated your %s account', 'updraftplus'), 'Dropbox'));
|
||||
|
||||
if (empty($account_info['code']) || "200" != $account_info['code']) {
|
||||
$message .= " (".__('though part of the returned information was not as expected - your mileage may vary', 'updraftplus').") ". $account_info['code'];
|
||||
if (!empty($accountinfo_err)) $message .= "<br>".htmlspecialchars($accountinfo_err);
|
||||
} else {
|
||||
$body = $account_info['body'];
|
||||
$name = '';
|
||||
if (isset($body->display_name)) {
|
||||
$name = $body->display_name;
|
||||
} else {
|
||||
$name = $body->name->display_name;
|
||||
}
|
||||
$message .= ". <br>".sprintf(__('Your %s account name: %s', 'updraftplus'), 'Dropbox', htmlspecialchars($name));
|
||||
$opts = $this->get_options();
|
||||
$opts['ownername'] = $name;
|
||||
$this->set_options($opts, true);
|
||||
|
||||
try {
|
||||
/**
|
||||
* Quota information is no longer provided with account information a new call to qoutaInfo must be made to get this information. The timeout is because we've seen cases where it returned after 180 seconds (apparently a faulty outgoing proxy), and we may as well wait as cause an error leading to user confusion.
|
||||
*/
|
||||
$quota_info = $dropbox->quotaInfo(array('timeout' => 190));
|
||||
|
||||
if (empty($quota_info['code']) || "200" != $quota_info['code']) {
|
||||
$message .= " (".__('though part of the returned information was not as expected - your mileage may vary', 'updraftplus').")". $quota_info['code'];
|
||||
if (!empty($accountinfo_err)) $message .= "<br>".htmlspecialchars($accountinfo_err);
|
||||
} else {
|
||||
$body = $quota_info['body'];
|
||||
if (isset($body->quota_info)) {
|
||||
$quota_info = $body->quota_info;
|
||||
$total_quota = max($quota_info->quota, 1);
|
||||
$normal_quota = $quota_info->normal;
|
||||
$shared_quota = $quota_info->shared;
|
||||
$available_quota =$total_quota - ($normal_quota + $shared_quota);
|
||||
$used_perc = round(($normal_quota + $shared_quota)*100/$total_quota, 1);
|
||||
$message .= ' <br>'.sprintf(__('Your %s quota usage: %s %% used, %s available', 'updraftplus'), 'Dropbox', $used_perc, round($available_quota/1048576, 1).' MB');
|
||||
} else {
|
||||
$total_quota = max($body->allocation->allocated, 1);
|
||||
$used = $body->used;
|
||||
/* check here to see if the account is a team account and if so use the other used value
|
||||
This will give us their total usage including their individual account and team account */
|
||||
if (isset($body->allocation->used)) $used = $body->allocation->used;
|
||||
$available_quota =$total_quota - $used;
|
||||
$used_perc = round($used*100/$total_quota, 1);
|
||||
$message .= ' <br>'.sprintf(__('Your %s quota usage: %s %% used, %s available', 'updraftplus'), 'Dropbox', $used_perc, round($available_quota/1048576, 1).' MB');
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// Catch
|
||||
}
|
||||
|
||||
}
|
||||
$updraftplus_admin->show_admin_warning($message);
|
||||
|
||||
}
|
||||
|
||||
public function auth_token() {
|
||||
$this->bootstrap();
|
||||
$opts = $this->get_options();
|
||||
if (!empty($opts['tk_access_token'])) {
|
||||
add_action('all_admin_notices', array($this, 'show_authed_admin_warning'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquire single-use authorization code
|
||||
*/
|
||||
public function auth_request() {
|
||||
$this->bootstrap();
|
||||
}
|
||||
|
||||
/**
|
||||
* This basically reproduces the relevant bits of bootstrap.php from the SDK
|
||||
*
|
||||
* @param boolean $deauthenticate indicates if we should bootstrap for a deauth or auth request
|
||||
* @return object
|
||||
*/
|
||||
public function bootstrap($deauthenticate = false) {
|
||||
|
||||
$storage = $this->get_storage();
|
||||
|
||||
if (!empty($storage) && !is_wp_error($storage)) return $storage;
|
||||
|
||||
// Dropbox APIv1 is dead, but we'll keep the variable in case v3 is ever announced
|
||||
$dropbox_api = 'Dropbox2';
|
||||
|
||||
include_once(UPDRAFTPLUS_DIR.'/includes/'.$dropbox_api.'/API.php');
|
||||
include_once(UPDRAFTPLUS_DIR.'/includes/'.$dropbox_api.'/Exception.php');
|
||||
include_once(UPDRAFTPLUS_DIR.'/includes/'.$dropbox_api.'/OAuth/Consumer/ConsumerAbstract.php');
|
||||
include_once(UPDRAFTPLUS_DIR.'/includes/'.$dropbox_api.'/OAuth/Storage/StorageInterface.php');
|
||||
include_once(UPDRAFTPLUS_DIR.'/includes/'.$dropbox_api.'/OAuth/Storage/Encrypter.php');
|
||||
include_once(UPDRAFTPLUS_DIR.'/includes/'.$dropbox_api.'/OAuth/Storage/WordPress.php');
|
||||
include_once(UPDRAFTPLUS_DIR.'/includes/'.$dropbox_api.'/OAuth/Consumer/Curl.php');
|
||||
// require_once(UPDRAFTPLUS_DIR.'/includes/'.$dropbox_api.'/OAuth/Consumer/WordPress.php');
|
||||
|
||||
$opts = $this->get_options();
|
||||
|
||||
$key = empty($opts['secret']) ? '' : $opts['secret'];
|
||||
$sec = empty($opts['appkey']) ? '' : $opts['appkey'];
|
||||
|
||||
$oauth2_id = base64_decode('aXA3NGR2Zm1sOHFteTA5');
|
||||
|
||||
// Set the callback URL
|
||||
$callbackhome = UpdraftPlus_Options::admin_page_url().'?page=updraftplus&action=updraftmethod-dropbox-auth';
|
||||
$callback = defined('UPDRAFTPLUS_DROPBOX_AUTH_RETURN_URL') ? UPDRAFTPLUS_DROPBOX_AUTH_RETURN_URL : 'https://auth.updraftplus.com/auth/dropbox/';
|
||||
|
||||
$instance_id = $this->get_instance_id();
|
||||
// Instantiate the Encrypter and storage objects
|
||||
$encrypter = new Dropbox_Encrypter('ThisOneDoesNotMatterBeyondLength');
|
||||
|
||||
// Instantiate the storage
|
||||
$dropbox_storage = new Dropbox_WordPress($encrypter, "tk_", 'updraft_dropbox', $this);
|
||||
|
||||
// WordPress consumer does not yet work
|
||||
// $oauth = new Dropbox_ConsumerWordPress($sec, $key, $dropbox_storage, $callback);
|
||||
|
||||
// Get the DropBox API access details
|
||||
list($d2, $d1) = $this->defaults();
|
||||
if (empty($sec)) {
|
||||
$sec = base64_decode($d1);
|
||||
}
|
||||
|
||||
if (empty($key)) {
|
||||
$key = base64_decode($d2);
|
||||
}
|
||||
|
||||
$root = 'sandbox';
|
||||
if ('dropbox:' == substr($sec, 0, 8)) {
|
||||
$sec = substr($sec, 8);
|
||||
$root = 'dropbox';
|
||||
}
|
||||
|
||||
try {
|
||||
$oauth = new Dropbox_Curl($sec, $oauth2_id, $key, $dropbox_storage, $callback, $callbackhome, $deauthenticate, $instance_id);
|
||||
} catch (Exception $e) {
|
||||
global $updraftplus;
|
||||
$this->log("Curl error: ".$e->getMessage());
|
||||
$this->log(sprintf(__("%s error: %s", 'updraftplus'), "Dropbox/Curl", $e->getMessage().' ('.get_class($e).') (line: '.$e->getLine().', file: '.$e->getFile()).')', 'error');
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($deauthenticate) return true;
|
||||
|
||||
$storage = new UpdraftPlus_Dropbox_API($oauth, $root);
|
||||
|
||||
$this->set_storage($storage);
|
||||
|
||||
return $storage;
|
||||
}
|
||||
}
|
||||
110
wp-content/plugins/updraftplus/methods/email.php
Normal file
110
wp-content/plugins/updraftplus/methods/email.php
Normal file
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed.');
|
||||
|
||||
// Files can easily get too big for this method
|
||||
|
||||
if (!class_exists('UpdraftPlus_BackupModule')) require_once(UPDRAFTPLUS_DIR.'/methods/backup-module.php');
|
||||
|
||||
class UpdraftPlus_BackupModule_email extends UpdraftPlus_BackupModule {
|
||||
|
||||
public function backup($backup_array) {
|
||||
|
||||
global $updraftplus, $updraftplus_backup;
|
||||
|
||||
$updraft_dir = trailingslashit($updraftplus->backups_dir_location());
|
||||
|
||||
$email = $updraftplus->just_one_email(UpdraftPlus_Options::get_updraft_option('updraft_email'), true);
|
||||
|
||||
if (!is_array($email)) $email = array_filter(array($email));
|
||||
|
||||
foreach ($backup_array as $type => $file) {
|
||||
|
||||
$descrip_type = preg_match('/^(.*)\d+$/', $type, $matches) ? $matches[1] : $type;
|
||||
|
||||
$fullpath = $updraft_dir.$file;
|
||||
|
||||
if (file_exists($fullpath) && filesize($fullpath) > UPDRAFTPLUS_WARN_EMAIL_SIZE) {
|
||||
$size_in_mb_of_big_file = round(filesize($fullpath)/1048576, 1);
|
||||
$toobig_hash = md5($file);
|
||||
$this->log($file.': '.sprintf(__('This backup archive is %s MB in size - the attempt to send this via email is likely to fail (few email servers allow attachments of this size). If so, you should switch to using a different remote storage method.', 'updraftplus'), $size_in_mb_of_big_file), 'warning', 'toobigforemail_'.$toobig_hash);
|
||||
}
|
||||
|
||||
$any_attempted = false;
|
||||
$any_sent = false;
|
||||
$any_skip = false;
|
||||
foreach ($email as $ind => $addr) {
|
||||
|
||||
if (apply_filters('updraftplus_email_backup', true, $addr, $ind, $type)) {
|
||||
foreach (explode(',', $addr) as $sendmail_addr) {
|
||||
|
||||
$send_short = (strlen($sendmail_addr)>5) ? substr($sendmail_addr, 0, 5).'...' : $sendmail_addr;
|
||||
$this->log("$file: email to: $send_short");
|
||||
$any_attempted = true;
|
||||
|
||||
$subject = __("WordPress Backup", 'updraftplus').': '.get_bloginfo('name').' (UpdraftPlus '.$updraftplus->version.') '.get_date_from_gmt(gmdate('Y-m-d H:i:s', $updraftplus->backup_time), 'Y-m-d H:i');
|
||||
|
||||
$sent = wp_mail(trim($sendmail_addr), $subject, sprintf(__("Backup is of: %s.", 'updraftplus'), site_url().' ('.$descrip_type.')'), null, array($fullpath));
|
||||
if ($sent) $any_sent = true;
|
||||
}
|
||||
} else {
|
||||
$log_message = apply_filters('updraftplus_email_backup_skip_log_message', '', $addr, $ind, $descrip_type);
|
||||
if (!empty($log_message)) {
|
||||
$this->log($log_message);
|
||||
}
|
||||
$any_skip = true;
|
||||
}
|
||||
}
|
||||
if ($any_sent) {
|
||||
if (isset($toobig_hash)) {
|
||||
$updraftplus->log_remove_warning('toobigforemail_'.$toobig_hash);
|
||||
// Don't leave it still set for the next archive
|
||||
unset($toobig_hash);
|
||||
}
|
||||
$updraftplus->uploaded_file($file);
|
||||
} elseif ($any_attempted) {
|
||||
$this->log('Mails were not sent successfully');
|
||||
$this->log(__('The attempt to send the backup via email failed (probably the backup was too large for this method)', 'updraftplus'), 'error');
|
||||
} elseif ($any_skip) {
|
||||
$this->log('No email addresses were configured to send email to '.$descrip_type);
|
||||
} else {
|
||||
$this->log('No email addresses were configured to send to');
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Acts as a WordPress options filter
|
||||
*
|
||||
* @param Array $options - An array of options
|
||||
*
|
||||
* @return Array - the returned array can either be the set of updated settings or a WordPress error array
|
||||
*/
|
||||
public function options_filter($options) {
|
||||
global $updraftplus;
|
||||
return $updraftplus->just_one_email($options);
|
||||
}
|
||||
|
||||
public function config_print() {
|
||||
?>
|
||||
<tr class="updraftplusmethod email">
|
||||
<th><?php _e('Note:', 'updraftplus');?></th>
|
||||
<td><?php
|
||||
|
||||
$used = apply_filters('updraftplus_email_whichaddresses',
|
||||
sprintf(__("Your site's admin email address (%s) will be used.", 'updraftplus'), get_bloginfo('admin_email').' - <a href="'.esc_attr(admin_url('options-general.php')).'">'.__("configure it here", 'updraftplus').'</a>').
|
||||
' <a href="'.apply_filters("updraftplus_com_link", "https://updraftplus.com/shop/reporting/").'" target="_blank">'.sprintf(__('For more options, use the "%s" add-on.', 'updraftplus'), __('Reporting', 'updraftplus')).'</a>'
|
||||
);
|
||||
|
||||
echo $used.' '.sprintf(__('Be aware that mail servers tend to have size limits; typically around %s MB; backups larger than any limits will likely not arrive.', 'updraftplus'), '10-20');
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function delete($files, $data = null, $sizeinfo = array()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
462
wp-content/plugins/updraftplus/methods/ftp.php
Normal file
462
wp-content/plugins/updraftplus/methods/ftp.php
Normal file
@@ -0,0 +1,462 @@
|
||||
<?php
|
||||
|
||||
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed.');
|
||||
|
||||
// Converted to array options: yes
|
||||
// Converted to job_options: yes
|
||||
|
||||
// Migrate options to new-style storage - May 2014
|
||||
if (!is_array(UpdraftPlus_Options::get_updraft_option('updraft_ftp')) && '' != UpdraftPlus_Options::get_updraft_option('updraft_server_address', '')) {
|
||||
$opts = array(
|
||||
'user' => UpdraftPlus_Options::get_updraft_option('updraft_ftp_login'),
|
||||
'pass' => UpdraftPlus_Options::get_updraft_option('updraft_ftp_pass'),
|
||||
'host' => UpdraftPlus_Options::get_updraft_option('updraft_server_address'),
|
||||
'path' => UpdraftPlus_Options::get_updraft_option('updraft_ftp_remote_path'),
|
||||
'passive' => true
|
||||
);
|
||||
UpdraftPlus_Options::update_updraft_option('updraft_ftp', $opts);
|
||||
UpdraftPlus_Options::delete_updraft_option('updraft_server_address');
|
||||
UpdraftPlus_Options::delete_updraft_option('updraft_ftp_pass');
|
||||
UpdraftPlus_Options::delete_updraft_option('updraft_ftp_remote_path');
|
||||
UpdraftPlus_Options::delete_updraft_option('updraft_ftp_login');
|
||||
}
|
||||
|
||||
if (!class_exists('UpdraftPlus_BackupModule')) require_once(UPDRAFTPLUS_DIR.'/methods/backup-module.php');
|
||||
|
||||
class UpdraftPlus_BackupModule_ftp extends UpdraftPlus_BackupModule {
|
||||
|
||||
/**
|
||||
* Get FTP object with parameters set
|
||||
*
|
||||
* @param string $server Specify Server
|
||||
* @param string $user Specify Username
|
||||
* @param string $pass Specify Password
|
||||
* @param boolean $disable_ssl Indicate whether to disable SSL
|
||||
* @param boolean $disable_verify Indicate whether to disable verifiction
|
||||
* @param boolean $use_server_certs Indicate whether to use server certificates
|
||||
* @param boolean $passive Indicate whether to use passive FTP mode
|
||||
* @return array
|
||||
*/
|
||||
private function getFTP($server, $user, $pass, $disable_ssl = false, $disable_verify = true, $use_server_certs = false, $passive = true) {
|
||||
|
||||
if ('' == trim($server) || '' == trim($user) || '' == trim($pass)) return new WP_Error('no_settings', sprintf(__('No %s settings were found', 'updraftplus'), 'FTP'));
|
||||
|
||||
if (!class_exists('UpdraftPlus_ftp_wrapper')) include_once(UPDRAFTPLUS_DIR.'/includes/ftp.class.php');
|
||||
|
||||
$port = 21;
|
||||
if (preg_match('/^(.*):(\d+)$/', $server, $matches)) {
|
||||
$server = $matches[1];
|
||||
$port = $matches[2];
|
||||
}
|
||||
|
||||
$ftp = new UpdraftPlus_ftp_wrapper($server, $user, $pass, $port);
|
||||
|
||||
if ($disable_ssl) $ftp->ssl = false;
|
||||
$ftp->use_server_certs = $use_server_certs;
|
||||
$ftp->disable_verify = $disable_verify;
|
||||
$ftp->passive = ($passive) ? true : false;
|
||||
|
||||
return $ftp;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* WordPress options filter, sanitising the FTP options saved from the options page
|
||||
*
|
||||
* @param Array $settings - the options, prior to sanitisation
|
||||
*
|
||||
* @return Array - the sanitised options for saving
|
||||
*/
|
||||
public function options_filter($settings) {
|
||||
if (is_array($settings) && !empty($settings['version']) && !empty($settings['settings'])) {
|
||||
foreach ($settings['settings'] as $instance_id => $instance_settings) {
|
||||
if (!empty($instance_settings['host']) && preg_match('#ftp(es|s)?://(.*)#i', $instance_settings['host'], $matches)) {
|
||||
$settings['settings'][$instance_id]['host'] = rtrim($matches[2], "/ \t\n\r\0x0B");
|
||||
}
|
||||
if (isset($instance_settings['pass'])) {
|
||||
$settings['settings'][$instance_id]['pass'] = trim($instance_settings['pass'], "\n\r\0\x0B");
|
||||
}
|
||||
}
|
||||
}
|
||||
return $settings;
|
||||
}
|
||||
|
||||
public function get_supported_features() {
|
||||
// The 'multi_options' options format is handled via only accessing options via $this->get_options()
|
||||
return array('multi_options', 'config_templates', 'multi_storage');
|
||||
}
|
||||
|
||||
public function get_default_options() {
|
||||
return array(
|
||||
'host' => '',
|
||||
'user' => '',
|
||||
'pass' => '',
|
||||
'path' => '',
|
||||
'passive' => 1
|
||||
);
|
||||
}
|
||||
|
||||
public function backup($backup_array) {
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
$opts = $this->get_options();
|
||||
|
||||
$ftp = $this->getFTP(
|
||||
$opts['host'],
|
||||
$opts['user'],
|
||||
$opts['pass'],
|
||||
$updraftplus->get_job_option('updraft_ssl_nossl'),
|
||||
$updraftplus->get_job_option('updraft_ssl_disableverify'),
|
||||
$updraftplus->get_job_option('updraft_ssl_useservercerts'),
|
||||
$opts['passive']
|
||||
);
|
||||
|
||||
if (is_wp_error($ftp) || !$ftp->connect()) {
|
||||
if (is_wp_error($ftp)) {
|
||||
$updraftplus->log_wp_error($ftp);
|
||||
} else {
|
||||
$this->log("Failure: we did not successfully log in with those credentials.");
|
||||
}
|
||||
$this->log(__("login failure", 'updraftplus'), 'error');
|
||||
return false;
|
||||
}
|
||||
|
||||
// $ftp->make_dir(); we may need to recursively create dirs? TODO
|
||||
|
||||
$updraft_dir = $updraftplus->backups_dir_location().'/';
|
||||
|
||||
$ftp_remote_path = trailingslashit($opts['path']);
|
||||
foreach ($backup_array as $file) {
|
||||
$fullpath = $updraft_dir.$file;
|
||||
$this->log("upload attempt: $file -> ftp://".$opts['user']."@".$opts['host']."/${ftp_remote_path}${file}");
|
||||
$timer_start = microtime(true);
|
||||
$size_k = round(filesize($fullpath)/1024, 1);
|
||||
// Note :Setting $resume to true unnecessarily is not meant to be a problem. Only ever (Feb 2014) seen one weird FTP server where calling SIZE on a non-existent file did create a problem. So, this code just helps that case. (the check for non-empty upload_status[p] is being cautious.
|
||||
$upload_status = $updraftplus->jobdata_get('uploading_substatus');
|
||||
if (0 == $updraftplus->current_resumption || (is_array($upload_status) && !empty($upload_status['p']) && 0 == $upload_status['p'])) {
|
||||
$resume = false;
|
||||
} else {
|
||||
$resume = true;
|
||||
}
|
||||
|
||||
if ($ftp->put($fullpath, $ftp_remote_path.$file, FTP_BINARY, $resume, $updraftplus)) {
|
||||
$this->log("upload attempt successful (".$size_k."KB in ".(round(microtime(true)-$timer_start, 2)).'s)');
|
||||
$updraftplus->uploaded_file($file);
|
||||
} else {
|
||||
$this->log("ERROR: FTP upload failed");
|
||||
$this->log(__("upload failed", 'updraftplus'), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
return array('ftp_object' => $ftp, 'ftp_remote_path' => $ftp_remote_path);
|
||||
}
|
||||
|
||||
public function listfiles($match = 'backup_') {
|
||||
global $updraftplus;
|
||||
|
||||
$opts = $this->get_options();
|
||||
|
||||
$ftp = $this->getFTP(
|
||||
$opts['host'],
|
||||
$opts['user'],
|
||||
$opts['pass'],
|
||||
$updraftplus->get_job_option('updraft_ssl_nossl'),
|
||||
$updraftplus->get_job_option('updraft_ssl_disableverify'),
|
||||
$updraftplus->get_job_option('updraft_ssl_useservercerts'),
|
||||
$opts['passive']
|
||||
);
|
||||
|
||||
if (is_wp_error($ftp)) return $ftp;
|
||||
|
||||
if (!$ftp->connect()) return new WP_Error('ftp_login_failed', sprintf(__("%s login failure", 'updraftplus'), 'FTP'));
|
||||
|
||||
$ftp_remote_path = $opts['path'];
|
||||
if ($ftp_remote_path) $ftp_remote_path = trailingslashit($ftp_remote_path);
|
||||
|
||||
$dirlist = $ftp->dir_list($ftp_remote_path);
|
||||
if (!is_array($dirlist)) return array();
|
||||
|
||||
$results = array();
|
||||
|
||||
foreach ($dirlist as $k => $path) {
|
||||
|
||||
if ($ftp_remote_path) {
|
||||
// Feb 2015 - found a case where the directory path was not prefixed on
|
||||
if (0 !== strpos($path, $ftp_remote_path) && (false !== strpos('/', $ftp_remote_path) && false !== strpos('\\', $ftp_remote_path))) continue;
|
||||
if (0 === strpos($path, $ftp_remote_path)) $path = substr($path, strlen($ftp_remote_path));
|
||||
// if (0 !== strpos($path, $ftp_remote_path)) continue;
|
||||
// $path = substr($path, strlen($ftp_remote_path));
|
||||
if (0 === strpos($path, $match)) $results[]['name'] = $path;
|
||||
} else {
|
||||
if ('/' == substr($path, 0, 1)) $path = substr($path, 1);
|
||||
if (false !== strpos($path, '/')) continue;
|
||||
if (0 === strpos($path, $match)) $results[]['name'] = $path;
|
||||
}
|
||||
|
||||
unset($dirlist[$k]);
|
||||
}
|
||||
|
||||
// ftp_nlist() doesn't return file sizes. rawlist() does, but is tricky to parse. So, we get the sizes manually.
|
||||
foreach ($results as $ind => $name) {
|
||||
$size = $ftp->size($ftp_remote_path.$name['name']);
|
||||
if (0 === $size) {
|
||||
unset($results[$ind]);
|
||||
} elseif ($size>0) {
|
||||
$results[$ind]['size'] = $size;
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
|
||||
}
|
||||
|
||||
public function delete($files, $ftparr = array(), $sizeinfo = array()) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
|
||||
|
||||
global $updraftplus;
|
||||
if (is_string($files)) $files = array($files);
|
||||
|
||||
$opts = $this->get_options();
|
||||
|
||||
if (is_array($ftparr) && isset($ftparr['ftp_object'])) {
|
||||
$ftp = $ftparr['ftp_object'];
|
||||
} else {
|
||||
$ftp = $this->getFTP(
|
||||
$opts['host'],
|
||||
$opts['user'],
|
||||
$opts['pass'],
|
||||
$updraftplus->get_job_option('updraft_ssl_nossl'),
|
||||
$updraftplus->get_job_option('updraft_ssl_disableverify'),
|
||||
$updraftplus->get_job_option('updraft_ssl_useservercerts'),
|
||||
$opts['passive']
|
||||
);
|
||||
|
||||
if (is_wp_error($ftp) || !$ftp->connect()) {
|
||||
if (is_wp_error($ftp)) $updraftplus->log_wp_error($ftp);
|
||||
$this->log("Failure: we did not successfully log in with those credentials (host=".$opts['host'].").");
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$ftp_remote_path = isset($ftparr['ftp_remote_path']) ? $ftparr['ftp_remote_path'] : trailingslashit($opts['path']);
|
||||
|
||||
$ret = true;
|
||||
foreach ($files as $file) {
|
||||
if (@$ftp->delete($ftp_remote_path.$file)) {
|
||||
$this->log("delete: succeeded (${ftp_remote_path}${file})");
|
||||
} else {
|
||||
$this->log("delete: failed (${ftp_remote_path}${file})");
|
||||
$ret = false;
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
|
||||
}
|
||||
|
||||
public function download($file) {
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
$opts = $this->get_options();
|
||||
|
||||
$ftp = $this->getFTP(
|
||||
$opts['host'],
|
||||
$opts['user'],
|
||||
$opts['pass'],
|
||||
$updraftplus->get_job_option('updraft_ssl_nossl'),
|
||||
$updraftplus->get_job_option('updraft_ssl_disableverify'),
|
||||
$updraftplus->get_job_option('updraft_ssl_useservercerts'),
|
||||
$opts['passive']
|
||||
);
|
||||
|
||||
if (is_wp_error($ftp)) {
|
||||
$this->log('Failure to get FTP object: '.$ftp->get_error_code().': '.$ftp->get_error_message());
|
||||
$this->log($ftp->get_error_message().' ('.$ftp->get_error_code().')', 'error');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$ftp->connect()) {
|
||||
$this->log('Failure: we did not successfully log in with those credentials.');
|
||||
$this->log(__('login failure', 'updraftplus'), 'error');
|
||||
return false;
|
||||
}
|
||||
|
||||
// $ftp->make_dir(); we may need to recursively create dirs? TODO
|
||||
|
||||
$ftp_remote_path = trailingslashit($opts['path']);
|
||||
$fullpath = $updraftplus->backups_dir_location().'/'.$file;
|
||||
|
||||
$resume = false;
|
||||
if (file_exists($fullpath)) {
|
||||
$resume = true;
|
||||
$this->log("File already exists locally; will resume: size: ".filesize($fullpath));
|
||||
}
|
||||
|
||||
return $ftp->get($fullpath, $ftp_remote_path.$file, FTP_BINARY, $resume, $updraftplus);
|
||||
}
|
||||
|
||||
private function ftp_possible() {
|
||||
$funcs_disabled = array();
|
||||
foreach (array('ftp_connect', 'ftp_login', 'ftp_nb_fput') as $func) {
|
||||
if (!function_exists($func)) $funcs_disabled['ftp'][] = $func;
|
||||
}
|
||||
$funcs_disabled = apply_filters('updraftplus_ftp_possible', $funcs_disabled);
|
||||
return (0 == count($funcs_disabled)) ? true : $funcs_disabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the pre configuration template
|
||||
*
|
||||
* @return String - the template
|
||||
*/
|
||||
public function get_pre_configuration_template() {
|
||||
|
||||
global $updraftplus_admin;
|
||||
|
||||
$classes = $this->get_css_classes(false);
|
||||
|
||||
?>
|
||||
<tr class="<?php echo $classes . ' ' . 'ftp_pre_config_container';?>">
|
||||
<td colspan="2">
|
||||
<h3><?php echo 'FTP'; ?></h3>
|
||||
<?php
|
||||
$possible = $this->ftp_possible();
|
||||
|
||||
if (is_array($possible)) {
|
||||
// Check requirements.
|
||||
global $updraftplus_admin;
|
||||
$trans = array(
|
||||
'ftp' => __('regular non-encrypted FTP', 'updraftplus'),
|
||||
'ftpsslimplicit' => __('encrypted FTP (implicit encryption)', 'updraftplus'),
|
||||
'ftpsslexplicit' => __('encrypted FTP (explicit encryption)', 'updraftplus')
|
||||
);
|
||||
foreach ($possible as $type => $missing) {
|
||||
$updraftplus_admin->show_double_warning('<strong>'.__('Warning', 'updraftplus').':</strong> '. sprintf(__("Your web server's PHP installation has these functions disabled: %s.", 'updraftplus'), implode(', ', $missing)).' '.sprintf(__('Your hosting company must enable these functions before %s can work.', 'updraftplus'), $trans[$type]), 'ftp');
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<em><?php echo '<p>' . apply_filters('updraft_sftp_ftps_notice', '<strong>'.htmlspecialchars(__('Only non-encrypted FTP is supported by regular UpdraftPlus.')).'</strong> <a href="'.apply_filters("updraftplus_com_link", "https://updraftplus.com/shop/sftp/").'" target="_blank">'.__('If you want encryption (e.g. you are storing sensitive business data), then an add-on is available.', 'updraftplus')).'</a></p>'; ?></em>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configuration template
|
||||
*
|
||||
* @return String - the template, ready for substitutions to be carried out
|
||||
*/
|
||||
public function get_configuration_template() {
|
||||
|
||||
ob_start();
|
||||
|
||||
$classes = $this->get_css_classes();
|
||||
|
||||
?>
|
||||
|
||||
<tr class="<?php echo $classes;?>">
|
||||
<th><?php _e('FTP server', 'updraftplus');?>:</th>
|
||||
<td><input class="updraft_input--wide" type="text" size="40" data-updraft_settings_test="server" <?php $this->output_settings_field_name_and_id('host');?> value="{{host}}" /></td>
|
||||
</tr>
|
||||
|
||||
<tr class="<?php echo $classes;?>">
|
||||
<th><?php _e('FTP login', 'updraftplus');?>:</th>
|
||||
<td><input class="updraft_input--wide" type="text" size="40" data-updraft_settings_test="login" <?php $this->output_settings_field_name_and_id('user');?> value="{{user}}" /></td>
|
||||
</tr>
|
||||
|
||||
<tr class="<?php echo $classes;?>">
|
||||
<th><?php _e('FTP password', 'updraftplus');?>:</th>
|
||||
<td><input class="updraft_input--wide" type="<?php echo apply_filters('updraftplus_admin_secret_field_type', 'password'); ?>" size="40" data-updraft_settings_test="pass" <?php $this->output_settings_field_name_and_id('pass');?> value="{{pass}}" /></td>
|
||||
</tr>
|
||||
|
||||
<tr class="<?php echo $classes;?>">
|
||||
<th><?php _e('Remote path', 'updraftplus');?>:</th>
|
||||
<td><input class="updraft_input--wide" type="text" size="64" data-updraft_settings_test="path" <?php $this->output_settings_field_name_and_id('path');?> value="{{path}}" /> <em><?php _e('Needs to already exist', 'updraftplus');?></em></td>
|
||||
</tr>
|
||||
|
||||
<tr class="<?php echo $classes;?>">
|
||||
<th><?php _e('Passive mode', 'updraftplus');?>:</th>
|
||||
<td>
|
||||
<input type="checkbox" data-updraft_settings_test="passive" <?php $this->output_settings_field_name_and_id('passive');?> value="1" {{#ifeq '1' passive}}checked="checked"{{/ifeq}}> <br><em><?php echo __('Almost all FTP servers will want passive mode; but if you need active mode, then uncheck this.', 'updraftplus');?></em></td>
|
||||
</tr>
|
||||
|
||||
<?php
|
||||
|
||||
echo $this->get_test_button_html('FTP');
|
||||
|
||||
return ob_get_clean();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a test of user-supplied credentials, and echo the result
|
||||
*
|
||||
* @param Array $posted_settings - settings to test
|
||||
*/
|
||||
public function credentials_test($posted_settings) {
|
||||
|
||||
$server = $posted_settings['server'];
|
||||
$login = $posted_settings['login'];
|
||||
$pass = $posted_settings['pass'];
|
||||
$path = $posted_settings['path'];
|
||||
$nossl = $posted_settings['nossl'];
|
||||
$passive = empty($posted_settings['passive']) ? false : true;
|
||||
|
||||
$disable_verify = $posted_settings['disableverify'];
|
||||
$use_server_certs = $posted_settings['useservercerts'];
|
||||
|
||||
if (empty($server)) {
|
||||
_e('Failure: No server details were given.', 'updraftplus');
|
||||
return;
|
||||
}
|
||||
if (empty($login)) {
|
||||
printf(__('Failure: No %s was given.', 'updraftplus'), __('login', 'updraftplus'));
|
||||
return;
|
||||
}
|
||||
if (empty($pass)) {
|
||||
printf(__('Failure: No %s was given.', 'updraftplus'), __('password', 'updraftplus'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (preg_match('#ftp(es|s)?://(.*)#i', $server, $matches)) $server = untrailingslashit($matches[2]);
|
||||
|
||||
// $ftp = $this->getFTP($server, $login, $pass, $nossl, $disable_verify, $use_server_certs);
|
||||
$ftp = $this->getFTP($server, $login, $pass, $nossl, $disable_verify, $use_server_certs, $passive);
|
||||
|
||||
if (!$ftp->connect()) {
|
||||
_e('Failure: we did not successfully log in with those credentials.', 'updraftplus');
|
||||
return;
|
||||
}
|
||||
// $ftp->make_dir(); we may need to recursively create dirs? TODO
|
||||
|
||||
$file = md5(rand(0, 99999999)).'.tmp';
|
||||
$fullpath = trailingslashit($path).$file;
|
||||
|
||||
if ($ftp->put(ABSPATH.WPINC.'/version.php', $fullpath, FTP_BINARY, false, true)) {
|
||||
echo __("Success: we successfully logged in, and confirmed our ability to create a file in the given directory (login type:", 'updraftplus')." ".$ftp->login_type.')';
|
||||
@$ftp->delete($fullpath);
|
||||
} else {
|
||||
_e('Failure: we successfully logged in, but were not able to create a file in the given directory.', 'updraftplus');
|
||||
if (!empty($ftp->ssl)) {
|
||||
echo ' '.__('This is sometimes caused by a firewall - try turning off SSL in the expert settings, and testing again.', 'updraftplus');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether options have been set up by the user, or not
|
||||
*
|
||||
* @param Array $opts - the potential options
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
public function options_exist($opts) {
|
||||
if (is_array($opts) && !empty($opts['host']) && isset($opts['user']) && '' != $opts['user']) return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
33
wp-content/plugins/updraftplus/methods/googlecloud.php
Normal file
33
wp-content/plugins/updraftplus/methods/googlecloud.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access.');
|
||||
|
||||
if (class_exists('UpdraftPlus_BackupModule_googlecloud')) return;
|
||||
|
||||
if (version_compare(PHP_VERSION, '5.2.4', '>=')) {
|
||||
|
||||
if (class_exists('UpdraftPlus_Addons_RemoteStorage_googlecloud')) {
|
||||
class UpdraftPlus_BackupModule_googlecloud extends UpdraftPlus_Addons_RemoteStorage_googlecloud {
|
||||
public function __construct() {
|
||||
parent::__construct('googlecloud', 'Google Cloud', '5.2.4', 'googlecloud.png');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
include_once(UPDRAFTPLUS_DIR.'/methods/addon-not-yet-present.php');
|
||||
/**
|
||||
* N.B. UpdraftPlus_BackupModule_AddonNotYetPresent extends UpdraftPlus_BackupModule
|
||||
*/
|
||||
class UpdraftPlus_BackupModule_googlecloud extends UpdraftPlus_BackupModule_AddonNotYetPresent {
|
||||
public function __construct() {
|
||||
parent::__construct('googlecloud', 'Google Cloud', '5.2.4', 'googlecloud.png');
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
include_once(UPDRAFTPLUS_DIR.'/methods/insufficient.php');
|
||||
class UpdraftPlus_BackupModule_googlecloud extends UpdraftPlus_BackupModule_insufficientphp {
|
||||
public function __construct() {
|
||||
parent::__construct('googlecloud', 'Google Cloud', '5.2.4', 'googlecloud.png');
|
||||
}
|
||||
}
|
||||
}
|
||||
1466
wp-content/plugins/updraftplus/methods/googledrive.php
Normal file
1466
wp-content/plugins/updraftplus/methods/googledrive.php
Normal file
File diff suppressed because it is too large
Load Diff
128
wp-content/plugins/updraftplus/methods/insufficient.php
Normal file
128
wp-content/plugins/updraftplus/methods/insufficient.php
Normal file
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed.');
|
||||
|
||||
if (!class_exists('UpdraftPlus_BackupModule')) require_once(UPDRAFTPLUS_DIR.'/methods/backup-module.php');
|
||||
|
||||
class UpdraftPlus_BackupModule_insufficientphp extends UpdraftPlus_BackupModule {
|
||||
|
||||
private $required_php;
|
||||
|
||||
private $error_msg;
|
||||
|
||||
private $method;
|
||||
|
||||
public function __construct($method, $desc, $php, $image = null) {
|
||||
$this->method = $method;
|
||||
$this->desc = $desc;
|
||||
$this->required_php = $php;
|
||||
$this->image = $image;
|
||||
$this->error_msg = 'This remote storage method ('.$this->desc.') requires PHP '.$this->required_php.' or later';
|
||||
$this->error_msg_trans = sprintf(__('This remote storage method (%s) requires PHP %s or later.', 'updraftplus'), $this->desc, $this->required_php);
|
||||
}
|
||||
|
||||
private function log_error() {
|
||||
global $updraftplus;
|
||||
$updraftplus->log($this->error_msg);
|
||||
$updraftplus->log($this->error_msg_trans, 'error', 'insufficientphp');
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* backup method: takes an array, and shovels them off to the cloud storage
|
||||
*
|
||||
* @param array $backup_array An array backups
|
||||
* @return array
|
||||
*/
|
||||
public function backup($backup_array) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
|
||||
return $this->log_error();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a list of supported features for this storage method
|
||||
*
|
||||
* Currently known features:
|
||||
*
|
||||
* - multi_options : indicates that the remote storage module
|
||||
* can handle its options being in the Feb-2017 multi-options
|
||||
* format. N.B. This only indicates options handling, not any
|
||||
* other multi-destination options.
|
||||
*
|
||||
* - multi_servers : not implemented yet: indicates that the
|
||||
* remote storage module can handle multiple servers at backup
|
||||
* time. This should not be specified without multi_options.
|
||||
* multi_options without multi_servers is fine - it will just
|
||||
* cause only the first entry in the options array to be used.
|
||||
*
|
||||
* - config_templates : not implemented yet: indicates that
|
||||
* the remote storage module can output its configuration in
|
||||
* Handlebars format via the get_configuration_template() method.
|
||||
*
|
||||
* @return Array - an array of supported features (any features not
|
||||
* mentioned are assumed to not be supported)
|
||||
*/
|
||||
public function get_supported_features() {
|
||||
// The 'multi_options' options format is handled via only accessing options via $this->get_options()
|
||||
return array('multi_options', 'config_templates');
|
||||
}
|
||||
|
||||
/**
|
||||
* $match: a substring to require (tested via strpos() !== false)
|
||||
*
|
||||
* @param string $match THis will specify which match is used for the SQL but by default it is set to 'backup_' unless specified
|
||||
* @return array
|
||||
*/
|
||||
public function listfiles($match = 'backup_') {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
|
||||
return new WP_Error('insufficient_php', $this->error_msg_trans);
|
||||
}
|
||||
|
||||
/**
|
||||
* delete method: takes an array of file names (base name) or a single string, and removes them from the cloud storage
|
||||
*
|
||||
* @param string $files List of files
|
||||
* @param boolean $data Specifies data or not
|
||||
* @param array $sizeinfo This is the size info on the file.
|
||||
* @return array
|
||||
*/
|
||||
public function delete($files, $data = false, $sizeinfo = array()) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
|
||||
return $this->log_error();
|
||||
}
|
||||
|
||||
/**
|
||||
* download method: takes a file name (base name), and brings it back from the cloud storage into Updraft's directory
|
||||
* You can register errors with $updraftplus->log("my error message", 'error')
|
||||
*
|
||||
* @param string $file List of files
|
||||
* @return array
|
||||
*/
|
||||
public function download($file) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
|
||||
return $this->log_error();
|
||||
}
|
||||
|
||||
private function extra_config() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configuration template
|
||||
*
|
||||
* @return String - the template, ready for substitutions to be carried out
|
||||
*/
|
||||
public function get_configuration_template() {
|
||||
ob_start();
|
||||
$this->extra_config();
|
||||
?>
|
||||
<tr class="updraftplusmethod <?php echo $this->method;?>">
|
||||
<th><?php echo htmlspecialchars($this->desc);?>:</th>
|
||||
<td>
|
||||
<em>
|
||||
<?php echo (!empty($this->image)) ? '<p><img src="'.UPDRAFTPLUS_URL.'/images/'.$this->image.'"></p>' : ''; ?>
|
||||
<?php echo htmlspecialchars($this->error_msg_trans);?>
|
||||
<?php echo htmlspecialchars(__('You will need to ask your web hosting company to upgrade.', 'updraftplus'));?>
|
||||
<?php echo sprintf(__('Your %s version: %s.', 'updraftplus'), 'PHP', phpversion());?>
|
||||
</em>
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
}
|
||||
36
wp-content/plugins/updraftplus/methods/onedrive.php
Normal file
36
wp-content/plugins/updraftplus/methods/onedrive.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access.');
|
||||
|
||||
if (version_compare(phpversion(), '5.3.3', '>=')) {
|
||||
|
||||
if (class_exists('UpdraftPlus_Addons_RemoteStorage_onedrive')) {
|
||||
|
||||
class UpdraftPlus_BackupModule_onedrive extends UpdraftPlus_Addons_RemoteStorage_onedrive {
|
||||
public function __construct() {
|
||||
parent::__construct('onedrive', 'Microsoft OneDrive', '5.3.3', 'onedrive.png');
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
include_once(UPDRAFTPLUS_DIR.'/methods/addon-not-yet-present.php');
|
||||
/**
|
||||
* N.B. UpdraftPlus_BackupModule_AddonNotYetPresent extends UpdraftPlus_BackupModule
|
||||
*/
|
||||
class UpdraftPlus_BackupModule_onedrive extends UpdraftPlus_BackupModule_AddonNotYetPresent {
|
||||
public function __construct() {
|
||||
parent::__construct('onedrive', 'Microsoft OneDrive', '5.3.3', 'onedrive.png');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
include_once(UPDRAFTPLUS_DIR.'/methods/insufficient.php');
|
||||
class UpdraftPlus_BackupModule_onedrive extends UpdraftPlus_BackupModule_insufficientphp {
|
||||
public function __construct() {
|
||||
parent::__construct('onedrive', 'Microsoft OneDrive', '5.3.3', 'onedrive.png');
|
||||
}
|
||||
}
|
||||
}
|
||||
596
wp-content/plugins/updraftplus/methods/openstack-base.php
Normal file
596
wp-content/plugins/updraftplus/methods/openstack-base.php
Normal file
@@ -0,0 +1,596 @@
|
||||
<?php
|
||||
|
||||
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed.');
|
||||
|
||||
if (!class_exists('UpdraftPlus_BackupModule')) require_once(UPDRAFTPLUS_DIR.'/methods/backup-module.php');
|
||||
|
||||
class UpdraftPlus_BackupModule_openstack_base extends UpdraftPlus_BackupModule {
|
||||
|
||||
protected $chunk_size;
|
||||
|
||||
protected $client;
|
||||
|
||||
protected $method;
|
||||
|
||||
protected $desc;
|
||||
|
||||
protected $long_desc;
|
||||
|
||||
protected $img_url;
|
||||
|
||||
public function __construct($method, $desc, $long_desc = null, $img_url = '') {
|
||||
$this->method = $method;
|
||||
$this->desc = $desc;
|
||||
$this->long_desc = (is_string($long_desc)) ? $long_desc : $desc;
|
||||
$this->img_url = $img_url;
|
||||
}
|
||||
|
||||
public function backup($backup_array) {
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
$default_chunk_size = (defined('UPDRAFTPLUS_UPLOAD_CHUNKSIZE') && UPDRAFTPLUS_UPLOAD_CHUNKSIZE > 0) ? max(UPDRAFTPLUS_UPLOAD_CHUNKSIZE, 1048576) : 5242880;
|
||||
|
||||
$this->chunk_size = $updraftplus->jobdata_get('openstack_chunk_size', $default_chunk_size);
|
||||
|
||||
$opts = $this->get_options();
|
||||
|
||||
$this->container = $opts['path'];
|
||||
|
||||
try {
|
||||
$storage = $this->get_openstack_service($opts, UpdraftPlus_Options::get_updraft_option('updraft_ssl_useservercerts'), UpdraftPlus_Options::get_updraft_option('updraft_ssl_disableverify'));
|
||||
} catch (AuthenticationError $e) {
|
||||
$updraftplus->log($this->desc.' authentication failed ('.$e->getMessage().')');
|
||||
$updraftplus->log(sprintf(__('%s authentication failed', 'updraftplus'), $this->desc).' ('.$e->getMessage().')', 'error');
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
$updraftplus->log($this->desc.' error - failed to access the container ('.$e->getMessage().') (line: '.$e->getLine().', file: '.$e->getFile().')');
|
||||
$updraftplus->log(sprintf(__('%s error - failed to access the container', 'updraftplus'), $this->desc).' ('.$e->getMessage().')', 'error');
|
||||
return false;
|
||||
}
|
||||
// Get the container
|
||||
try {
|
||||
$this->container_object = $storage->getContainer($this->container);
|
||||
} catch (Exception $e) {
|
||||
$updraftplus->log('Could not access '.$this->desc.' container ('.get_class($e).', '.$e->getMessage().') (line: '.$e->getLine().', file: '.$e->getFile().')');
|
||||
$updraftplus->log(sprintf(__('Could not access %s container', 'updraftplus'), $this->desc).' ('.get_class($e).', '.$e->getMessage().')', 'error');
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($backup_array as $key => $file) {
|
||||
|
||||
$file_key = 'status_'.md5($file);
|
||||
$file_status = $this->jobdata_get($file_key, null, 'openstack_'.$file_key);
|
||||
if (is_array($file_status) && !empty($file_status['chunks']) && !empty($file_status['chunks'][1]['size'])) $this->chunk_size = $file_status['chunks'][1]['size'];
|
||||
|
||||
// First, see the object's existing size (if any)
|
||||
$uploaded_size = $this->get_remote_size($file);
|
||||
|
||||
try {
|
||||
if (1 === $updraftplus->chunked_upload($this, $file, $this->method."://".$this->container."/$file", $this->desc, $this->chunk_size, $uploaded_size)) {
|
||||
try {
|
||||
if (false !== ($data = fopen($updraftplus->backups_dir_location().'/'.$file, 'r+'))) {
|
||||
$this->container_object->uploadObject($file, $data);
|
||||
$updraftplus->log($this->desc." regular upload: success");
|
||||
$updraftplus->uploaded_file($file);
|
||||
} else {
|
||||
throw new Exception('uploadObject failed: fopen failed');
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->log("$logname regular upload: failed ($file) (".$e->getMessage().")");
|
||||
$this->log("$file: ".sprintf(__('%s Error: Failed to upload', 'updraftplus'), $logname), 'error');
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$updraftplus->log($this->desc.' error - failed to upload file'.' ('.$e->getMessage().') (line: '.$e->getLine().', file: '.$e->getFile().')');
|
||||
$updraftplus->log(sprintf(__('%s error - failed to upload file', 'updraftplus'), $this->desc).' ('.$e->getMessage().')', 'error');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return array('object' => $this->container_object, 'orig_path' => $opts['path'], 'container' => $this->container);
|
||||
|
||||
}
|
||||
|
||||
private function get_remote_size($file) {
|
||||
try {
|
||||
$response = $this->container_object->getClient()->head($this->container_object->getUrl($file))->send();
|
||||
$response_object = $this->container_object->dataObject()->populateFromResponse($response)->setName($file);
|
||||
return $response_object->getContentLength();
|
||||
} catch (Exception $e) {
|
||||
// Allow caller to distinguish between zero-sized and not-found
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function lists the files found in the configured storage location
|
||||
*
|
||||
* @param String $match a substring to require (tested via strpos() !== false)
|
||||
*
|
||||
* @return Array - each file is represented by an array with entries 'name' and (optional) 'size'
|
||||
*/
|
||||
public function listfiles($match = 'backup_') {
|
||||
$opts = $this->get_options();
|
||||
$container = $opts['path'];
|
||||
$path = $container;
|
||||
|
||||
if (empty($opts['user']) || (empty($opts['apikey']) && empty($opts['password']))) return new WP_Error('no_settings', __('No settings were found', 'updraftplus'));
|
||||
|
||||
try {
|
||||
$storage = $this->get_openstack_service($opts, UpdraftPlus_Options::get_updraft_option('updraft_ssl_useservercerts'), UpdraftPlus_Options::get_updraft_option('updraft_ssl_disableverify'));
|
||||
} catch (Exception $e) {
|
||||
return new WP_Error('no_access', sprintf(__('%s error - failed to access the container', 'updraftplus'), $this->desc).' ('.$e->getMessage().')');
|
||||
}
|
||||
|
||||
// Get the container
|
||||
try {
|
||||
$this->container_object = $storage->getContainer($container);
|
||||
} catch (Exception $e) {
|
||||
return new WP_Error('no_access', sprintf(__('%s error - failed to access the container', 'updraftplus'), $this->desc).' ('.$e->getMessage().')');
|
||||
}
|
||||
|
||||
$results = array();
|
||||
$marker = '';
|
||||
$page_size = 1000;
|
||||
try {
|
||||
// http://php-opencloud.readthedocs.io/en/latest/services/object-store/objects.html#list-objects-in-a-container
|
||||
while (null !== $marker) {
|
||||
|
||||
$params = array(
|
||||
'prefix' => $match,
|
||||
'limit' => $page_size,
|
||||
'marker' => $marker
|
||||
);
|
||||
|
||||
$objects = $this->container_object->objectList($params);
|
||||
|
||||
$total = $objects->count();
|
||||
|
||||
if (0 == $total) break;
|
||||
|
||||
$index = 0;
|
||||
|
||||
while (false !== ($file = $objects->offsetGet($index)) && !empty($file)) {
|
||||
$index++;
|
||||
try {
|
||||
if ((is_object($file) && !empty($file->name))) {
|
||||
$result = array('name' => $file->name);
|
||||
// Rackspace returns the size of a manifested file properly; other OpenStack implementations may not
|
||||
if (!empty($file->bytes)) {
|
||||
$result['size'] = $file->bytes;
|
||||
} else {
|
||||
$size = $this->get_remote_size($file->name);
|
||||
if (false !== $size && $size > 0) $result['size'] = $size;
|
||||
}
|
||||
$results[] = $result;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// Catch
|
||||
}
|
||||
$marker = (!empty($file->name) && $total >= $page_size) ? $file->name : null;
|
||||
}
|
||||
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// Catch
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when all chunks have been uploaded, to allow any required finishing actions to be carried out
|
||||
*
|
||||
* @param String $file - the basename of the file being uploaded
|
||||
*
|
||||
* @return Boolean - success or failure state of any finishing actions
|
||||
*/
|
||||
public function chunked_upload_finish($file) {
|
||||
|
||||
$chunk_path = 'chunk-do-not-delete-'.$file;
|
||||
try {
|
||||
|
||||
$headers = array(
|
||||
'Content-Length' => 0,
|
||||
'X-Object-Manifest' => sprintf('%s/%s', $this->container, $chunk_path.'_')
|
||||
);
|
||||
|
||||
$url = $this->container_object->getUrl($file);
|
||||
$this->container_object->getClient()->put($url, $headers)->send();
|
||||
return true;
|
||||
|
||||
} catch (Exception $e) {
|
||||
global $updraftplus;
|
||||
$updraftplus->log("Error when sending manifest (".get_class($e)."): ".$e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* N.B. Since we use varying-size chunks, we must be careful as to what we do with $chunk_index
|
||||
*
|
||||
* @param String $file Full path for the file being uploaded
|
||||
* @param Resource $fp File handle to read upload data from
|
||||
* @param Integer $chunk_index Index of chunked upload
|
||||
* @param Integer $upload_size Size of the upload, in bytes
|
||||
* @param Integer $upload_start How many bytes into the file the upload process has got
|
||||
* @param Integer $upload_end How many bytes into the file we will be after this chunk is uploaded
|
||||
* @param Integer $total_file_size Total file size
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
public function chunked_upload($file, $fp, $chunk_index, $upload_size, $upload_start, $upload_end, $total_file_size) {
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
$file_key = 'status_'.md5($file);
|
||||
$file_status = $this->jobdata_get($file_key, null, 'openstack_'.$file_key);
|
||||
|
||||
$next_chunk_size = $upload_size;
|
||||
|
||||
$bytes_already_uploaded = 0;
|
||||
|
||||
$last_uploaded_chunk_index = 0;
|
||||
|
||||
// Once a chunk is uploaded, its status is set, allowing the sequence to be reconstructed
|
||||
if (is_array($file_status) && isset($file_status['chunks']) && !empty($file_status['chunks'])) {
|
||||
foreach ($file_status['chunks'] as $c_id => $c_status) {
|
||||
if ($c_id > $last_uploaded_chunk_index) $last_uploaded_chunk_index = $c_id;
|
||||
if ($chunk_index + 1 == $c_id) {
|
||||
$next_chunk_size = $c_status['size'];
|
||||
}
|
||||
$bytes_already_uploaded += $c_status['size'];
|
||||
}
|
||||
} else {
|
||||
$file_status = array('chunks' => array());
|
||||
}
|
||||
|
||||
$this->jobdata_set($file_key, $file_status);
|
||||
|
||||
if ($upload_start < $bytes_already_uploaded) {
|
||||
if ($next_chunk_size != $upload_size) {
|
||||
$response = new stdClass;
|
||||
$response->new_chunk_size = $upload_size;
|
||||
$response->log = false;
|
||||
return $response;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Shouldn't be able to happen
|
||||
if ($chunk_index <= $last_uploaded_chunk_index) {
|
||||
$updraftplus->log($this->desc.": Chunk sequence error; chunk_index=$chunk_index, last_uploaded_chunk_index=$last_uploaded_chunk_index, upload_start=$upload_start, upload_end=$upload_end, file_status=".json_encode($file_status));
|
||||
}
|
||||
|
||||
// Used to use $chunk_index here, before switching to variable chunk sizes
|
||||
$upload_remotepath = 'chunk-do-not-delete-'.$file.'_'.sprintf("%016d", $chunk_index);
|
||||
|
||||
$remote_size = $this->get_remote_size($upload_remotepath);
|
||||
|
||||
// Without this, some versions of Curl add Expect: 100-continue, which results in Curl then giving this back: curl error: 55) select/poll returned error
|
||||
// Didn't make the difference - instead we just check below for actual success even when Curl reports an error
|
||||
// $chunk_object->headers = array('Expect' => '');
|
||||
|
||||
if ($remote_size >= $upload_size) {
|
||||
$updraftplus->log($this->desc.": Chunk ($upload_start - $upload_end, $chunk_index): already uploaded");
|
||||
} else {
|
||||
$updraftplus->log($this->desc.": Chunk ($upload_start - $upload_end, $chunk_index): begin upload");
|
||||
// Upload the chunk
|
||||
try {
|
||||
$data = fread($fp, $upload_size);
|
||||
$time_start = microtime(true);
|
||||
$this->container_object->uploadObject($upload_remotepath, $data);
|
||||
$time_now = microtime(true);
|
||||
$time_taken = $time_now - $time_start;
|
||||
if ($next_chunk_size < 52428800 && $total_file_size > 0 && $upload_end + 1 < $total_file_size) {
|
||||
$job_run_time = $time_now - $updraftplus->job_time_ms;
|
||||
if ($time_taken < 10) {
|
||||
$upload_rate = $upload_size / max($time_taken, 0.0001);
|
||||
$upload_secs = min(floor($job_run_time), 10);
|
||||
if ($job_run_time < 15) $upload_secs = max(6, $job_run_time*0.6);
|
||||
|
||||
// In megabytes
|
||||
$memory_limit_mb = $updraftplus->memory_check_current();
|
||||
$bytes_used = memory_get_usage();
|
||||
$bytes_free = $memory_limit_mb * 1048576 - $bytes_used;
|
||||
|
||||
$new_chunk = max(min($upload_secs * $upload_rate * 0.9, 52428800, $bytes_free), 5242880);
|
||||
$new_chunk = $new_chunk - ($new_chunk % 5242880);
|
||||
$next_chunk_size = (int) $new_chunk;
|
||||
$updraftplus->jobdata_set('openstack_chunk_size', $next_chunk_size);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
$updraftplus->log($this->desc." chunk upload: error: ($file / $chunk_index) (".$e->getMessage().") (line: ".$e->getLine().', file: '.$e->getFile().')');
|
||||
// Experience shows that Curl sometimes returns a select/poll error (curl error 55) even when everything succeeded. Google seems to indicate that this is a known bug.
|
||||
|
||||
$remote_size = $this->get_remote_size($upload_remotepath);
|
||||
|
||||
if ($remote_size >= $upload_size) {
|
||||
$updraftplus->log("$file: Chunk now exists; ignoring error (presuming it was an apparently known curl bug)");
|
||||
} else {
|
||||
$updraftplus->log("$file: ".sprintf(__('%s Error: Failed to upload', 'updraftplus'), $this->desc), 'error');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$file_status['chunks'][$chunk_index]['size'] = $upload_size;
|
||||
|
||||
$this->jobdata_set($file_key, $file_status);
|
||||
|
||||
if ($next_chunk_size != $upload_size) {
|
||||
$response = new stdClass;
|
||||
$response->new_chunk_size = $next_chunk_size;
|
||||
$response->log = true;
|
||||
return $response;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function delete($files, $data = false, $sizeinfo = array()) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
|
||||
|
||||
global $updraftplus;
|
||||
if (is_string($files)) $files = array($files);
|
||||
|
||||
if (is_array($data)) {
|
||||
$container_object = $data['object'];
|
||||
$container = $data['container'];
|
||||
$path = $data['orig_path'];
|
||||
} else {
|
||||
$opts = $this->get_options();
|
||||
$container = $opts['path'];
|
||||
$path = $container;
|
||||
try {
|
||||
$storage = $this->get_openstack_service($opts, UpdraftPlus_Options::get_updraft_option('updraft_ssl_useservercerts'), UpdraftPlus_Options::get_updraft_option('updraft_ssl_disableverify'));
|
||||
} catch (AuthenticationError $e) {
|
||||
$updraftplus->log($this->desc.' authentication failed ('.$e->getMessage().')');
|
||||
$updraftplus->log(sprintf(__('%s authentication failed', 'updraftplus'), $this->desc).' ('.$e->getMessage().')', 'error');
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
$updraftplus->log($this->desc.' error - failed to access the container ('.$e->getMessage().')');
|
||||
$updraftplus->log(sprintf(__('%s error - failed to access the container', 'updraftplus'), $this->desc).' ('.$e->getMessage().')', 'error');
|
||||
return false;
|
||||
}
|
||||
// Get the container
|
||||
try {
|
||||
$container_object = $storage->getContainer($container);
|
||||
} catch (Exception $e) {
|
||||
$updraftplus->log('Could not access '.$this->desc.' container ('.get_class($e).', '.$e->getMessage().')');
|
||||
$updraftplus->log(sprintf(__('Could not access %s container', 'updraftplus'), $this->desc).' ('.get_class($e).', '.$e->getMessage().')', 'error');
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$ret = true;
|
||||
foreach ($files as $file) {
|
||||
|
||||
$updraftplus->log($this->desc.": Delete remote: container=$container, path=$file");
|
||||
|
||||
// We need to search for chunks
|
||||
$chunk_path = "chunk-do-not-delete-".$file;
|
||||
|
||||
try {
|
||||
$objects = $container_object->objectList(array('prefix' => $chunk_path));
|
||||
$index = 0;
|
||||
while (false !== ($chunk = $objects->offsetGet($index)) && !empty($chunk)) {
|
||||
try {
|
||||
$name = $chunk->name;
|
||||
$container_object->dataObject()->setName($name)->delete();
|
||||
$updraftplus->log($this->desc.': Chunk deleted: '.$name);
|
||||
} catch (Exception $e) {
|
||||
$updraftplus->log($this->desc." chunk delete failed: $name: ".$e->getMessage());
|
||||
}
|
||||
$index++;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$updraftplus->log($this->desc.' chunk delete failed: '.$e->getMessage());
|
||||
}
|
||||
|
||||
// Finally, delete the object itself
|
||||
try {
|
||||
$container_object->dataObject()->setName($file)->delete();
|
||||
$updraftplus->log($this->desc.': Deleted: '.$file);
|
||||
} catch (Exception $e) {
|
||||
$updraftplus->log($this->desc.' delete failed: '.$e->getMessage());
|
||||
$ret = false;
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function download($file) {
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
$opts = $this->get_options();
|
||||
|
||||
try {
|
||||
$storage = $this->get_openstack_service($opts, UpdraftPlus_Options::get_updraft_option('updraft_ssl_useservercerts'), UpdraftPlus_Options::get_updraft_option('updraft_ssl_disableverify'));
|
||||
} catch (AuthenticationError $e) {
|
||||
$updraftplus->log($this->desc.' authentication failed ('.$e->getMessage().')');
|
||||
$updraftplus->log(sprintf(__('%s authentication failed', 'updraftplus'), $this->desc).' ('.$e->getMessage().')', 'error');
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
$updraftplus->log($this->desc.' error - failed to access the container ('.$e->getMessage().')');
|
||||
$updraftplus->log(sprintf(__('%s error - failed to access the container', 'updraftplus'), $this->desc).' ('.$e->getMessage().')', 'error');
|
||||
return false;
|
||||
}
|
||||
|
||||
$container = untrailingslashit($opts['path']);
|
||||
$updraftplus->log($this->desc." download: ".$this->method."://$container/$file");
|
||||
|
||||
// Get the container
|
||||
try {
|
||||
$this->container_object = $storage->getContainer($container);
|
||||
} catch (Exception $e) {
|
||||
$updraftplus->log('Could not access '.$this->desc.' container ('.get_class($e).', '.$e->getMessage().')');
|
||||
$updraftplus->log(sprintf(__('Could not access %s container', 'updraftplus'), $this->desc).' ('.get_class($e).', '.$e->getMessage().')', 'error');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get information about the object within the container
|
||||
$remote_size = $this->get_remote_size($file);
|
||||
if (false === $remote_size) {
|
||||
$updraftplus->log('Could not access '.$this->desc.' object');
|
||||
$updraftplus->log(sprintf(__('The %s object was not found', 'updraftplus'), $this->desc), 'error');
|
||||
return false;
|
||||
}
|
||||
|
||||
return (!is_bool($remote_size)) ? $updraftplus->chunked_download($file, $this, $remote_size, true, $this->container_object) : false;
|
||||
|
||||
}
|
||||
|
||||
public function chunked_download($file, $headers, $container_object) {
|
||||
try {
|
||||
$dl = $container_object->getObject($file, $headers);
|
||||
} catch (Exception $e) {
|
||||
global $updraftplus;
|
||||
$updraftplus->log("$file: Failed to download (".$e->getMessage().")");
|
||||
$updraftplus->log("$file: ".sprintf(__("%s Error", 'updraftplus'), $this->desc).": ".__('Error downloading remote file: Failed to download', 'updraftplus').' ('.$e->getMessage().")", 'error');
|
||||
return false;
|
||||
}
|
||||
return $dl->getContent();
|
||||
}
|
||||
|
||||
public function credentials_test_go($opts, $path, $useservercerts, $disableverify) {
|
||||
|
||||
if (preg_match("#^([^/]+)/(.*)$#", $path, $bmatches)) {
|
||||
$container = $bmatches[1];
|
||||
$path = $bmatches[2];
|
||||
} else {
|
||||
$container = $path;
|
||||
$path = '';
|
||||
}
|
||||
|
||||
if (empty($container)) {
|
||||
_e('Failure: No container details were given.', 'updraftplus');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$storage = $this->get_openstack_service($opts, $useservercerts, $disableverify);
|
||||
// @codingStandardsIgnoreLine
|
||||
} catch (Guzzle\Http\Exception\ClientErrorResponseException $e) {
|
||||
$response = $e->getResponse();
|
||||
$code = $response->getStatusCode();
|
||||
$reason = $response->getReasonPhrase();
|
||||
if (401 == $code && 'Unauthorized' == $reason) {
|
||||
echo __('Authorisation failed (check your credentials)', 'updraftplus');
|
||||
} else {
|
||||
echo __('Authorisation failed (check your credentials)', 'updraftplus')." ($code:$reason)";
|
||||
}
|
||||
return;
|
||||
} catch (AuthenticationError $e) {
|
||||
echo sprintf(__('%s authentication failed', 'updraftplus'), $this->desc).' ('.$e->getMessage().')';
|
||||
return;
|
||||
} catch (Exception $e) {
|
||||
echo sprintf(__('%s authentication failed', 'updraftplus'), $this->desc).' ('.get_class($e).', '.$e->getMessage().')';
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$container_object = $storage->getContainer($container);
|
||||
// @codingStandardsIgnoreLine
|
||||
} catch (Guzzle\Http\Exception\ClientErrorResponseException $e) {
|
||||
$response = $e->getResponse();
|
||||
$code = $response->getStatusCode();
|
||||
$reason = $response->getReasonPhrase();
|
||||
if (404 == $code) {
|
||||
$container_object = $storage->createContainer($container);
|
||||
} else {
|
||||
echo __('Authorisation failed (check your credentials)', 'updraftplus')." ($code:$reason)";
|
||||
return;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
echo sprintf(__('%s authentication failed', 'updraftplus'), $this->desc).' ('.get_class($e).', '.$e->getMessage().')';
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_a($container_object, 'OpenCloud\ObjectStore\Resource\Container') && !is_a($container_object, 'Container')) {
|
||||
echo sprintf(__('%s authentication failed', 'updraftplus'), $this->desc).' ('.get_class($container_object).')';
|
||||
return;
|
||||
}
|
||||
|
||||
$try_file = md5(rand()).'.txt';
|
||||
|
||||
try {
|
||||
$object = $container_object->uploadObject($try_file, 'UpdraftPlus test file', array('content-type' => 'text/plain'));
|
||||
} catch (Exception $e) {
|
||||
echo sprintf(__('%s error - we accessed the container, but failed to create a file within it', 'updraftplus'), $this->desc).' ('.get_class($e).', '.$e->getMessage().')';
|
||||
if (!empty($this->region)) echo ' '.sprintf(__('Region: %s', 'updraftplus'), $this->region);
|
||||
return;
|
||||
}
|
||||
|
||||
echo __('Success', 'updraftplus').": ".__('We accessed the container, and were able to create files within it.', 'updraftplus');
|
||||
if (!empty($this->region)) echo ' '.sprintf(__('Region: %s', 'updraftplus'), $this->region);
|
||||
|
||||
try {
|
||||
if (!empty($object)) {
|
||||
// One OpenStack server we tested on did not delete unless we slept... some kind of race condition at their end
|
||||
sleep(1);
|
||||
$object->delete();
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// Catch
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the pre configuration template
|
||||
*
|
||||
* @return String - the template
|
||||
*/
|
||||
public function get_pre_configuration_template() {
|
||||
|
||||
global $updraftplus_admin;
|
||||
|
||||
$classes = $this->get_css_classes(false);
|
||||
|
||||
?>
|
||||
<tr class="<?php echo $classes . ' ' . $this->method . '_pre_config_container';?>">
|
||||
<td colspan="2">
|
||||
<?php
|
||||
if (!empty($this->img_url)) {
|
||||
?>
|
||||
<img alt="<?php echo $this->long_desc; ?>" src="<?php echo UPDRAFTPLUS_URL.$this->img_url; ?>">
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<br>
|
||||
<?php
|
||||
// Check requirements.
|
||||
global $updraftplus_admin;
|
||||
if (!function_exists('mb_substr')) {
|
||||
$updraftplus_admin->show_double_warning('<strong>'.__('Warning', 'updraftplus').':</strong> '.sprintf(__('Your web server\'s PHP installation does not included a required module (%s). Please contact your web hosting provider\'s support.', 'updraftplus'), 'mbstring').' '.sprintf(__("UpdraftPlus's %s module <strong>requires</strong> %s. Please do not file any support requests; there is no alternative.", 'updraftplus'), $this->desc, 'mbstring'), $this->method);
|
||||
}
|
||||
$updraftplus_admin->curl_check($this->long_desc, false, $this->method);
|
||||
echo '<br>';
|
||||
$this->get_pre_configuration_middlesection_template();
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configuration template
|
||||
*
|
||||
* @return String - the template, ready for substitutions to be carried out
|
||||
*/
|
||||
public function get_configuration_template() {
|
||||
ob_start();
|
||||
$classes = $this->get_css_classes();
|
||||
$template_str = ob_get_clean();
|
||||
$template_str .= $this->get_configuration_middlesection_template();
|
||||
$template_str .= $this->get_test_button_html($this->desc);
|
||||
return $template_str;
|
||||
}
|
||||
}
|
||||
15
wp-content/plugins/updraftplus/methods/openstack.php
Normal file
15
wp-content/plugins/updraftplus/methods/openstack.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access.');
|
||||
|
||||
// Necessary to place the code in a separate file, because it uses namespaces, which cause a fatal error in PHP 5.2
|
||||
if (version_compare(phpversion(), '5.3.3', '>=')) {
|
||||
include_once(UPDRAFTPLUS_DIR.'/methods/openstack2.php');
|
||||
} else {
|
||||
include_once(UPDRAFTPLUS_DIR.'/methods/insufficient.php');
|
||||
class UpdraftPlus_BackupModule_openstack extends UpdraftPlus_BackupModule_insufficientphp {
|
||||
public function __construct() {
|
||||
parent::__construct('openstack', 'OpenStack', '5.3.3');
|
||||
}
|
||||
}
|
||||
}
|
||||
221
wp-content/plugins/updraftplus/methods/openstack2.php
Normal file
221
wp-content/plugins/updraftplus/methods/openstack2.php
Normal file
@@ -0,0 +1,221 @@
|
||||
<?php
|
||||
|
||||
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed.');
|
||||
|
||||
// SDK uses namespacing - requires PHP 5.3 (actually the SDK states its requirements as 5.3.3)
|
||||
// @codingStandardsIgnoreLine
|
||||
use OpenCloud\OpenStack;
|
||||
|
||||
require_once(UPDRAFTPLUS_DIR.'/methods/openstack-base.php');
|
||||
|
||||
class UpdraftPlus_BackupModule_openstack extends UpdraftPlus_BackupModule_openstack_base {
|
||||
|
||||
public function __construct() {
|
||||
// 4th parameter is a relative (to UPDRAFTPLUS_DIR) logo URL, which should begin with /, should we get approved for use of the OpenStack logo in future (have requested info)
|
||||
parent::__construct('openstack', 'OpenStack', 'OpenStack (Swift)', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Openstack service
|
||||
*
|
||||
* @param string $opts THis contains: 'tenant', 'user', 'password', 'authurl', (optional) 'region'
|
||||
* @param boolean $useservercerts User server certificates
|
||||
* @param string $disablesslverify Check to disable SSL Verify
|
||||
* @return array
|
||||
*/
|
||||
public function get_openstack_service($opts, $useservercerts = false, $disablesslverify = null) {
|
||||
|
||||
// 'tenant', 'user', 'password', 'authurl', 'path', (optional) 'region'
|
||||
extract($opts);
|
||||
|
||||
if (null === $disablesslverify) $disablesslverify = UpdraftPlus_Options::get_updraft_option('updraft_ssl_disableverify');
|
||||
|
||||
if (empty($user) || empty($password) || empty($authurl)) throw new Exception(__('Authorisation failed (check your credentials)', 'updraftplus'));
|
||||
|
||||
include_once(UPDRAFTPLUS_DIR.'/vendor/autoload.php');
|
||||
global $updraftplus;
|
||||
$updraftplus->log("OpenStack authentication URL: ".$authurl);
|
||||
|
||||
$client = new OpenStack($authurl, array(
|
||||
'username' => $user,
|
||||
'password' => $password,
|
||||
'tenantName' => $tenant
|
||||
));
|
||||
$this->client = $client;
|
||||
|
||||
if ($disablesslverify) {
|
||||
$client->setSslVerification(false);
|
||||
} else {
|
||||
if ($useservercerts) {
|
||||
$client->setConfig(array($client::SSL_CERT_AUTHORITY => false));
|
||||
} else {
|
||||
$client->setSslVerification(UPDRAFTPLUS_DIR.'/includes/cacert.pem', true, 2);
|
||||
}
|
||||
}
|
||||
|
||||
$client->authenticate();
|
||||
|
||||
if (empty($region)) {
|
||||
$catalog = $client->getCatalog();
|
||||
if (!empty($catalog)) {
|
||||
$items = $catalog->getItems();
|
||||
if (is_array($items)) {
|
||||
foreach ($items as $item) {
|
||||
$name = $item->getName();
|
||||
$type = $item->getType();
|
||||
if ('swift' != $name || 'object-store' != $type) continue;
|
||||
$eps = $item->getEndpoints();
|
||||
if (!is_array($eps)) continue;
|
||||
foreach ($eps as $ep) {
|
||||
if (is_object($ep) && !empty($ep->region)) {
|
||||
$region = $ep->region;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->region = $region;
|
||||
|
||||
return $client->objectStoreService('swift', $region);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method overrides the parent method and lists the supported features of this remote storage option.
|
||||
*
|
||||
* @return Array - an array of supported features (any features not
|
||||
* mentioned are assumed to not be supported)
|
||||
*/
|
||||
public function get_supported_features() {
|
||||
// This options format is handled via only accessing options via $this->get_options()
|
||||
return array('multi_options', 'config_templates', 'multi_storage');
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve default options for this remote storage module.
|
||||
*
|
||||
* @return Array - an array of options
|
||||
*/
|
||||
public function get_default_options() {
|
||||
return array(
|
||||
'user' => '',
|
||||
'authurl' => '',
|
||||
'password' => '',
|
||||
'tenant' => '',
|
||||
'path' => '',
|
||||
'region' => ''
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the pre middlesection configuration template
|
||||
*
|
||||
* @return String - the template
|
||||
*/
|
||||
public function get_pre_configuration_middlesection_template() {
|
||||
|
||||
?>
|
||||
<p><?php _e('Get your access credentials from your OpenStack Swift provider, and then pick a container name to use for storage. This container will be created for you if it does not already exist.', 'updraftplus');?> <a href="<?php echo apply_filters("updraftplus_com_link", "https://updraftplus.com/faqs/there-appear-to-be-lots-of-extra-files-in-my-rackspace-cloud-files-container/");?>" target="_blank"><?php _e('Also, you should read this important FAQ.', 'updraftplus'); ?></a></p>
|
||||
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* This outputs the html to the settings page for the Openstack settings.
|
||||
*
|
||||
* @return String - the partial template, ready for substitutions to be carried out
|
||||
*/
|
||||
public function get_configuration_middlesection_template() {
|
||||
ob_start();
|
||||
$classes = $this->get_css_classes();
|
||||
?>
|
||||
|
||||
<tr class="<?php echo $classes; ?>">
|
||||
<th><?php echo ucfirst(__('authentication URI', 'updraftplus'));?>:</th>
|
||||
<td><input data-updraft_settings_test="authurl" type="text" autocomplete="off" class="updraft_input--wide" <?php $this->output_settings_field_name_and_id('authurl');?> value="{{authurl}}" />
|
||||
<br>
|
||||
<em><?php echo _x('This needs to be a v2 (Keystone) authentication URI; v1 (Swauth) is not supported.', 'Keystone and swauth are technical terms which cannot be translated', 'updraftplus');?></em>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr class="<?php echo $classes; ?>">
|
||||
<th><a href="http://docs.openstack.org/openstack-ops/content/projects_users.html" title="<?php _e('Follow this link for more information', 'updraftplus');?>" target="_blank"><?php _e('Tenant', 'updraftplus');?></a>:</th>
|
||||
<td><input data-updraft_settings_test="tenant" type="text" autocomplete="off" class="updraft_input--wide" <?php $this->output_settings_field_name_and_id('tenant');?> value="{{tenant}}" />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr class="<?php echo $classes; ?>">
|
||||
<th><?php _e('Region', 'updraftplus');?>:</th>
|
||||
<td><input data-updraft_settings_test="region" type="text" autocomplete="off" class="updraft_input--wide" <?php $this->output_settings_field_name_and_id('region');?> value="{{region}}" />
|
||||
<br>
|
||||
<em><?php _e('Leave this blank, and a default will be chosen.', 'updraftplus');?></em>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr class="<?php echo $classes; ?>">
|
||||
<th><?php _e('Username', 'updraftplus');?>:</th>
|
||||
<td><input data-updraft_settings_test="user" type="text" autocomplete="off" class="updraft_input--wide" <?php $this->output_settings_field_name_and_id('user');?> value="{{user}}" />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr class="<?php echo $classes; ?>">
|
||||
<th><?php _e('Password', 'updraftplus');?>:</th>
|
||||
<td><input data-updraft_settings_test="password" type="<?php echo apply_filters('updraftplus_admin_secret_field_type', 'password'); ?>" autocomplete="off" class="updraft_input--wide" <?php $this->output_settings_field_name_and_id('password');?> value="{{password}}" />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr class="<?php echo $classes; ?>">
|
||||
<th><?php echo __('Container', 'updraftplus');?>:</th>
|
||||
<td><input data-updraft_settings_test="path" type="text" class="updraft_input--wide" <?php $this->output_settings_field_name_and_id('path');?> value="{{path}}" /></td>
|
||||
</tr>
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
public function credentials_test($posted_settings) {
|
||||
|
||||
if (empty($posted_settings['user'])) {
|
||||
printf(__("Failure: No %s was given.", 'updraftplus'), __('username', 'updraftplus'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($posted_settings['password'])) {
|
||||
printf(__("Failure: No %s was given.", 'updraftplus'), __('password', 'updraftplus'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($posted_settings['tenant'])) {
|
||||
printf(__("Failure: No %s was given.", 'updraftplus'), _x('tenant', '"tenant" is a term used with OpenStack storage - Google for "OpenStack tenant" to get more help on its meaning', 'updraftplus'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($posted_settings['authurl'])) {
|
||||
printf(__("Failure: No %s was given.", 'updraftplus'), __('authentication URI', 'updraftplus'));
|
||||
return;
|
||||
}
|
||||
|
||||
$opts = array(
|
||||
'user' => $posted_settings['user'],
|
||||
'password' => $posted_settings['password'],
|
||||
'authurl' => $posted_settings['authurl'],
|
||||
'tenant' => $posted_settings['tenant'],
|
||||
'region' => empty($posted_settings['region']) ? '' : $posted_settings['region'],
|
||||
);
|
||||
|
||||
$this->credentials_test_go($opts, $posted_settings['path'], $posted_settings['useservercerts'], $posted_settings['disableverify']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether options have been set up by the user, or not
|
||||
*
|
||||
* @param Array $opts - the potential options
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
public function options_exist($opts) {
|
||||
if (is_array($opts) && $opts['user'] && '' !== $opts['user'] && !empty($opts['authurl'])) return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
504
wp-content/plugins/updraftplus/methods/remotesend.php
Normal file
504
wp-content/plugins/updraftplus/methods/remotesend.php
Normal file
@@ -0,0 +1,504 @@
|
||||
<?php
|
||||
|
||||
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed');
|
||||
|
||||
if (!class_exists('UpdraftPlus_RemoteStorage_Addons_Base_v2')) require_once(UPDRAFTPLUS_DIR.'/methods/addon-base-v2.php');
|
||||
|
||||
class UpdraftPlus_BackupModule_remotesend extends UpdraftPlus_RemoteStorage_Addons_Base_v2 {
|
||||
|
||||
private $default_chunk_size;
|
||||
|
||||
private $remotesend_use_chunk_size;
|
||||
|
||||
private $remotesend_chunked_wp_error;
|
||||
|
||||
private $try_format_upgrade = false;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
// 3rd parameter: chunking? 4th: Test button?
|
||||
parent::__construct('remotesend', 'Remote send', false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Supplies the list of keys for options to be saved in the backup job.
|
||||
*
|
||||
* @return Array
|
||||
*/
|
||||
public function get_credentials() {
|
||||
return array('updraft_ssl_disableverify', 'updraft_ssl_nossl', 'updraft_ssl_useservercerts');
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload a single file
|
||||
*
|
||||
* @param String $file - the basename of the file to upload
|
||||
* @param String $from - the full path of the file
|
||||
*
|
||||
* @return Boolean - success status. Failures can also be thrown as exceptions.
|
||||
*/
|
||||
public function do_upload($file, $from) {
|
||||
|
||||
global $updraftplus;
|
||||
$opts = $this->options;
|
||||
|
||||
static $registered_completion_event = false;
|
||||
if (!$registered_completion_event) {
|
||||
// This is here, instead of the constructor, to make sure that, if multiple objects are insantiated, then only the one actually used for uploading gets involved in the upload_complete event
|
||||
$registered_completion_event = true;
|
||||
add_action('updraftplus_remotesend_upload_complete', array($this, 'upload_complete'));
|
||||
}
|
||||
|
||||
try {
|
||||
$storage = $this->bootstrap();
|
||||
if (is_wp_error($storage)) throw new Exception($storage->get_error_message());
|
||||
if (!is_object($storage)) throw new Exception("RPC service error");
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage().' ('.get_class($e).') (line: '.$e->getLine().', file: '.$e->getFile().')';
|
||||
$this->log("RPC service error: ".$message);
|
||||
$this->log($message, 'error');
|
||||
return false;
|
||||
}
|
||||
|
||||
$filesize = filesize($from);
|
||||
$this->remotesend_file_size = $filesize;
|
||||
|
||||
// See what the sending side currently has. This also serves as a ping. For that reason, we don't try/catch - we let them be caught at the next level up.
|
||||
|
||||
$get_remote_size = $this->send_message('get_file_status', $file, 30);
|
||||
|
||||
if (is_wp_error($get_remote_size)) {
|
||||
throw new Exception($get_remote_size->get_error_message().' ('.$get_remote_size->get_error_code().')');
|
||||
}
|
||||
|
||||
if (!is_array($get_remote_size) || empty($get_remote_size['response'])) throw new Exception(__('Unexpected response:', 'updraftplus').' '.serialize($get_remote_size));
|
||||
|
||||
if ('error' == $get_remote_size['response']) {
|
||||
$msg = $get_remote_size['data'];
|
||||
// Could interpret the codes to get more interesting messages directly to the user
|
||||
throw new Exception(__('Error:', 'updraftplus').' '.$msg);
|
||||
}
|
||||
|
||||
if (empty($get_remote_size['data']) || !isset($get_remote_size['data']['size']) || 'file_status' != $get_remote_size['response']) throw new Exception(__('Unexpected response:', 'updraftplus').' '.serialize($get_remote_size));
|
||||
|
||||
// Possible statuses: 0=temporary file (or not present), 1=file
|
||||
if (empty($get_remote_size['data'])) {
|
||||
$remote_size = 0;
|
||||
$remote_status = 0;
|
||||
} else {
|
||||
$remote_size = (int) $get_remote_size['data']['size'];
|
||||
$remote_status = $get_remote_size['data']['status'];
|
||||
}
|
||||
|
||||
$this->log("$file: existing size: ".$remote_size);
|
||||
|
||||
// Perhaps it already exists? (if we didn't get the final confirmation)
|
||||
if ($remote_size >= $filesize && $remote_status) {
|
||||
$this->log("$file: already uploaded");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Length = 44 (max = 45)
|
||||
$this->remote_sent_defchunk_transient = 'ud_rsenddck_'.md5($opts['name_indicator']);
|
||||
|
||||
if (empty($this->default_chunk_size)) {
|
||||
|
||||
$clone_would_like = $updraftplus->verify_free_memory(4194304*2) ? 4194304 : 2097152;
|
||||
|
||||
// Default is 2MB. After being b64-encoded twice, this is ~ 3.7MB = 113 seconds on 32KB/s uplink
|
||||
$default_chunk_size = $updraftplus->jobdata_get('clone_job') ? 4194304 : 2097152;
|
||||
|
||||
if (defined('UPDRAFTPLUS_REMOTESEND_DEFAULT_CHUNK_BYTES') && UPDRAFTPLUS_REMOTESEND_DEFAULT_CHUNK_BYTES >= 16384) $default_chunk_size = UPDRAFTPLUS_REMOTESEND_DEFAULT_CHUNK_BYTES;
|
||||
|
||||
$this->default_chunk_size = $default_chunk_size;
|
||||
|
||||
}
|
||||
|
||||
$default_chunk_size = $this->default_chunk_size;
|
||||
|
||||
if (false !== ($saved_default_chunk_size = get_transient($this->remote_sent_defchunk_transient)) && $saved_default_chunk_size > 16384) {
|
||||
// Don't go lower than 256KB for the *default*. (The job size can go lower).
|
||||
$default_chunk_size = max($saved_default_chunk_size, 262144);
|
||||
}
|
||||
|
||||
$this->remotesend_use_chunk_size = $updraftplus->jobdata_get('remotesend_chunksize', $default_chunk_size);
|
||||
|
||||
if (0 == $remote_size && $this->remotesend_use_chunk_size == $this->default_chunk_size && $updraftplus->current_resumption - max($updraftplus->jobdata_get('uploaded_lastreset'), 1) > 1) {
|
||||
$new_chunk_size = floor($this->remotesend_use_chunk_size / 2);
|
||||
$this->log("No uploading activity has been detected for a while; reducing chunk size in case a timeout was occurring. New chunk size: ".$new_chunk_size);
|
||||
$this->remotesend_set_new_chunk_size($new_chunk_size);
|
||||
}
|
||||
|
||||
try {
|
||||
if (false != ($handle = fopen($from, 'rb'))) {
|
||||
|
||||
$this->remotesend_uploaded_size = $remote_size;
|
||||
$ret = $updraftplus->chunked_upload($this, $file, $this->method."://".trailingslashit($opts['url']).$file, $this->description, $this->remotesend_use_chunk_size, $remote_size, true);
|
||||
|
||||
fclose($handle);
|
||||
|
||||
return $ret;
|
||||
} else {
|
||||
throw new Exception("Failed to open file for reading: $from");
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->log("upload: error (".get_class($e)."): ($file) (".$e->getMessage().") (line: ".$e->getLine().', file: '.$e->getFile().')');
|
||||
if (!empty($this->remotesend_chunked_wp_error) && is_wp_error($this->remotesend_chunked_wp_error)) {
|
||||
$this->log("Exception data: ".base64_encode(serialize($this->remotesend_chunked_wp_error->get_error_data())));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Chunked upload
|
||||
*
|
||||
* @param String $file Specific file to be used in chunked upload
|
||||
* @param Resource $fp File handle
|
||||
* @param Integer $chunk_index The index of the chunked data
|
||||
* @param Integer $upload_size Size of the upload
|
||||
* @param Integer $upload_start String the upload starts on
|
||||
* @param Integer $upload_end String the upload ends on
|
||||
*
|
||||
* @return Boolean|Integer Result (N.B> (int)1 means the same as true, but additionally indicates "don't log it")
|
||||
*/
|
||||
public function chunked_upload($file, $fp, $chunk_index, $upload_size, $upload_start, $upload_end) {
|
||||
|
||||
// Condition used to be "$upload_start < $this->remotesend_uploaded_size" - but this assumed that the other side never failed after writing only some bytes to disk
|
||||
// $upload_end is the byte offset of the final byte. Therefore, add 1 onto it when comparing with a size.
|
||||
if ($upload_end + 1 <= $this->remotesend_uploaded_size) return 1;
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
$storage = $this->get_storage();
|
||||
|
||||
$chunk = fread($fp, $upload_size);
|
||||
|
||||
if (false === $chunk) {
|
||||
$this->log("upload: $file: fread failure ($upload_start)");
|
||||
return false;
|
||||
}
|
||||
|
||||
$try_again = false;
|
||||
|
||||
$data = array('file' => $file, 'data' => base64_encode($chunk), 'start' => $upload_start);
|
||||
|
||||
if ($upload_end+1 >= $this->remotesend_file_size) {
|
||||
$data['last_chunk'] = true;
|
||||
if ('' != ($label = $updraftplus->jobdata_get('label'))) $data['label'] = $label;
|
||||
}
|
||||
|
||||
// ~ 3.7MB of data typically - timeout allows for 15.9KB/s
|
||||
try {
|
||||
$put_chunk = $this->send_message('send_chunk', $data, 240);
|
||||
} catch (Exception $e) {
|
||||
$try_again = true;
|
||||
}
|
||||
|
||||
if ($try_again || is_wp_error($put_chunk)) {
|
||||
// 413 = Request entity too large
|
||||
// Don't go lower than 64KB chunks (i.e. 128KB/2)
|
||||
// Note that mod_security can be configured to 'helpfully' decides to replace HTTP error codes + messages with a simple serving up of the site home page, which means that we need to also guess about other reasons this condition may have occurred other than detecting via the direct 413 code. Of course, our search for wp-includes|wp-content|WordPress|/themes/ would be thwarted by someone who tries to hide their WP. The /themes/ is pretty hard to hide, as the theme directory is always <wp-content-dir>/themes - even if you moved your wp-content. The point though is just a 'best effort' - this doesn't have to be infallible.
|
||||
if (is_wp_error($put_chunk)) {
|
||||
|
||||
$error_data = $put_chunk->get_error_data();
|
||||
|
||||
$is_413 = ('unexpected_http_code' == $put_chunk->get_error_code() && (
|
||||
413 == $error_data
|
||||
|| (is_array($error_data) && !empty($error_data['response']['code']) && 413 == $error_data['response']['code'])
|
||||
)
|
||||
);
|
||||
|
||||
$is_timeout = ('http_request_failed' == $put_chunk->get_error_code() && false !== strpos($put_chunk->get_error_message(), 'timed out'));
|
||||
|
||||
if ($this->remotesend_use_chunk_size >= 131072 && ($is_413 || $is_timeout || ('response_not_understood' == $put_chunk->get_error_code() && (false !== strpos($error_data, 'wp-includes') || false !== strpos($error_data, 'wp-content') || false !== strpos($error_data, 'WordPress') || false !== strpos($put_chunk->get_error_data(), '/themes/'))))) {
|
||||
if (1 == $chunk_index) {
|
||||
$new_chunk_size = floor($this->remotesend_use_chunk_size / 2);
|
||||
$this->remotesend_set_new_chunk_size($new_chunk_size);
|
||||
$log_msg = "Returned WP_Error: code=".$put_chunk->get_error_code();
|
||||
if ('unexpected_http_code' == $put_chunk->get_error_code()) $log_msg .= ' ('.$error_data.')';
|
||||
$log_msg .= " - reducing chunk size to: ".$new_chunk_size;
|
||||
$this->log($log_msg);
|
||||
return new WP_Error('reduce_chunk_size', 'HTTP 413 or possibly equivalent condition on first chunk - should reduce chunk size', $new_chunk_size);
|
||||
} elseif ($this->remotesend_use_chunk_size >= 131072 && $is_413) {
|
||||
// In this limited case, where we got a 413 but the chunk is not number 1, our algorithm/architecture doesn't allow us to just resume immediately with a new chunk size. However, we can just have UD reduce the chunk size on its next resumption.
|
||||
$new_chunk_size = floor($this->remotesend_use_chunk_size / 2);
|
||||
$this->remotesend_set_new_chunk_size($new_chunk_size);
|
||||
$log_msg = "Returned WP_Error: code=".$put_chunk->get_error_code().", message=".$put_chunk->get_error_message();
|
||||
$log_msg .= " - reducing chunk size to: ".$new_chunk_size." and then scheduling resumption/aborting";
|
||||
$this->log($log_msg);
|
||||
UpdraftPlus_Job_Scheduler::reschedule(50);
|
||||
UpdraftPlus_Job_Scheduler::record_still_alive();
|
||||
die;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
$put_chunk = $this->send_message('send_chunk', $data, 240);
|
||||
}
|
||||
|
||||
if (is_wp_error($put_chunk)) {
|
||||
// The exception handler is within this class. So we can store the data.
|
||||
$this->remotesend_chunked_wp_error = $put_chunk;
|
||||
throw new Exception($put_chunk->get_error_message().' ('.$put_chunk->get_error_code().')');
|
||||
}
|
||||
|
||||
if (!is_array($put_chunk) || empty($put_chunk['response'])) throw new Exception(__('Unexpected response:', 'updraftplus').' '.serialize($put_chunk));
|
||||
|
||||
if ('error' == $put_chunk['response']) {
|
||||
$msg = $put_chunk['data'];
|
||||
// Could interpret the codes to get more interesting messages directly to the user
|
||||
// The textual prefixes here were added after 1.12.5 - hence optional when parsing
|
||||
if (preg_match('/^invalid_start_too_big:(start=)?(\d+),(existing_size=)?(\d+)/', $msg, $matches)) {
|
||||
$attempted_start = $matches[1];
|
||||
$existing_size = $matches[2];
|
||||
if ($existing_size < $this->remotesend_uploaded_size) {
|
||||
// The file on the remote system seems to have shrunk. Could be some load-balancing system with a distributed filesystem that is only eventually consistent.
|
||||
return new WP_Error('try_again', 'File on remote system is smaller than expected - perhaps an eventually-consistent filesystem (wait and retry)');
|
||||
}
|
||||
}
|
||||
throw new Exception(__('Error:', 'updraftplus').' '.$msg);
|
||||
}
|
||||
|
||||
if ('file_status' != $put_chunk['response']) throw new Exception(__('Unexpected response:', 'updraftplus').' '.serialize($put_chunk));
|
||||
|
||||
// Possible statuses: 0=temporary file (or not present), 1=file
|
||||
if (empty($put_chunk['data']) || !is_array($put_chunk['data'])) {
|
||||
$this->log("Unexpected response when putting chunk $chunk_index: ".serialize($put_chunk));
|
||||
return false;
|
||||
} else {
|
||||
$remote_size = (int) $put_chunk['data']['size'];
|
||||
$remote_status = $put_chunk['data']['status'];
|
||||
$this->remotesend_uploaded_size = $remote_size;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called via an action and will send a message to the remote site to inform it that the backup has finished sending
|
||||
*/
|
||||
public function upload_complete() {
|
||||
global $updraftplus;
|
||||
|
||||
$service = $updraftplus->jobdata_get('service');
|
||||
$remote_sent = (!empty($service) && ((is_array($service) && in_array('remotesend', $service)) || 'remotesend' === $service));
|
||||
|
||||
if (!$remote_sent) return;
|
||||
|
||||
try {
|
||||
$storage = $this->bootstrap();
|
||||
if (is_wp_error($storage)) throw new Exception($storage->get_error_message());
|
||||
if (!is_object($storage)) throw new Exception("RPC service error");
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage().' ('.get_class($e).') (line: '.$e->getLine().', file: '.$e->getFile().')';
|
||||
$this->log("RPC service error: ".$message);
|
||||
$this->log($message, 'error');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_wp_error($storage)) return $updraftplus->log_wp_error($storage, false, true);
|
||||
|
||||
$response = $this->send_message('upload_complete', array('job_id' => $updraftplus->nonce), 30);
|
||||
|
||||
if (is_wp_error($response)) {
|
||||
throw new Exception($response->get_error_message().' ('.$response->get_error_code().')');
|
||||
}
|
||||
|
||||
if (!is_array($response) || empty($response['response'])) throw new Exception(__('Unexpected response:', 'updraftplus').' '.serialize($response));
|
||||
|
||||
if ('error' == $response['response']) {
|
||||
$msg = $response['data'];
|
||||
// Could interpret the codes to get more interesting messages directly to the user
|
||||
throw new Exception(__('Error', 'updraftplus').': '.$msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the chunk size
|
||||
*
|
||||
* @param Integer $new_chunk_size - in bytes
|
||||
*/
|
||||
private function remotesend_set_new_chunk_size($new_chunk_size) {
|
||||
global $updraftplus;
|
||||
$this->remotesend_use_chunk_size = $new_chunk_size;
|
||||
$updraftplus->jobdata_set('remotesend_chunksize', $new_chunk_size);
|
||||
// Save, so that we don't have to cycle through the illegitimate/larger chunk sizes each time. Set the transient expiry to 120 days, in case they change hosting/configuration - so that we're not stuck on the lower size forever.
|
||||
set_transient($this->remote_sent_defchunk_transient, $new_chunk_size, 86400*120);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message to the remote site
|
||||
*
|
||||
* @param String $message - the message identifier
|
||||
* @param Array|Null $data - the data to send with the message
|
||||
* @param Integer $timeout - timeout in waiting for a response
|
||||
*
|
||||
* @return Array|WP_Error - results, or an error
|
||||
*/
|
||||
private function send_message($message, $data = null, $timeout = 30) {
|
||||
$storage = $this->get_storage();
|
||||
|
||||
if (is_array($this->try_format_upgrade) && is_array($data)) {
|
||||
$data['sender_public'] = $this->try_format_upgrade['local_public'];
|
||||
}
|
||||
|
||||
$response = $storage->send_message($message, $data, $timeout);
|
||||
|
||||
if (is_array($response) && !empty($response['data']) && is_array($response['data'])) {
|
||||
|
||||
if (!empty($response['data']['php_events']) && !empty($response['data']['previous_data'])) {
|
||||
foreach ($response['data']['php_events'] as $logline) {
|
||||
$this->log("From remote side: ".$logline);
|
||||
}
|
||||
$response['data'] = $response['data']['previous_data'];
|
||||
}
|
||||
|
||||
if (is_array($response) && is_array($response['data']) && !empty($response['data']['got_public'])) {
|
||||
$name_indicator = $this->try_format_upgrade['name_indicator'];
|
||||
|
||||
$remotesites = UpdraftPlus_Options::get_updraft_option('updraft_remotesites');
|
||||
|
||||
foreach ($remotesites as $key => $site) {
|
||||
if (!is_array($site) || empty($site['name_indicator']) || $site['name_indicator'] != $name_indicator) continue;
|
||||
// This means 'format 2'
|
||||
$this->try_format_upgrade = true;
|
||||
$remotesites[$key]['remote_got_public'] = 1;
|
||||
// If this DB save fails, they'll have to recreate the key
|
||||
UpdraftPlus_Options::update_updraft_option('updraft_remotesites', $remotesites);
|
||||
// Now we need to get a fresh storage object, because the remote end will no longer accept messages with format=1
|
||||
$this->set_storage(null);
|
||||
$this->do_bootstrap(null);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
public function do_bootstrap($opts, $connect = true) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
if (!class_exists('UpdraftPlus_Remote_Communications')) include_once(apply_filters('updraftplus_class_udrpc_path', UPDRAFTPLUS_DIR.'/includes/class-udrpc.php', $updraftplus->version));
|
||||
|
||||
$opts = $this->get_opts();
|
||||
|
||||
try {
|
||||
$ud_rpc = new UpdraftPlus_Remote_Communications($opts['name_indicator']);
|
||||
if (!empty($opts['format_support']) && 2 == $opts['format_support'] && !empty($opts['local_private']) && !empty($opts['local_public']) && !empty($opts['remote_got_public'])) {
|
||||
$ud_rpc->set_message_format(2);
|
||||
$ud_rpc->set_key_remote($opts['key']);
|
||||
$ud_rpc->set_key_local($opts['local_private']);
|
||||
} else {
|
||||
// Enforce the legacy communications protocol (which is only suitable for when only one side only sends, and the other only receives - which is what we happen to do)
|
||||
$ud_rpc->set_message_format(1);
|
||||
$ud_rpc->set_key_local($opts['key']);
|
||||
if (!empty($opts['format_support']) && 2 == $opts['format_support'] && !empty($opts['local_public']) && !empty($opts['local_private'])) {
|
||||
$this->try_format_upgrade = array('name_indicator' => $opts['name_indicator'], 'local_public' => $opts['local_public']);
|
||||
}
|
||||
}
|
||||
$ud_rpc->set_destination_url($opts['url']);
|
||||
$ud_rpc->activate_replay_protection();
|
||||
} catch (Exception $e) {
|
||||
return new WP_Error('rpc_failure', "Commmunications failure: ".$e->getMessage().' (line: '.$e->getLine().', file: '.$e->getFile().')');
|
||||
}
|
||||
|
||||
do_action('updraftplus_remotesend_udrpc_object_obtained', $ud_rpc, $opts);
|
||||
|
||||
$this->set_storage($ud_rpc);
|
||||
|
||||
return $ud_rpc;
|
||||
}
|
||||
|
||||
public function options_exist($opts) {
|
||||
if (is_array($opts) && !empty($opts['url']) && !empty($opts['name_indicator']) && !empty($opts['key'])) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public function get_opts() {
|
||||
global $updraftplus;
|
||||
$opts = $updraftplus->jobdata_get('remotesend_info');
|
||||
$opts = $this->clone_remotesend_options($opts);
|
||||
if (true === $this->try_format_upgrade && is_array($opts)) $opts['remote_got_public'] = 1;
|
||||
return is_array($opts) ? $opts : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will check the options we have for the remote send and if it's a clone job and there are missing settings it will call the mothership to get this information.
|
||||
*
|
||||
* @param Array $opts - an array of remote send options
|
||||
*
|
||||
* @return Array - an array of options
|
||||
*/
|
||||
public function clone_remotesend_options($opts) {
|
||||
|
||||
// Don't call self::log() - this then requests options (to get the label), causing an infinite loop.
|
||||
|
||||
global $updraftplus;
|
||||
if (empty($updraftplus_admin)) include_once(UPDRAFTPLUS_DIR.'/admin.php');
|
||||
|
||||
$clone_job = $updraftplus->jobdata_get('clone_job');
|
||||
|
||||
// check this is a clone job before we proceed
|
||||
if (empty($clone_job)) return $opts;
|
||||
|
||||
// check that we don't already have the needed information
|
||||
if (is_array($opts) && !empty($opts['url']) && !empty($opts['name_indicator']) && !empty($opts['key'])) return $opts;
|
||||
|
||||
$updraftplus->jobdata_set('jobstatus', 'clonepolling');
|
||||
$clone_id = $updraftplus->jobdata_get('clone_id');
|
||||
$clone_url = $updraftplus->jobdata_get('clone_url');
|
||||
$clone_key = $updraftplus->jobdata_get('clone_key');
|
||||
$secret_token = $updraftplus->jobdata_get('secret_token');
|
||||
|
||||
if (empty($clone_id) && empty($secret_token)) return $opts;
|
||||
|
||||
$params = array('clone_id' => $clone_id, 'secret_token' => $secret_token);
|
||||
$response = $updraftplus->get_updraftplus_clone()->clone_info_poll($params);
|
||||
|
||||
if (!isset($response['status']) || 'success' != $response['status']) {
|
||||
$updraftplus->log("UpdraftClone migration information poll failed with code: " . $response['code']);
|
||||
return $opts;
|
||||
}
|
||||
|
||||
if (!isset($response['data']) || !isset($response['data']['url']) || !isset($response['data']['key'])) {
|
||||
$updraftplus->log("UpdraftClone migration information poll unexpected return information with code:" . $response['code']);
|
||||
return $opts;
|
||||
}
|
||||
|
||||
$clone_url = $response['data']['url'];
|
||||
$clone_key = json_decode($response['data']['key'], true);
|
||||
|
||||
if (empty($clone_url) || empty($clone_key)) {
|
||||
$updraftplus->log("UpdraftClone migration information not found (probably still provisioning): will poll again in 60");
|
||||
UpdraftPlus_Job_Scheduler::reschedule(60);
|
||||
UpdraftPlus_Job_Scheduler::record_still_alive();
|
||||
die;
|
||||
}
|
||||
|
||||
// Store the information
|
||||
$remotesites = UpdraftPlus_Options::get_updraft_option('updraft_remotesites');
|
||||
if (!is_array($remotesites)) $remotesites = array();
|
||||
|
||||
foreach ($remotesites as $k => $rsite) {
|
||||
if (!is_array($rsite)) continue;
|
||||
if ($rsite['url'] == $clone_key['url']) unset($remotesites[$k]);
|
||||
}
|
||||
|
||||
$remotesites[] = $clone_key;
|
||||
UpdraftPlus_Options::update_updraft_option('updraft_remotesites', $remotesites);
|
||||
|
||||
$updraftplus->jobdata_set_multi('clone_url', $clone_url, 'clone_key', $clone_key, 'remotesend_info', $clone_key, 'jobstatus', 'clouduploading');
|
||||
|
||||
return $clone_key;
|
||||
}
|
||||
|
||||
// do_listfiles(), do_download(), do_delete() : the absence of any method here means that the parent will correctly throw an error
|
||||
}
|
||||
1241
wp-content/plugins/updraftplus/methods/s3.php
Normal file
1241
wp-content/plugins/updraftplus/methods/s3.php
Normal file
File diff suppressed because it is too large
Load Diff
127
wp-content/plugins/updraftplus/methods/s3generic.php
Normal file
127
wp-content/plugins/updraftplus/methods/s3generic.php
Normal file
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed.');
|
||||
|
||||
require_once(UPDRAFTPLUS_DIR.'/methods/s3.php');
|
||||
|
||||
/**
|
||||
* Converted to multi-options (Feb 2017-) and previous options conversion removed: Yes
|
||||
*/
|
||||
class UpdraftPlus_BackupModule_s3generic extends UpdraftPlus_BackupModule_s3 {
|
||||
|
||||
protected $use_v4 = false;
|
||||
|
||||
protected function set_region($obj, $region = '', $bucket_name = '') {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
|
||||
$config = $this->get_config();
|
||||
$endpoint = ('' != $region && 'n/a' != $region) ? $region : $config['endpoint'];
|
||||
$log_message = "Set endpoint: $endpoint";
|
||||
if (is_string($endpoint) && preg_match('/^(.*):(\d+)$/', $endpoint, $matches)) {
|
||||
$endpoint = $matches[1];
|
||||
$port = $matches[2];
|
||||
$log_message .= ", port=$port";
|
||||
$obj->setPort($port);
|
||||
}
|
||||
global $updraftplus;
|
||||
if ($updraftplus->backup_time) $this->log($log_message);
|
||||
$obj->setEndpoint($endpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method overrides the parent method and lists the supported features of this remote storage option.
|
||||
*
|
||||
* @return Array - an array of supported features (any features not mentioned are asuumed to not be supported)
|
||||
*/
|
||||
public function get_supported_features() {
|
||||
// This options format is handled via only accessing options via $this->get_options()
|
||||
return array('multi_options', 'config_templates', 'multi_storage');
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve default options for this remote storage module.
|
||||
*
|
||||
* @return Array - an array of options
|
||||
*/
|
||||
public function get_default_options() {
|
||||
return array(
|
||||
'accesskey' => '',
|
||||
'secretkey' => '',
|
||||
'path' => '',
|
||||
'endpoint' => '',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve specific options for this remote storage module
|
||||
*
|
||||
* @param Boolean $force_refresh - if set, and if relevant, don't use cached credentials, but get them afresh
|
||||
*
|
||||
* @return Array - an array of options
|
||||
*/
|
||||
protected function get_config($force_refresh = false) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
|
||||
$opts = $this->get_options();
|
||||
$opts['whoweare'] = 'S3';
|
||||
$opts['whoweare_long'] = __('S3 (Compatible)', 'updraftplus');
|
||||
$opts['key'] = 's3generic';
|
||||
return $opts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the pre configuration template
|
||||
*
|
||||
* @return String - the template
|
||||
*/
|
||||
public function get_pre_configuration_template() {
|
||||
$this->get_pre_configuration_template_engine('s3generic', 'S3', __('S3 (Compatible)', 'updraftplus'), 'S3', '', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configuration template
|
||||
*
|
||||
* @return String - the template, ready for substitutions to be carried out
|
||||
*/
|
||||
public function get_configuration_template() {
|
||||
// 5th parameter = control panel URL
|
||||
// 6th = image HTML
|
||||
return $this->get_configuration_template_engine('s3generic', 'S3', __('S3 (Compatible)', 'updraftplus'), 'S3', '', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies handerbar template options
|
||||
* The function require because It should override parent class's UpdraftPlus_BackupModule_s3::transform_options_for_template() functionality with no operation.
|
||||
*
|
||||
* @param array $opts
|
||||
* @return array - Modified handerbar template options
|
||||
*/
|
||||
public function transform_options_for_template($opts) {
|
||||
return $opts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether options have been set up by the user, or not
|
||||
*
|
||||
* @param Array $opts - the potential options
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
public function options_exist($opts) {
|
||||
return (parent::options_exist($opts) && !empty($opts['endpoint']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get handlebar partial template string for endpoint of s3 compatible remote storage method. Other child class can extend it.
|
||||
*
|
||||
* @return string the partial template string
|
||||
*/
|
||||
protected function get_partial_configuration_template_for_endpoint() {
|
||||
return '<tr class="'.$this->get_css_classes().'">
|
||||
<th>'.sprintf(__('%s end-point', 'updraftplus'), 'S3').'</th>
|
||||
<td>
|
||||
<input data-updraft_settings_test="endpoint" type="text" class="updraft_input--wide" '.$this->output_settings_field_name_and_id('endpoint', true).' value="{{endpoint}}" />
|
||||
</td>
|
||||
</tr>';
|
||||
}
|
||||
|
||||
public function credentials_test($posted_settings) {
|
||||
$this->credentials_test_engine($this->get_config(), $posted_settings);
|
||||
}
|
||||
}
|
||||
33
wp-content/plugins/updraftplus/methods/sftp.php
Normal file
33
wp-content/plugins/updraftplus/methods/sftp.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed.');
|
||||
|
||||
if (class_exists('UpdraftPlus_Addons_RemoteStorage_sftp')) {
|
||||
|
||||
// Migrate options to standard-style - April 2017. This then enables them to get picked up by the multi-options settings translation code
|
||||
if (!is_array(UpdraftPlus_Options::get_updraft_option('updraft_sftp')) && '' != UpdraftPlus_Options::get_updraft_option('updraft_sftp_settings', '')) {
|
||||
$opts = UpdraftPlus_Options::get_updraft_option('updraft_sftp_settings');
|
||||
UpdraftPlus_Options::update_updraft_option('updraft_sftp', $opts);
|
||||
UpdraftPlus_Options::delete_updraft_option('updraft_sftp_settings');
|
||||
}
|
||||
|
||||
class UpdraftPlus_BackupModule_sftp extends UpdraftPlus_Addons_RemoteStorage_sftp {
|
||||
public function __construct() {
|
||||
parent::__construct('sftp', 'SFTP/SCP');
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
include_once(UPDRAFTPLUS_DIR.'/methods/addon-not-yet-present.php');
|
||||
|
||||
/**
|
||||
* N.B. UpdraftPlus_BackupModule_AddonNotYetPresent extends UpdraftPlus_BackupModule
|
||||
*/
|
||||
class UpdraftPlus_BackupModule_sftp extends UpdraftPlus_BackupModule_AddonNotYetPresent {
|
||||
public function __construct() {
|
||||
parent::__construct('sftp', 'SFTP/SCP');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
113
wp-content/plugins/updraftplus/methods/template.php
Normal file
113
wp-content/plugins/updraftplus/methods/template.php
Normal file
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
/**
|
||||
* This is a bare-bones to get you started with developing an access method. The methods provided below are all ones you will want to use (though note that the provided email.php method is an
|
||||
* example of truly bare-bones for a method that cannot delete or download and has no configuration).
|
||||
*
|
||||
* Read the existing methods for help. There is no hard-and-fast need to put all your code in this file; it is just for increasing convenience and maintainability; there are no bonus points for 100% elegance. If you need access to some part of WordPress that you can only reach through the main plugin file (updraftplus.php), then go right ahead and patch that.
|
||||
*
|
||||
* Some handy tips:
|
||||
* - Search-and-replace "template" for the name of your access method
|
||||
* - You can also add the methods config_print_javascript_onready and credentials_test if you like
|
||||
* - Name your file accordingly (it is now template.php)
|
||||
* - Add the method to the array $backup_methods in updraftplus.php when ready
|
||||
* - Use the constant UPDRAFTPLUS_DIR to reach Updraft's plugin directory
|
||||
* - Call $updraftplus->log("my log message") to log things, which greatly helps debugging
|
||||
* - UpdraftPlus is licenced under the GPLv3 or later. In order to combine your backup method with UpdraftPlus, you will need to licence to anyone and everyone that you distribute it to in a compatible way.
|
||||
*/
|
||||
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed.');
|
||||
|
||||
if (!class_exists('UpdraftPlus_BackupModule')) require_once(UPDRAFTPLUS_DIR.'/methods/backup-module.php');
|
||||
|
||||
class UpdraftPlus_BackupModule_template extends UpdraftPlus_BackupModule {
|
||||
|
||||
/**
|
||||
* backup method: takes an array, and shovels them off to the cloud storage
|
||||
*
|
||||
* @param Array $backup_array Array of files (basenames) to sent to remote storage
|
||||
* @return Mixed - (boolean)false to indicate failure; otherwise, something to be passed back when deleting files
|
||||
*/
|
||||
public function backup($backup_array) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
// foreach ($backup_array as $file) {
|
||||
|
||||
// Do our uploading stuff...
|
||||
|
||||
// If successful, then you must do this:
|
||||
// $updraftplus->uploaded_file($file);
|
||||
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This function lists the files found in the configured storage location
|
||||
*
|
||||
* @param String $match a substring to require (tested via strpos() !== false)
|
||||
*
|
||||
* @return Array - each file is represented by an array with entries 'name' and (optional) 'size'
|
||||
*/
|
||||
public function listfiles($match = 'backup_') {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
|
||||
// This function needs to return an array of arrays. The keys for the sub-arrays are name (a path-less filename, i.e. a basename), (optional)size, and should be a list of matching files from the storage backend. A WP_Error object can also be returned; and the error code should be no_settings if that is relevant.
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* delete method: takes an array of file names (base name) or a single string, and removes them from the cloud storage
|
||||
*
|
||||
* @param string $files The specific files
|
||||
* @param mixed $data Anything passed back from self::backup()
|
||||
* @param array $sizeinfo Size information
|
||||
* @return Boolean - whether the operation succeeded or not
|
||||
*/
|
||||
public function delete($files, $data = false, $sizeinfo = array()) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
if (is_string($files)) $files = array($files);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* download method: takes a file name (base name), and brings it back from the cloud storage into Updraft's directory
|
||||
* You can register errors with $updraftplus->log("my error message", 'error')
|
||||
*
|
||||
* @param string $file The specific file to be downloaded from the Cloud Storage
|
||||
*/
|
||||
public function download($file) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
}
|
||||
|
||||
public function get_supported_features() {
|
||||
// This options format is handled via only accessing options via $this->get_options()
|
||||
return array('multi_options', 'config_templates');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configuration template, in Handlebars format.
|
||||
* Note that logging is not available from this context; it will do nothing.
|
||||
*
|
||||
* @return String - the template, ready for substitutions to be carried out
|
||||
*/
|
||||
public function get_configuration_template() {
|
||||
|
||||
ob_start();
|
||||
|
||||
$classes = $this->get_css_classes();
|
||||
|
||||
?>
|
||||
<tr class="updraftplusmethod <?php echo $classes;?>">
|
||||
<th>My Method:</th>
|
||||
<td>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<?php
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
||||
}
|
||||
775
wp-content/plugins/updraftplus/methods/updraftvault.php
Normal file
775
wp-content/plugins/updraftplus/methods/updraftvault.php
Normal file
@@ -0,0 +1,775 @@
|
||||
<?php
|
||||
|
||||
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed.');
|
||||
|
||||
require_once(UPDRAFTPLUS_DIR.'/methods/s3.php');
|
||||
|
||||
class UpdraftPlus_BackupModule_updraftvault extends UpdraftPlus_BackupModule_s3 {
|
||||
|
||||
private $vault_mothership = 'https://vault.updraftplus.com/plugin-info/';
|
||||
|
||||
private $vault_config;
|
||||
|
||||
/**
|
||||
* This function makes testing easier, rather than having to change the URLs in multiple places
|
||||
*
|
||||
* @param boolean|string $which_page specifies which page to get the URL for
|
||||
* @return string
|
||||
*/
|
||||
private function get_url($which_page = false) {
|
||||
$base = defined('UPDRAFTPLUS_VAULT_SHOP_BASE') ? UPDRAFTPLUS_VAULT_SHOP_BASE : 'https://updraftplus.com/shop/';
|
||||
switch ($which_page) {
|
||||
case 'get_more_quota':
|
||||
return apply_filters('updraftplus_com_link', $base.'product-category/updraftplus-vault/');
|
||||
break;
|
||||
case 'more_vault_info_faqs':
|
||||
return apply_filters('updraftplus_com_link', 'https://updraftplus.com/support/updraftplus-vault-faqs/');
|
||||
break;
|
||||
case 'more_vault_info_landing':
|
||||
return apply_filters('updraftplus_com_link', 'https://updraftplus.com/landing/vault');
|
||||
break;
|
||||
case 'vault_forgotten_credentials_links':
|
||||
return apply_filters('updraftplus_com_link', 'https://updraftplus.com/my-account/lost-password/');
|
||||
break;
|
||||
default:
|
||||
return apply_filters('updraftplus_com_link', $base);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method overrides the parent method and lists the supported features of this remote storage option.
|
||||
*
|
||||
* @return Array - an array of supported features (any features not mentioned are asuumed to not be supported)
|
||||
*/
|
||||
public function get_supported_features() {
|
||||
// This options format is handled via only accessing options via $this->get_options()
|
||||
return array('multi_options', 'config_templates');
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve default options for this remote storage module.
|
||||
*
|
||||
* @return Array - an array of options
|
||||
*/
|
||||
public function get_default_options() {
|
||||
return array(
|
||||
'token' => '',
|
||||
'email' => '',
|
||||
'quota' => -1
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve specific options for this remote storage module
|
||||
*
|
||||
* @param Array $config an array of config options
|
||||
* @return Array - an array of options
|
||||
*/
|
||||
protected function vault_set_config($config) {
|
||||
$config['whoweare'] = 'UpdraftVault';
|
||||
$config['whoweare_long'] = __('UpdraftVault', 'updraftplus');
|
||||
$config['key'] = 'updraftvault';
|
||||
$this->vault_config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the UpdraftVault configuration and credentials
|
||||
*
|
||||
* @param Boolean $force_refresh - if set, and if relevant, don't use cached credentials, but get them afresh
|
||||
*
|
||||
* @return array An array containing the Amazon S3 credentials (accesskey, secretkey, etc.)
|
||||
* along with some configuration values.
|
||||
*/
|
||||
public function get_config($force_refresh = false) {
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
if (!$force_refresh) {
|
||||
// Have we already done this?
|
||||
if (!empty($this->vault_config)) return $this->vault_config;
|
||||
|
||||
// Stored in the job?
|
||||
if ($job_config = $this->jobdata_get('config', null, 'updraftvault_config')) {
|
||||
if (!empty($job_config) && is_array($job_config)) {
|
||||
$this->vault_config = $job_config;
|
||||
return $job_config;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pass back empty settings, if nothing better can be found - this ensures that the error eventually is raised in the right place
|
||||
$config = array('accesskey' => '', 'secretkey' => '', 'path' => '');
|
||||
$config['whoweare'] = 'Updraft Vault';
|
||||
$config['whoweare_long'] = __('Updraft Vault', 'updraftplus');
|
||||
$config['key'] = 'updraftvault';
|
||||
|
||||
// Get the stored options
|
||||
$opts = $this->get_options();
|
||||
|
||||
if (!is_array($opts) || empty($opts['token']) || empty($opts['email'])) {
|
||||
// Not connected
|
||||
$this->log("this site has not been connected - check your settings");
|
||||
$config['error'] = array('message' => 'site_not_connected', 'values' => array());
|
||||
|
||||
$this->vault_config = $config;
|
||||
$this->jobdata_set('config', $config);
|
||||
return $config;
|
||||
}
|
||||
|
||||
$site_id = $updraftplus->siteid();
|
||||
|
||||
$this->log("requesting access details (sid=$site_id, email=".$opts['email'].")");
|
||||
|
||||
// Request the credentials using our token
|
||||
$post_body = array(
|
||||
'e' => (string) $opts['email'],
|
||||
'sid' => $site_id,
|
||||
'token' => (string) $opts['token'],
|
||||
'su' => base64_encode(home_url())
|
||||
);
|
||||
|
||||
if (!empty($this->vault_in_config_print)) {
|
||||
// In this case, all that the get_config() is being done for is to get the quota info. Send back the cached quota info instead (rather than have an HTTP trip every time the settings page is loaded). The config will get updated whenever there's a backup, or the user presses the link to update.
|
||||
$getconfig = get_transient('udvault_last_config');
|
||||
}
|
||||
|
||||
// Use SSL to prevent snooping
|
||||
if (empty($getconfig) || !is_array($getconfig) || empty($getconfig['accesskey'])) {
|
||||
$config_array = apply_filters('updraftplus_vault_config_add_headers', array('timeout' => 25, 'body' => $post_body));
|
||||
|
||||
$getconfig = wp_remote_post($this->vault_mothership.'/?udm_action=vault_getconfig', $config_array);
|
||||
}
|
||||
|
||||
$details_retrieved = false;
|
||||
if (!is_wp_error($getconfig) && false != $getconfig && isset($getconfig['body'])) {
|
||||
|
||||
$response_code = wp_remote_retrieve_response_code($getconfig);
|
||||
|
||||
if ($response_code >= 200 && $response_code < 300) {
|
||||
$response = json_decode(wp_remote_retrieve_body($getconfig), true);
|
||||
if (is_array($response) && isset($response['user_messages']) && is_array($response['user_messages'])) {
|
||||
foreach ($response['user_messages'] as $message) {
|
||||
if (!is_array($message)) continue;
|
||||
$msg_txt = $this->vault_translate_remote_message($message['message'], $message['code']);
|
||||
$this->log($msg_txt, $message['level'], $message['code']);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_array($response) && isset($response['accesskey']) && isset($response['secretkey']) && isset($response['path'])) {
|
||||
$details_retrieved = true;
|
||||
$opts['last_config']['accesskey'] = $response['accesskey'];
|
||||
$opts['last_config']['secretkey'] = $response['secretkey'];
|
||||
$opts['last_config']['path'] = $response['path'];
|
||||
unset($opts['last_config']['quota_root']);
|
||||
if (!empty($response['quota_root'])) {
|
||||
$opts['last_config']['quota_root'] = $response['quota_root'];
|
||||
$config['quota_root'] = $response['quota_root'];
|
||||
$opts['quota_root'] = $response['quota_root'];
|
||||
}
|
||||
$opts['last_config']['time'] = time();
|
||||
// This is just a cache of the most recent setting
|
||||
if (isset($response['quota'])) {
|
||||
$opts['quota'] = $response['quota'];
|
||||
$config['quota'] = $response['quota'];
|
||||
}
|
||||
$this->set_options($opts, true);
|
||||
$config['accesskey'] = $response['accesskey'];
|
||||
$config['secretkey'] = $response['secretkey'];
|
||||
$config['path'] = $response['path'];
|
||||
$config['sessiontoken'] = (isset($response['sessiontoken']) ? $response['sessiontoken'] : '');
|
||||
} elseif (is_array($response) && isset($response['result']) && ('token_unknown' == $response['result'] || 'site_duplicated' == $response['result'])) {
|
||||
$this->log("This site appears to not be connected to UpdraftPlus Vault (".$response['result'].")");
|
||||
$config['error'] = array('message' => 'site_not_connected', 'values' => array($response['result']));
|
||||
|
||||
$config['accesskey'] = '';
|
||||
$config['secretkey'] = '';
|
||||
$config['path'] = '';
|
||||
$config['sessiontoken'] = '';
|
||||
unset($config['quota']);
|
||||
if (!empty($response['message'])) $config['error_message'] = $response['message'];
|
||||
$details_retrieved = true;
|
||||
} else {
|
||||
if (is_array($response) && !empty($response['result'])) {
|
||||
$msg = "response code: ".$response['result'];
|
||||
if (!empty($response['code'])) $msg .= " (".$response['code'].")";
|
||||
if (!empty($response['message'])) $msg .= " (".$response['message'].")";
|
||||
if (!empty($response['data'])) $msg .= " (".json_encode($response['data']).")";
|
||||
$this->log($msg);
|
||||
$config['error'] = array('message' => 'general_error_response', 'values' => array($msg));
|
||||
} else {
|
||||
$this->log("Received response, but it was not in the expected format: ".substr(wp_remote_retrieve_body($getconfig), 0, 100).' ...');
|
||||
$config['error'] = array('message' => 'unexpected_format', 'values' => array(substr(wp_remote_retrieve_body($getconfig), 0, 100).' ...'));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->log("Unexpected HTTP response code (please try again later): ".$response_code);
|
||||
$config['error'] = array('message' => 'unexpected_http_response', 'values' => array($response_code));
|
||||
}
|
||||
} elseif (is_wp_error($getconfig)) {
|
||||
$updraftplus->log_wp_error($getconfig);
|
||||
$config['error'] = array('message' => 'general_error_response', 'values' => array($getconfig));
|
||||
} else {
|
||||
if (!isset($getconfig['accesskey'])) {
|
||||
$this->log("wp_remote_post returned a result that was not understood (".gettype($getconfig).")");
|
||||
$config['error'] = array('message' => 'result_not_understood', 'values' => array(gettype($getconfig)));
|
||||
}
|
||||
}
|
||||
|
||||
if (!$details_retrieved) {
|
||||
// Don't log anything yet, as this will replace the most recently logged message in the main panel
|
||||
if (!empty($opts['last_config']) && is_array($opts['last_config'])) {
|
||||
$last_config = $opts['last_config'];
|
||||
if (!empty($last_config['time']) && is_numeric($last_config['time']) && $last_config['time'] > time() - 86400*15) {
|
||||
if ($updraftplus->backup_time) $this->log("failed to retrieve access details from updraftplus.com: will attempt to use most recently stored configuration");
|
||||
if (!empty($last_config['accesskey'])) $config['accesskey'] = $last_config['accesskey'];
|
||||
if (!empty($last_config['secretkey'])) $config['secretkey'] = $last_config['secretkey'];
|
||||
if (isset($last_config['path'])) $config['path'] = $last_config['path'];
|
||||
if (isset($opts['quota'])) $config['quota'] = $opts['quota'];
|
||||
} else {
|
||||
if ($updraftplus->backup_time) $this->log("failed to retrieve access details from updraftplus.com: no recently stored configuration was found to use instead");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$config['server_side_encryption'] = 'AES256';
|
||||
$this->vault_config = $config;
|
||||
$this->jobdata_set('config', $config);
|
||||
// N.B. This isn't multi-server compatible
|
||||
set_transient('udvault_last_config', $config, 86400*7);
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to always use server-side encryption - which, with Vault, we do (and our marketing says so).
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
protected function use_sse() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function vault_translate_remote_message($message, $code) {
|
||||
switch ($code) {
|
||||
case 'premium_overdue':
|
||||
return __('Your UpdraftPlus Premium purchase is over a year ago. You should renew immediately to avoid losing the 12 months of free storage allowance that you get for being a current UpdraftPlus Premium customer.', 'updraftplus');
|
||||
break;
|
||||
case 'vault_subscription_overdue':
|
||||
return __('You have an UpdraftPlus Vault subscription with overdue payment. You are within the few days of grace period before it will be suspended, and you will lose your quota and access to data stored within it. Please renew as soon as possible!', 'updraftplus');
|
||||
break;
|
||||
case 'vault_subscription_suspended':
|
||||
return __("You have an UpdraftPlus Vault subscription that has not been renewed, and the grace period has expired. In a few days' time, your stored data will be permanently removed. If you do not wish this to happen, then you should renew as soon as possible.", 'updraftplus');
|
||||
// The following shouldn't be a possible response (the server can deal with duplicated sites with the same IDs) - but there's no harm leaving it in for now (Dec 2015)
|
||||
// This means that the site is accessing with a different home_url() than it was registered with.
|
||||
break;
|
||||
case 'site_duplicated':
|
||||
return __('No Vault connection was found for this site (has it moved?); please disconnect and re-connect.', 'updraftplus');
|
||||
break;
|
||||
}
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* This over-rides the method in UpdraftPlus_BackupModule and stops the hidden version field being output. This is so that blank settings are not returned and saved to the database as this storage option outputs no other fields.
|
||||
*
|
||||
* @return [boolean] - return false so that the hidden version field is not output
|
||||
*/
|
||||
public function print_shared_settings_fields() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the pre configuration template
|
||||
*
|
||||
* @return Void - currently does not have a pre config template, this method is needed to stop it taking it's parents
|
||||
*/
|
||||
public function get_pre_configuration_template() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configuration template
|
||||
*
|
||||
* @return String - the template, ready for substitutions to be carried out
|
||||
*/
|
||||
public function get_configuration_template() {
|
||||
global $updraftplus, $updraftplus_checkout_embed;
|
||||
|
||||
$checkout_embed_5gb_attribute = '';
|
||||
$checkout_embed_15gb_attribute = '';
|
||||
$checkout_embed_50gb_attribute = '';
|
||||
$checkout_embed_250gb_attribute = '';
|
||||
|
||||
if ($updraftplus_checkout_embed) {
|
||||
$checkout_embed_5gb_attribute = $updraftplus_checkout_embed->get_product('updraftplus-vault-storage-5-gb') ? 'data-embed-checkout="'.apply_filters('updraftplus_com_link', $updraftplus_checkout_embed->get_product('updraftplus-vault-storage-5-gb', UpdraftPlus_Options::admin_page_url().'?page=updraftplus&tab=settings')).'"' : '';
|
||||
$checkout_embed_15gb_attribute = $updraftplus_checkout_embed->get_product('updraftplus-vault-storage-15-gb') ? 'data-embed-checkout="'.apply_filters('updraftplus_com_link', $updraftplus_checkout_embed->get_product('updraftplus-vault-storage-15-gb', UpdraftPlus_Options::admin_page_url().'?page=updraftplus&tab=settings')).'"' : '';
|
||||
$checkout_embed_50gb_attribute = $updraftplus_checkout_embed->get_product('updraftplus-vault-storage-50-gb') ? 'data-embed-checkout="'.apply_filters('updraftplus_com_link', $updraftplus_checkout_embed->get_product('updraftplus-vault-storage-50-gb', UpdraftPlus_Options::admin_page_url().'?page=updraftplus&tab=settings')).'"' : '';
|
||||
$checkout_embed_250gb_attribute = $updraftplus_checkout_embed->get_product('updraftplus-vault-storage-250-gb') ? 'data-embed-checkout="'.apply_filters('updraftplus_com_link', $updraftplus_checkout_embed->get_product('updraftplus-vault-storage-250-gb', UpdraftPlus_Options::admin_page_url().'?page=updraftplus&tab=settings')).'"' : '';
|
||||
}
|
||||
|
||||
// Used to decide whether we can afford HTTP calls or not, or would prefer to rely on cached data
|
||||
$this->vault_in_config_print = true;
|
||||
|
||||
$shop_url_base = $this->get_url();
|
||||
$get_more_quota = $this->get_url('get_more_quota');
|
||||
|
||||
$vault_settings = $this->get_options();
|
||||
$connected = (!empty($vault_settings['token']) && !empty($vault_settings['email'])) ? true : false;
|
||||
$classes = $this->get_css_classes();
|
||||
$template_str = '
|
||||
<tr class="'.$classes.'">
|
||||
<th><img id="vaultlogo" src="'.esc_attr(UPDRAFTPLUS_URL.'/images/updraftvault-150.png').'" alt="UpdraftPlus Vault" width="150" height="116"></th>
|
||||
<td valign="top" id="updraftvault_settings_cell">';
|
||||
global $updraftplus_admin;
|
||||
|
||||
if (!class_exists('SimpleXMLElement')) {
|
||||
$template_str .= $updraftplus_admin->show_double_warning('<strong>'.__('Warning', 'updraftplus').':</strong> '.sprintf(__("Your web server's PHP installation does not included a <strong>required</strong> (for %s) module (%s). Please contact your web hosting provider's support and ask for them to enable it.", 'updraftplus'), 'UpdraftPlus Vault', 'SimpleXMLElement'), 'updraftvault', false);
|
||||
}
|
||||
$template_str .= $updraftplus_admin->curl_check('UpdraftPlus Vault', false, 'updraftvault', false).'
|
||||
<div id="updraftvault_settings_default"{{#if is_connected}} style="display:none;" class="updraft-hidden"{{/if}}>
|
||||
<p>
|
||||
'.__('UpdraftPlus Vault brings you storage that is <strong>reliable, easy to use and a great price</strong>.', 'updraftplus').' '.__('Press a button to get started.', 'updraftplus').'
|
||||
</p>
|
||||
<div class="vault_primary_option clear-left">
|
||||
<div><strong>'.__('Need to get space?', 'updraftplus').'</strong></div>
|
||||
<button id="updraftvault_showoptions" class="button-primary">'.__('Show the options', 'updraftplus').'</button>
|
||||
</div>
|
||||
<div class="vault_primary_option">
|
||||
<div><strong>'.__('Already got space?', 'updraftplus').'</strong></div>
|
||||
<button id="updraftvault_connect" class="button-primary">'.__('Connect', 'updraftplus').'</button>
|
||||
</div>
|
||||
<p>
|
||||
<em>'.__("UpdraftPlus Vault is built on top of Amazon's world-leading data-centres, with redundant data storage to achieve 99.999999999% reliability.", 'updraftplus').'<a target="_blank" href="'.esc_attr($this->get_url('more_vault_info_landing')).'">'.__('Read more about it here.', 'updraftplus').'</a> <a target="_blank" href="'.esc_attr($this->get_url('more_vault_info_faqs')).'">'.__('Read the FAQs here.', 'updraftplus').'</a></em>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div id="updraftvault_settings_showoptions" style="display:none;" class="updraft-hidden">
|
||||
<p>
|
||||
'. __('UpdraftPlus Vault brings you storage that is <strong>reliable, easy to use and a great price</strong>.', 'updraftplus').' '.__('Press a button to get started.', 'updraftplus').'</p>
|
||||
<div class="vault-purchase-option-container">
|
||||
<div class="vault-purchase-option">
|
||||
<div class="vault-purchase-option-size">5 GB</div>
|
||||
<div class="vault-purchase-option-link"><b>'.sprintf(__('%s per year', 'updraftplus'), '$35').'</b></div>
|
||||
<div class="vault-purchase-option-or">'.__('with the option of', 'updraftplus').'</div>
|
||||
<div class="vault-purchase-option-link"><b>'.sprintf(__('%s month %s trial', 'updraftplus'), '1', '$1').'</b></div>
|
||||
<div class="vault-purchase-option-link"><a target="_blank" title="Start a 5GB UpdraftVault Subscription" href="'.apply_filters('updraftplus_com_link', $updraftplus->get_url('shop_vault_5')).'" '.$checkout_embed_5gb_attribute.'><button class="button-primary">'.__('Start Trial', 'updraftplus').'</button></a></div>
|
||||
</div>
|
||||
<div class="vault-purchase-option">
|
||||
<div class="vault-purchase-option-size">15 GB</div>
|
||||
<div class="vault-purchase-option-link"><b>'.sprintf(__('%s per quarter', 'updraftplus'), '$20').'</b></div>
|
||||
<div class="vault-purchase-option-or">'.__('or (annual discount)', 'updraftplus').'</div>
|
||||
<div class="vault-purchase-option-link"><b>'.sprintf(__('%s per year', 'updraftplus'), '$70').'</b></div>
|
||||
<div class="vault-purchase-option-link"><a target="_blank" title="Start a 15GB UpdraftVault Subscription" href="'.apply_filters('updraftplus_com_link', $updraftplus->get_url('shop_vault_15')).'" '.$checkout_embed_15gb_attribute.'><button class="button-primary">'.__('Start Subscription', 'updraftplus').'</button></a></div>
|
||||
</div>
|
||||
<div class="vault-purchase-option">
|
||||
<div class="vault-purchase-option-size">50 GB</div>
|
||||
<div class="vault-purchase-option-link"><b>'.sprintf(__('%s per quarter', 'updraftplus'), '$50').'</b></div>
|
||||
<div class="vault-purchase-option-or">'.__('or (annual discount)', 'updraftplus').'</div>
|
||||
<div class="vault-purchase-option-link"><b>'.sprintf(__('%s per year', 'updraftplus'), '$175').'</b></div>
|
||||
<div class="vault-purchase-option-link"><a target="_blank" title="Start a 50GB UpdraftVault Subscription" href="'.apply_filters('updraftplus_com_link', $updraftplus->get_url('shop_vault_50')).'" '.$checkout_embed_50gb_attribute.'><button class="button-primary">'.__('Start Subscription', 'updraftplus').'</button></a></div>
|
||||
</div>
|
||||
<div class="vault-purchase-option">
|
||||
<div class="vault-purchase-option-size">250 GB</div>
|
||||
<div class="vault-purchase-option-link"><b>'.sprintf(__('%s per quarter', 'updraftplus'), '$125').'</b></div>
|
||||
<div class="vault-purchase-option-or">'.__('or (annual discount)', 'updraftplus').'</div>
|
||||
<div class="vault-purchase-option-link"><b>'.sprintf(__('%s per year', 'updraftplus'), '$450').'</b></div>
|
||||
<div class="vault-purchase-option-link"><a target="_blank" title="Start a 250GB UpdraftVault Subscription" href="'.apply_filters('updraftplus_com_link', $updraftplus->get_url('shop_vault_250')).'" '.$checkout_embed_250gb_attribute.'><button class="button-primary">'.__('Start Subscription', 'updraftplus').'</button></a></div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="clear-left padding-top-20px">
|
||||
'.__('Payments can be made in US dollars, euros or GB pounds sterling, via card or PayPal.', 'updraftplus').' '. __('Subscriptions can be cancelled at any time.', 'updraftplus').'
|
||||
</p>
|
||||
<p class="clear-left padding-top-20px">
|
||||
<em>'.__("UpdraftPlus Vault is built on top of Amazon's world-leading data-centres, with redundant data storage to achieve 99.999999999% reliability.", 'updraftplus').' <a target="_blank" href="'.esc_attr($this->get_url('more_vault_info_landing')).'">'.__('Read more about it here.', 'updraftplus').'</a> <a target="_blank" href="'.esc_attr($this->get_url('more_vault_info_faqs')).'">'.__('Read the FAQs here.', 'updraftplus').'</a></em>
|
||||
</p>
|
||||
<p>
|
||||
<a href="'.UpdraftPlus::get_current_clean_url().'" class="updraftvault_backtostart">'.__('Back...', 'updraftplus').'</a>
|
||||
</p>
|
||||
</div>
|
||||
<div id="updraftvault_settings_connect" data-instance_id="{{instance_id}}" style="display:none;" class="updraft-hidden">
|
||||
<p>'.__('Enter your UpdraftPlus.Com email / password here to connect:', 'updraftplus').'</p>
|
||||
<p>
|
||||
<input id="updraftvault_email" class="udignorechange" type="text" placeholder="'.esc_attr__('E-mail', 'updraftplus').'">
|
||||
<input id="updraftvault_pass" class="udignorechange" type="password" placeholder="'.esc_attr__('Password', 'updraftplus').'">
|
||||
<button id="updraftvault_connect_go" class="button-primary">'.__('Connect', 'updraftplus').'</button>
|
||||
</p>
|
||||
<p class="padding-top-14px">
|
||||
<em>'.__("Don't know your email address, or forgotten your password?", 'updraftplus').' <a href="'.esc_attr($this->get_url('vault_forgotten_credentials_links')).'">'.__('Go here for help', 'updraftplus').'</a></em>
|
||||
</p>
|
||||
<p class="padding-top-14px">
|
||||
<em><a href="'.UpdraftPlus::get_current_clean_url().'" class="updraftvault_backtostart">'.__('Back...', 'updraftplus').'</a></em>
|
||||
</p>
|
||||
</div>
|
||||
<div id="updraftvault_settings_connected"{{#unless is_connected}} style="display:none;" class="updraft-hidden"{{/unless}}>
|
||||
'.$this->get_connected_configuration_template().'
|
||||
</div>
|
||||
</td>
|
||||
</tr>';
|
||||
$this->vault_in_config_print = false;
|
||||
return $template_str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the partial configuration template for connected html
|
||||
*
|
||||
* @return String - the partial template, ready for substitutions to be carried out
|
||||
*/
|
||||
public function get_connected_configuration_template() {
|
||||
$ret = '{{#if is_connected}}
|
||||
<p id="vault-is-connected">';
|
||||
$ret .= __('This site is <strong>connected</strong> to UpdraftPlus Vault.', 'updraftplus').' '.__("Well done - there's nothing more needed to set up.", 'updraftplus').'</p><p><strong>'.__('Vault owner', 'updraftplus').':</strong> {{email}}';
|
||||
$ret .= '<br><strong>'.__('Quota:', 'updraftplus').'</strong> ';
|
||||
$ret .= '{{{quota_text}}}';
|
||||
$ret .= '</p>';
|
||||
$ret .= '<p><button id="updraftvault_disconnect" class="button-primary">'.__('Disconnect', 'updraftplus').'</button></p>';
|
||||
$ret .= '{{else}}
|
||||
<p>'.__('You are <strong>not connected</strong> to UpdraftPlus Vault.', 'updraftplus').'</p>
|
||||
{{/if}}';
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies handerbar template options
|
||||
*
|
||||
* @param array $opts
|
||||
* @return array - Modified handerbar template options
|
||||
*/
|
||||
public function transform_options_for_template($opts) {
|
||||
if (!empty($opts['token']) || !empty($opts['email'])) {
|
||||
$opts['is_connected'] = true;
|
||||
}
|
||||
if (!isset($opts['quota']) || !is_numeric($opts['quota']) || $opts['quota'] < 0) {
|
||||
$opts['quota_text'] = __('Unknown', 'updraftplus');
|
||||
} else {
|
||||
$opts['quota_text'] = $this->s3_get_quota_info('text', $opts['quota']);
|
||||
}
|
||||
return $opts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether options have been set up by the user, or not
|
||||
*
|
||||
* @param Array $opts - the potential options
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
public function options_exist($opts) {
|
||||
if (is_array($opts) && isset($opts['email'])) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives settings keys which values should not passed to handlebarsjs context.
|
||||
* The settings stored in UD in the database sometimes also include internal information that it would be best not to send to the front-end (so that it can't be stolen by a man-in-the-middle attacker)
|
||||
*
|
||||
* @return array - Settings array keys which should be filtered
|
||||
*/
|
||||
public function filter_frontend_settings_keys() {
|
||||
return array(
|
||||
'last_config',
|
||||
'quota',
|
||||
'quota_root',
|
||||
'token',
|
||||
);
|
||||
}
|
||||
|
||||
private function connected_html($vault_settings = false) {
|
||||
if (!is_array($vault_settings)) {
|
||||
$vault_settings = $this->get_options();
|
||||
}
|
||||
if (!is_array($vault_settings) || empty($vault_settings['token']) || empty($vault_settings['email'])) return '<p>'.__('You are <strong>not connected</strong> to UpdraftPlus Vault.', 'updraftplus').'</p>';
|
||||
|
||||
$ret = '<p id="vault-is-connected">';
|
||||
|
||||
$ret .= __('This site is <strong>connected</strong> to UpdraftPlus Vault.', 'updraftplus').' '.__("Well done - there's nothing more needed to set up.", 'updraftplus').'</p><p><strong>'.__('Vault owner', 'updraftplus').':</strong> '.htmlspecialchars($vault_settings['email']);
|
||||
|
||||
$ret .= '<br><strong>'.__('Quota:', 'updraftplus').'</strong> ';
|
||||
if (!isset($vault_settings['quota']) || !is_numeric($vault_settings['quota']) || $vault_settings['quota'] < 0) {
|
||||
$ret .= __('Unknown', 'updraftplus');
|
||||
} else {
|
||||
$ret .= $this->s3_get_quota_info('text', $vault_settings['quota']);
|
||||
}
|
||||
$ret .= '</p>';
|
||||
|
||||
$ret .= '<p><button id="updraftvault_disconnect" class="button-primary">'.__('Disconnect', 'updraftplus').'</button></p>';
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
protected function s3_out_of_quota($total, $used, $needed) {
|
||||
global $updraftplus;
|
||||
$this->log("Error: Quota exhausted (used=$used, total=$total, needed=$needed)");
|
||||
$this->log(sprintf(__('Error: you have insufficient storage quota available (%s) to upload this archive (%s).', 'updraftplus'), round(($total-$used)/1048576, 2).' MB', round($needed/1048576, 2).' MB').' '.__('You can get more quota here', 'updraftplus').': '.$this->get_url('get_more_quota'), 'error');
|
||||
}
|
||||
|
||||
protected function s3_record_quota_info($quota_used, $quota) {
|
||||
|
||||
$ret = __('Current use:', 'updraftplus').' '.round($quota_used / 1048576, 1).' / '.round($quota / 1048576, 1).' MB';
|
||||
$ret .= ' ('.sprintf('%.1f', 100*$quota_used / max($quota, 1)).' %)';
|
||||
|
||||
$ret .= ' - <a href="'.esc_attr($this->get_url('get_more_quota')).'">'.__('Get more quota', 'updraftplus').'</a>';
|
||||
|
||||
$ret_dashboard = $ret . ' - <a href="#" id="updraftvault_recountquota">'.__('Refresh current status', 'updraftplus').'</a>';
|
||||
|
||||
set_transient('updraftvault_quota_text', $ret_dashboard, 86400*3);
|
||||
|
||||
}
|
||||
|
||||
public function s3_prune_retained_backups_finished() {
|
||||
$config = $this->get_config();
|
||||
$quota = $config['quota'];
|
||||
$quota_used = $this->s3_get_quota_info('numeric', $config['quota']);
|
||||
|
||||
$ret = __('Current use:', 'updraftplus').' '.round($quota_used / 1048576, 1).' / '.round($quota / 1048576, 1).' MB';
|
||||
$ret .= ' ('.sprintf('%.1f', 100*$quota_used / max($quota, 1)).' %)';
|
||||
|
||||
$ret_plain = $ret . ' - '.__('Get more quota', 'updraftplus').': '.$this->get_url('get_more_quota');
|
||||
|
||||
$ret .= ' - <a href="'.esc_attr($this->get_url('get_more_quota')).'">'.__('Get more quota', 'updraftplus').'</a>';
|
||||
|
||||
do_action('updraft_report_remotestorage_extrainfo', 'updraftvault', $ret, $ret_plain);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will return the S3 quota Information
|
||||
*
|
||||
* @param string|integer $format n numeric, returns an integer or false for an error (never returns an error)
|
||||
* @param integer $quota S3 quota information
|
||||
* @return string|integer
|
||||
*/
|
||||
protected function s3_get_quota_info($format = 'numeric', $quota = 0) {
|
||||
$ret = '';
|
||||
|
||||
if ($quota > 0) {
|
||||
|
||||
if (!empty($this->vault_in_config_print) && 'text' == $format) {
|
||||
$quota_via_transient = get_transient('updraftvault_quota_text');
|
||||
if (is_string($quota) && $quota) return $quota;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
$config = $this->get_config();
|
||||
|
||||
if (empty($config['quota_root'])) {
|
||||
// This next line is wrong: it lists the files *in this site's sub-folder*, rather than the whole Vault
|
||||
$current_files = $this->listfiles('');
|
||||
} else {
|
||||
$current_files = $this->listfiles_with_path($config['quota_root'], '', true);
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
global $updraftplus;
|
||||
$this->log("Listfiles failed during quota calculation: ".$e->getMessage());
|
||||
$current_files = new WP_Error('listfiles_exception', $e->getMessage().' ('.get_class($e).')');
|
||||
}
|
||||
|
||||
$ret .= __('Current use:', 'updraftplus').' ';
|
||||
|
||||
$counted = false;
|
||||
if (is_wp_error($current_files)) {
|
||||
$ret .= __('Error:', 'updraftplus').' '.$current_files->get_error_message().' ('.$current_files->get_error_code().')';
|
||||
} elseif (!is_array($current_files)) {
|
||||
$ret .= __('Unknown', 'updraftplus');
|
||||
} else {
|
||||
foreach ($current_files as $file) {
|
||||
$counted += $file['size'];
|
||||
}
|
||||
$ret .= round($counted / 1048576, 1);
|
||||
$ret .= ' / '.round($quota / 1048576, 1).' MB';
|
||||
$ret .= ' ('.sprintf('%.1f', 100*$counted / $quota).' %)';
|
||||
}
|
||||
} else {
|
||||
$ret .= '0';
|
||||
}
|
||||
|
||||
$ret .= ' - <a href="'.esc_attr($this->get_url('get_more_quota')).'">'.__('Get more quota', 'updraftplus').'</a> - <a href="'.UpdraftPlus::get_current_clean_url().'" id="updraftvault_recountquota">'.__('Refresh current status', 'updraftplus').'</a>';
|
||||
|
||||
if ('text' == $format) set_transient('updraftvault_quota_text', $ret, 86400*3);
|
||||
|
||||
return ('text' == $format) ? $ret : $counted;
|
||||
}
|
||||
|
||||
public function credentials_test($posted_settings) {
|
||||
$this->credentials_test_engine($this->get_config(), $posted_settings);
|
||||
}
|
||||
|
||||
public function ajax_vault_recountquota($echo_results = true) {
|
||||
// Force the opts to be refreshed
|
||||
$config = $this->get_config();
|
||||
|
||||
if (empty($config['accesskey']) && !empty($config['error_message'])) {
|
||||
$results = array('html' => htmlspecialchars($config['error_message']), 'connected' => 0);
|
||||
} else {
|
||||
// Now read the opts
|
||||
$opts = $this->get_options();
|
||||
$results = array('html' => $this->connected_html($opts), 'connected' => 1);
|
||||
}
|
||||
if ($echo_results) {
|
||||
echo json_encode($results);
|
||||
} else {
|
||||
return $results;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method also gets called directly, so don't add code that assumes that it's definitely an AJAX situation
|
||||
*
|
||||
* @param boolean $echo_results check to see if the results need to be echoed
|
||||
* @return array
|
||||
*/
|
||||
public function ajax_vault_disconnect($echo_results = true) {
|
||||
$vault_settings = $this->get_options();
|
||||
$this->set_options(array(), true);
|
||||
global $updraftplus;
|
||||
|
||||
delete_transient('udvault_last_config');
|
||||
delete_transient('updraftvault_quota_text');
|
||||
|
||||
$response = array('disconnected' => 1, 'html' => $this->connected_html());
|
||||
|
||||
if ($echo_results) {
|
||||
$updraftplus->close_browser_connection(json_encode($response));
|
||||
}
|
||||
|
||||
// If $_POST['reset_hash'] is set, then we were alerted by updraftplus.com - no need to notify back
|
||||
if (is_array($vault_settings) && isset($vault_settings['email']) && empty($_POST['reset_hash'])) {
|
||||
|
||||
$post_body = array(
|
||||
'e' => (string) $vault_settings['email'],
|
||||
'sid' => $updraftplus->siteid(),
|
||||
'su' => base64_encode(home_url())
|
||||
);
|
||||
|
||||
if (!empty($vault_settings['token'])) $post_body['token'] = (string) $vault_settings['token'];
|
||||
|
||||
// Use SSL to prevent snooping
|
||||
wp_remote_post($this->vault_mothership.'/?udm_action=vault_disconnect', array(
|
||||
'timeout' => 20,
|
||||
'body' => $post_body,
|
||||
));
|
||||
}
|
||||
|
||||
return $response;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called from the UD admin object
|
||||
*
|
||||
* @param boolean $echo_results A Flag to see if results need to be echoed or returned
|
||||
* @param boolean|array $use_credentials Check if Vault needs to use credentials
|
||||
* @return array
|
||||
*/
|
||||
public function ajax_vault_connect($echo_results = true, $use_credentials = false) {
|
||||
|
||||
if (empty($use_credentials)) $use_credentials = $_REQUEST;
|
||||
|
||||
$connect = $this->vault_connect($use_credentials['email'], $use_credentials['pass']);
|
||||
if (true === $connect) {
|
||||
$response = array('connected' => true, 'html' => $this->connected_html(false));
|
||||
} else {
|
||||
$response = array(
|
||||
'e' => __('An unknown error occurred when trying to connect to UpdraftPlus.Com', 'updraftplus')
|
||||
);
|
||||
if (is_wp_error($connect)) {
|
||||
$response['e'] = $connect->get_error_message();
|
||||
$response['code'] = $connect->get_error_code();
|
||||
$response['data'] = serialize($connect->get_error_data());
|
||||
}
|
||||
}
|
||||
|
||||
if ($echo_results) {
|
||||
echo json_encode($response);
|
||||
} else {
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns either true (in which case the Vault token will be stored), or false|WP_Error
|
||||
*
|
||||
* @param string $email Vault Email
|
||||
* @param string $password Vault Password
|
||||
* @return boolean|WP_Error
|
||||
*/
|
||||
private function vault_connect($email, $password) {
|
||||
|
||||
// Username and password set up?
|
||||
if (empty($email) || empty($password)) return new WP_Error('blank_details', __('You need to supply both an email address and a password', 'updraftplus'));
|
||||
|
||||
global $updraftplus;
|
||||
|
||||
$remote_post_array = apply_filters('updraftplus_vault_config_add_headers', array(
|
||||
'timeout' => 20,
|
||||
'body' => array(
|
||||
'e' => $email,
|
||||
'p' => base64_encode($password),
|
||||
'sid' => $updraftplus->siteid(),
|
||||
'su' => base64_encode(home_url())
|
||||
)
|
||||
));
|
||||
|
||||
// Use SSL to prevent snooping
|
||||
$result = wp_remote_post($this->vault_mothership.'/?udm_action=vault_connect', $remote_post_array);
|
||||
|
||||
if (is_wp_error($result) || false === $result) return $result;
|
||||
|
||||
$response = json_decode(wp_remote_retrieve_body($result), true);
|
||||
|
||||
if (!is_array($response) || !isset($response['mothership']) || !isset($response['loggedin'])) {
|
||||
|
||||
if (preg_match('/has banned your IP address \(([\.:0-9a-f]+)\)/', $result['body'], $matches)) {
|
||||
return new WP_Error('banned_ip', sprintf(__("UpdraftPlus.com has responded with 'Access Denied'.", 'updraftplus').'<br>'.__("It appears that your web server's IP Address (%s) is blocked.", 'updraftplus').' '.__('This most likely means that you share a webserver with a hacked website that has been used in previous attacks.', 'updraftplus').'<br> <a href="'.apply_filters("updraftplus_com_link", "https://updraftplus.com/unblock-ip-address/").'" target="_blank">'.__('To remove the block, please go here.', 'updraftplus').'</a> ', $matches[1]));
|
||||
} else {
|
||||
return new WP_Error('unknown_response', sprintf(__('UpdraftPlus.Com returned a response which we could not understand (data: %s)', 'updraftplus'), wp_remote_retrieve_body($result)));
|
||||
}
|
||||
}
|
||||
|
||||
switch ($response['loggedin']) {
|
||||
case 'connected':
|
||||
if (!empty($response['token'])) {
|
||||
// Store it
|
||||
$vault_settings = $this->get_options();
|
||||
if (!is_array($vault_settings)) $vault_settings = array();
|
||||
$vault_settings['email'] = $email;
|
||||
$vault_settings['token'] = (string) $response['token'];
|
||||
$vault_settings['quota'] = -1;
|
||||
unset($vault_settings['last_config']);
|
||||
if (isset($response['quota'])) $vault_settings['quota'] = $response['quota'];
|
||||
$this->set_options($vault_settings, true);
|
||||
if (!empty($response['config']) && is_array($response['config'])) {
|
||||
if (!empty($response['config']['accesskey'])) {
|
||||
$this->vault_set_config($response['config']);
|
||||
} elseif (!empty($response['config']['result']) && ('token_unknown' == $response['config']['result'] || 'site_duplicated' == $response['config']['result'])) {
|
||||
return new WP_Error($response['config']['result'], $this->vault_translate_remote_message($response['config']['message'], $response['config']['result']));
|
||||
}
|
||||
// else... would also be an error condition, but not one known possible (and it will show a generic error anyway)
|
||||
}
|
||||
} elseif (isset($response['quota']) && !$response['quota']) {
|
||||
return new WP_Error('no_quota', __('You do not currently have any UpdraftPlus Vault quota', 'updraftplus'));
|
||||
} else {
|
||||
return new WP_Error('unknown_response', __('UpdraftPlus.Com returned a response, but we could not understand it', 'updraftplus'));
|
||||
}
|
||||
break;
|
||||
case 'authfailed':
|
||||
if (!empty($response['authproblem'])) {
|
||||
if ('invalidpassword' == $response['authproblem']) {
|
||||
$authfail_error = new WP_Error('authfailed', __('Your email address was valid, but your password was not recognised by UpdraftPlus.Com.', 'updraftplus').' <a href="'.esc_attr($this->get_url('vault_forgotten_credentials_links')).'">'.__('If you have forgotten your password, then go here to change your password on updraftplus.com.', 'updraftplus').'</a>');
|
||||
return $authfail_error;
|
||||
} elseif ('invaliduser' == $response['authproblem']) {
|
||||
return new WP_Error('authfailed', __('You entered an email address that was not recognised by UpdraftPlus.Com', 'updraftplus'));
|
||||
}
|
||||
}
|
||||
return new WP_Error('authfailed', __('Your email address and password were not recognised by UpdraftPlus.Com', 'updraftplus'));
|
||||
break;
|
||||
default:
|
||||
return new WP_Error('unknown_response', __('UpdraftPlus.Com returned a response, but we could not understand it', 'updraftplus'));
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
33
wp-content/plugins/updraftplus/methods/webdav.php
Normal file
33
wp-content/plugins/updraftplus/methods/webdav.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed.');
|
||||
|
||||
if (class_exists('UpdraftPlus_Addons_RemoteStorage_webdav')) {
|
||||
|
||||
// Migrate options to new-style storage - April 2017
|
||||
if (!is_array(UpdraftPlus_Options::get_updraft_option('updraft_webdav')) && '' != UpdraftPlus_Options::get_updraft_option('updraft_webdav_settings', '')) {
|
||||
$opts = UpdraftPlus_Options::get_updraft_option('updraft_webdav_settings');
|
||||
UpdraftPlus_Options::update_updraft_option('updraft_webdav', $opts);
|
||||
UpdraftPlus_Options::delete_updraft_option('updraft_webdav_settings');
|
||||
}
|
||||
|
||||
class UpdraftPlus_BackupModule_webdav extends UpdraftPlus_Addons_RemoteStorage_webdav {
|
||||
public function __construct() {
|
||||
parent::__construct('webdav', 'WebDAV');
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
include_once(UPDRAFTPLUS_DIR.'/methods/addon-not-yet-present.php');
|
||||
|
||||
/**
|
||||
* N.B. UpdraftPlus_BackupModule_AddonNotYetPresent extends UpdraftPlus_BackupModule
|
||||
*/
|
||||
class UpdraftPlus_BackupModule_webdav extends UpdraftPlus_BackupModule_AddonNotYetPresent {
|
||||
public function __construct() {
|
||||
parent::__construct('webdav', 'WebDAV');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user