addon-base-v2.php
6 years ago
addon-not-yet-present.php
6 years ago
azure.php
8 years ago
backblaze.php
8 years ago
backup-module.php
6 years ago
cloudfiles-new.php
6 years ago
cloudfiles.php
6 years ago
dreamobjects.php
7 years ago
dropbox.php
6 years ago
email.php
6 years ago
ftp.php
6 years ago
googlecloud.php
8 years ago
googledrive.php
6 years ago
insufficient.php
6 years ago
onedrive.php
8 years ago
openstack-base.php
6 years ago
openstack.php
8 years ago
openstack2.php
6 years ago
remotesend.php
6 years ago
s3.php
6 years ago
s3generic.php
7 years ago
sftp.php
8 years ago
template.php
6 years ago
updraftvault.php
6 years ago
webdav.php
8 years ago
addon-base-v2.php
372 lines
| 1 | <?php |
| 2 | |
| 3 | if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed'); |
| 4 | |
| 5 | /* |
| 6 | Methods to define when extending this class (can use $this->storage and $this->options where relevant): |
| 7 | do_bootstrap($possible_options_array) # Return a WP_Error object if something goes wrong |
| 8 | do_upload($file, $sourcefile) # Return true/false |
| 9 | do_listfiles($match) |
| 10 | do_delete($file) - return true/false |
| 11 | do_download($file, $fullpath, $start_offset) - return true/false |
| 12 | do_config_print() |
| 13 | get_credentials_test_required_parameters() - return an array: keys = required _POST parameters; values = description of each |
| 14 | do_credentials_test($testfile, $posted_settings) - return true/false; or alternatively an array with keys 'result' (true/false) and 'data' (arbitrary debug data) |
| 15 | do_credentials_test_deletefile($testfile, $posted_settings) |
| 16 | */ |
| 17 | |
| 18 | // Uses job options: Yes |
| 19 | // Uses single-array storage: Yes |
| 20 | |
| 21 | if (!class_exists('UpdraftPlus_BackupModule')) require_once(UPDRAFTPLUS_DIR.'/methods/backup-module.php'); |
| 22 | |
| 23 | /** |
| 24 | * 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. |
| 25 | */ |
| 26 | abstract class UpdraftPlus_RemoteStorage_Addons_Base_v2 extends UpdraftPlus_BackupModule { |
| 27 | |
| 28 | protected $method; |
| 29 | |
| 30 | protected $description; |
| 31 | |
| 32 | protected $options; |
| 33 | |
| 34 | private $chunked; |
| 35 | |
| 36 | public function __construct($method, $description, $chunked = true, $test_button = true) { |
| 37 | |
| 38 | $this->method = $method; |
| 39 | $this->description = $description; |
| 40 | $this->chunked = $chunked; |
| 41 | $this->test_button = $test_button; |
| 42 | |
| 43 | } |
| 44 | |
| 45 | /** |
| 46 | * download method: takes a file name (base name), and removes it from the cloud storage |
| 47 | * |
| 48 | * @param string $file specific file for being removed from cloud storage |
| 49 | * @return array |
| 50 | */ |
| 51 | public function download($file) { |
| 52 | return $this->download_file(false, $file); |
| 53 | } |
| 54 | |
| 55 | public function backup($backup_array) { |
| 56 | return $this->upload_files(null, $backup_array); |
| 57 | } |
| 58 | |
| 59 | public function delete($files, $method_obj = false, $sizeinfo = array()) { |
| 60 | return $this->delete_files(false, $files, $method_obj, $sizeinfo); |
| 61 | } |
| 62 | |
| 63 | protected function required_configuration_keys() { |
| 64 | } |
| 65 | |
| 66 | public function upload_files($ret, $backup_array) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found |
| 67 | |
| 68 | global $updraftplus; |
| 69 | |
| 70 | $this->options = $this->get_options(); |
| 71 | |
| 72 | if (!$this->options_exist($this->options)) { |
| 73 | $this->log('No settings were found'); |
| 74 | $this->log(sprintf(__('No %s settings were found', 'updraftplus'), $this->description), 'error'); |
| 75 | return false; |
| 76 | } |
| 77 | |
| 78 | $storage = $this->bootstrap(); |
| 79 | if (is_wp_error($storage)) return $updraftplus->log_wp_error($storage, false, true); |
| 80 | |
| 81 | $this->set_storage($storage); |
| 82 | |
| 83 | $updraft_dir = trailingslashit($updraftplus->backups_dir_location()); |
| 84 | |
| 85 | foreach ($backup_array as $file) { |
| 86 | $this->log("upload ".((!empty($this->options['ownername'])) ? '(account owner: '.$this->options['ownername'].')' : '').": attempt: $file"); |
| 87 | try { |
| 88 | if ($this->do_upload($file, $updraft_dir.$file)) { |
| 89 | $updraftplus->uploaded_file($file); |
| 90 | } else { |
| 91 | $any_failures = true; |
| 92 | $this->log('ERROR: Failed to upload file: '.$file); |
| 93 | $this->log(__('Error', 'updraftplus').': '.$this->description.': '.sprintf(__('Failed to upload %s', 'updraftplus'), $file), 'error'); |
| 94 | } |
| 95 | } catch (Exception $e) { |
| 96 | $any_failures = true; |
| 97 | $this->log('ERROR ('.get_class($e).'): '.$file.': Failed to upload file: '.$e->getMessage().' (code: '.$e->getCode().', line: '.$e->getLine().', file: '.$e->getFile().')'); |
| 98 | $this->log(__('Error', 'updraftplus').': '.$this->description.': '.sprintf(__('Failed to upload %s', 'updraftplus'), $file), 'error'); |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | return (!empty($any_failures)) ? null : true; |
| 103 | |
| 104 | } |
| 105 | |
| 106 | public function listfiles($match = 'backup_') { |
| 107 | |
| 108 | try { |
| 109 | |
| 110 | if (!method_exists($this, 'do_listfiles')) { |
| 111 | return new WP_Error('no_listing', 'This remote storage method does not support file listing'); |
| 112 | } |
| 113 | |
| 114 | $this->options = $this->get_options(); |
| 115 | if (!$this->options_exist($this->options)) return new WP_Error('no_settings', sprintf(__('No %s settings were found', 'updraftplus'), $this->description)); |
| 116 | |
| 117 | $storage = $this->bootstrap(); |
| 118 | if (is_wp_error($storage)) return $storage; |
| 119 | |
| 120 | return $this->do_listfiles($match); |
| 121 | |
| 122 | } catch (Exception $e) { |
| 123 | $this->log('ERROR: Failed to list files: '.$e->getMessage().' (code: '.$e->getCode().', line: '.$e->getLine().', file: '.$e->getFile().')'); |
| 124 | return new WP_Error('list_failed', $this->description.': '.__('failed to list files', 'updraftplus')); |
| 125 | } |
| 126 | |
| 127 | } |
| 128 | |
| 129 | /** |
| 130 | * This function handles bootstrapping and calling the remote methods delete function |
| 131 | * |
| 132 | * @param boolean $ret - A boolean value |
| 133 | * @param array $files - An array of files to delete. |
| 134 | * @param boolean $ignore_it - unused parameter |
| 135 | * |
| 136 | * @return - On success returns true, false or WordPress Error on failure |
| 137 | */ |
| 138 | public function delete_files($ret, $files, $ignore_it = false) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found |
| 139 | |
| 140 | global $updraftplus; |
| 141 | |
| 142 | if (is_string($files)) $files = array($files); |
| 143 | |
| 144 | if (empty($files)) return true; |
| 145 | if (!method_exists($this, 'do_delete')) { |
| 146 | $this->log("Delete failed: this storage method does not allow deletions"); |
| 147 | return false; |
| 148 | } |
| 149 | |
| 150 | $storage = $this->get_storage(); |
| 151 | |
| 152 | if (empty($storage)) { |
| 153 | |
| 154 | $this->options = $this->get_options(); |
| 155 | if (!$this->options_exist($this->options)) { |
| 156 | $this->log('No settings were found'); |
| 157 | $this->log(sprintf(__('No %s settings were found', 'updraftplus'), $this->description), 'error'); |
| 158 | return false; |
| 159 | } |
| 160 | |
| 161 | $storage = $this->bootstrap(); |
| 162 | if (is_wp_error($storage)) return $storage; |
| 163 | |
| 164 | } |
| 165 | |
| 166 | $ret = true; |
| 167 | |
| 168 | if ($this->supports_feature('multi_delete')) { |
| 169 | $updraftplus->log("Delete remote files: ".implode($files)); |
| 170 | try { |
| 171 | $responses = $this->do_delete($files); |
| 172 | |
| 173 | if (is_array($responses)) { |
| 174 | foreach ($responses as $key => $response) { |
| 175 | if ('success' == $response) { |
| 176 | $updraftplus->log("$files[$key]: Delete succeeded"); |
| 177 | } elseif (is_array($response)) { |
| 178 | $ret = false; |
| 179 | if (isset($response['error']) && isset($response['error']['code']) && isset($response['error']['message'])) { |
| 180 | $updraftplus->log("Delete failed for file: $files[$key] with error code: ".$response['error']['code']." message: ".$response['error']['message']); |
| 181 | } else { |
| 182 | $updraftplus->log("Delete failed for file: $files[$key]"); |
| 183 | } |
| 184 | } |
| 185 | } |
| 186 | } elseif (!$responses) { |
| 187 | $ret = false; |
| 188 | $updraftplus->log("Delete failed for files: ".implode($files)); |
| 189 | } |
| 190 | } catch (Exception $e) { |
| 191 | $updraftplus->log('ERROR:'.implode($files).': Failed to delete files: '.$e->getMessage().' (code: '.$e->getCode().', line: '.$e->getLine().', file: '.$e->getFile().')'); |
| 192 | $ret = false; |
| 193 | } |
| 194 | |
| 195 | return $ret; |
| 196 | } |
| 197 | |
| 198 | foreach ($files as $file) { |
| 199 | $this->log("Delete remote: $file"); |
| 200 | try { |
| 201 | if (!$this->do_delete($file)) { |
| 202 | $ret = false; |
| 203 | $this->log("Delete failed"); |
| 204 | } else { |
| 205 | $this->log("$file: Delete succeeded"); |
| 206 | } |
| 207 | } catch (Exception $e) { |
| 208 | $this->log('ERROR: '.$file.': Failed to delete file: '.$e->getMessage().' (code: '.$e->getCode().', line: '.$e->getLine().', file: '.$e->getFile().')'); |
| 209 | $ret = false; |
| 210 | } |
| 211 | } |
| 212 | |
| 213 | return $ret; |
| 214 | |
| 215 | } |
| 216 | |
| 217 | public function download_file($ret, $files) { |
| 218 | |
| 219 | global $updraftplus; |
| 220 | |
| 221 | if (is_string($files)) $files = array($files); |
| 222 | |
| 223 | if (empty($files)) return true; |
| 224 | if (!method_exists($this, 'do_download')) { |
| 225 | $this->log("Download failed: this storage method does not allow downloading"); |
| 226 | $this->log(__('This storage method does not allow downloading', 'updraftplus'), 'error'); |
| 227 | return false; |
| 228 | } |
| 229 | |
| 230 | $this->options = $this->get_options(); |
| 231 | if (!$this->options_exist($this->options)) { |
| 232 | $this->log('No settings were found'); |
| 233 | $this->log(sprintf(__('No %s settings were found', 'updraftplus'), $this->description), 'error'); |
| 234 | return false; |
| 235 | } |
| 236 | |
| 237 | try { |
| 238 | $storage = $this->bootstrap(); |
| 239 | if (is_wp_error($storage)) return $updraftplus->log_wp_error($storage, false, true); |
| 240 | } catch (Exception $e) { |
| 241 | $ret = false; |
| 242 | $this->log('ERROR: '.$files[0].': Failed to download file: '.$e->getMessage().' (code: '.$e->getCode().', line: '.$e->getLine().', file: '.$e->getFile().')'); |
| 243 | $this->log(__('Error', 'updraftplus').': '.$this->description.': '.sprintf(__('Failed to download %s', 'updraftplus'), $files[0]), 'error'); |
| 244 | } |
| 245 | |
| 246 | $ret = true; |
| 247 | $updraft_dir = untrailingslashit($updraftplus->backups_dir_location()); |
| 248 | |
| 249 | foreach ($files as $file) { |
| 250 | try { |
| 251 | $fullpath = $updraft_dir.'/'.$file; |
| 252 | $start_offset = file_exists($fullpath) ? filesize($fullpath) : 0; |
| 253 | |
| 254 | if (false == $this->do_download($file, $fullpath, $start_offset)) { |
| 255 | $ret = false; |
| 256 | $this->log("error: failed to download: $file"); |
| 257 | $this->log("$file: ".sprintf(__("%s Error", 'updraftplus'), $this->description).": ".__('Failed to download', 'updraftplus'), 'error'); |
| 258 | } |
| 259 | |
| 260 | } catch (Exception $e) { |
| 261 | $ret = false; |
| 262 | $this->log('ERROR: '.$file.': Failed to download file: '.$e->getMessage().' (code: '.$e->getCode().', line: '.$e->getLine().', file: '.$e->getFile().')'); |
| 263 | $this->log(__('Error', 'updraftplus').': '.$this->description.': '.sprintf(__('Failed to download %s', 'updraftplus'), $file), 'error'); |
| 264 | } |
| 265 | } |
| 266 | |
| 267 | return $ret; |
| 268 | } |
| 269 | |
| 270 | /** |
| 271 | * Get the configuration template |
| 272 | * |
| 273 | * @return String - the template, ready for substitutions to be carried out |
| 274 | */ |
| 275 | public function get_configuration_template() { |
| 276 | $template_str = ''; |
| 277 | |
| 278 | if (method_exists($this, 'do_get_configuration_template')) { |
| 279 | $template_str .= $this->do_get_configuration_template(); |
| 280 | } |
| 281 | if (!$this->test_button || (method_exists($this, 'should_print_test_button') && !$this->should_print_test_button())) return $template_str; |
| 282 | $template_str .= $this->get_test_button_html($this->description); |
| 283 | return $template_str; |
| 284 | } |
| 285 | |
| 286 | /** |
| 287 | * Modifies handerbar template options |
| 288 | * |
| 289 | * @param array $opts |
| 290 | * @return array - Modified handerbar template options |
| 291 | */ |
| 292 | public function transform_options_for_template($opts) { |
| 293 | if (method_exists($this, 'do_transform_options_for_template')) { |
| 294 | $opts = $this->do_transform_options_for_template($opts); |
| 295 | } |
| 296 | return $opts; |
| 297 | } |
| 298 | |
| 299 | public function config_print_javascript_onready() { |
| 300 | $this->do_config_javascript(); |
| 301 | } |
| 302 | |
| 303 | protected function do_config_javascript() { |
| 304 | } |
| 305 | |
| 306 | /** |
| 307 | * Analyse the passed-in options to indicate whether something is configured or not. |
| 308 | * |
| 309 | * @param Array $opts - options to examine |
| 310 | * |
| 311 | * @return Boolean |
| 312 | */ |
| 313 | public function options_exist($opts) { |
| 314 | if (is_array($opts) && !empty($opts)) return true; |
| 315 | return false; |
| 316 | } |
| 317 | |
| 318 | public function bootstrap($opts = false, $connect = true) { |
| 319 | if (false === $opts) $opts = $this->options; |
| 320 | $storage = $this->get_storage(); |
| 321 | // Be careful of checking empty($opts) here - some storage methods may have no options until the OAuth token has been obtained |
| 322 | if ($connect && !$this->options_exist($opts)) return new WP_Error('no_settings', sprintf(__('No %s settings were found', 'updraftplus'), $this->description)); |
| 323 | if (!empty($storage) && !is_wp_error($storage)) return $storage; |
| 324 | return $this->do_bootstrap($opts, $connect); |
| 325 | } |
| 326 | |
| 327 | /** |
| 328 | * Run a credentials test. Output can be echoed. |
| 329 | * |
| 330 | * @param Array $posted_settings - settings to use |
| 331 | * |
| 332 | * @return Mixed - any data to return (gets logged in the browser eventually) |
| 333 | */ |
| 334 | public function credentials_test($posted_settings) { |
| 335 | |
| 336 | $required_test_parameters = $this->get_credentials_test_required_parameters(); |
| 337 | |
| 338 | foreach ($required_test_parameters as $param => $descrip) { |
| 339 | if (empty($posted_settings[$param])) { |
| 340 | printf(__("Failure: No %s was given.", 'updraftplus'), $descrip)."\n"; |
| 341 | return; |
| 342 | } |
| 343 | } |
| 344 | |
| 345 | $storage = $this->bootstrap($posted_settings); |
| 346 | |
| 347 | if (is_wp_error($storage)) { |
| 348 | echo __("Failed", 'updraftplus').": "; |
| 349 | foreach ($storage->get_error_messages() as $key => $msg) { |
| 350 | echo "$msg\n"; |
| 351 | } |
| 352 | return; |
| 353 | } |
| 354 | |
| 355 | $testfile = md5(time().rand()).'.txt'; |
| 356 | |
| 357 | $test_results = $this->do_credentials_test($testfile, $posted_settings); |
| 358 | |
| 359 | $data = (is_array($test_results) && isset($test_results['data'])) ? $test_results['data'] : null; |
| 360 | |
| 361 | if ((is_array($test_results) && $test_results['result']) || (!is_array($test_results) && $test_results)) { |
| 362 | _e('Success', 'updraftplus'); |
| 363 | $this->do_credentials_test_deletefile($testfile, $posted_settings); |
| 364 | } else { |
| 365 | _e("Failed: We were not able to place a file in that directory - please check your credentials.", 'updraftplus'); |
| 366 | } |
| 367 | |
| 368 | return $data; |
| 369 | |
| 370 | } |
| 371 | } |
| 372 |