PluginProbe
UpdraftPlus: WP Backup & Migration Plugin / 1.12.4
UpdraftPlus: WP Backup & Migration Plugin v1.12.4
1.26.7 1.26.6 1.26.5 1.26.4 1.26.3 1.9.19 1.9.25 1.9.26 1.9.30 1.9.31 1.9.32 1.9.4 1.9.40 1.9.41 1.9.42 1.9.43 1.9.44 1.9.45 1.9.46 1.9.5 1.9.50 1.9.51 1.9.60 1.9.62 1.9.63 All 371 releases
updraftplus / methods / openstack-base.php

openstack-base.php in UpdraftPlus: WP Backup & Migration Plugin 1.12.4, at methods/openstack-base.php

446 lines 17.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 class UpdraftPlus_BackupModule_openstack_base {
4
5 protected $chunk_size = 5242880;
6
7 protected $client;
8 protected $method;
9 protected $desc;
10 protected $long_desc;
11 protected $img_url;
12
13 public function __construct($method, $desc, $long_desc = null, $img_url = '') {
14 $this->method = $method;
15 $this->desc = $desc;
16 $this->long_desc = (is_string($long_desc)) ? $long_desc : $desc;
17 $this->img_url = $img_url;
18 }
19
20 public function backup($backup_array) {
21
22 global $updraftplus, $updraftplus_backup;
23
24 $opts = $this->get_opts();
25
26 $this->container = $opts['path'];
27
28 try {
29 $service = $this->get_service($opts, UpdraftPlus_Options::get_updraft_option('updraft_ssl_useservercerts'), UpdraftPlus_Options::get_updraft_option('updraft_ssl_disableverify'));
30 } catch(AuthenticationError $e) {
31 $updraftplus->log($this->desc.' authentication failed ('.$e->getMessage().')');
32 $updraftplus->log(sprintf(__('%s authentication failed', 'updraftplus'), $this->desc).' ('.$e->getMessage().')', 'error');
33 return false;
34 } catch (Exception $e) {
35 $updraftplus->log($this->desc.' error - failed to access the container ('.$e->getMessage().') (line: '.$e->getLine().', file: '.$e->getFile().')');
36 $updraftplus->log(sprintf(__('%s error - failed to access the container', 'updraftplus'), $this->desc).' ('.$e->getMessage().')', 'error');
37 return false;
38 }
39 # Get the container
40 try {
41 $this->container_object = $service->getContainer($this->container);
42 } catch (Exception $e) {
43 $updraftplus->log('Could not access '.$this->desc.' container ('.get_class($e).', '.$e->getMessage().') (line: '.$e->getLine().', file: '.$e->getFile().')');
44 $updraftplus->log(sprintf(__('Could not access %s container', 'updraftplus'), $this->desc).' ('.get_class($e).', '.$e->getMessage().')', 'error');
45 return false;
46 }
47
48 foreach ($backup_array as $key => $file) {
49
50 # First, see the object's existing size (if any)
51 $uploaded_size = $this->get_remote_size($file);
52
53 try {
54 if (1 === $updraftplus->chunked_upload($this, $file, $this->method."://".$this->container."/$file", $this->desc, $this->chunk_size, $uploaded_size)) {
55 try {
56 if (false !== ($data = fopen($updraftplus->backups_dir_location().'/'.$file, 'r+'))) {
57 $this->container_object->uploadObject($file, $data);
58 $updraftplus->log($this->desc." regular upload: success");
59 $updraftplus->uploaded_file($file);
60 } else {
61 throw new Exception('uploadObject failed: fopen failed');
62 }
63 } catch (Exception $e) {
64 $this->log("$logname regular upload: failed ($file) (".$e->getMessage().")");
65 $this->log("$file: ".sprintf(__('%s Error: Failed to upload','updraftplus'),$logname), 'error');
66 }
67 }
68 } catch (Exception $e) {
69 $updraftplus->log($this->desc.' error - failed to upload file'.' ('.$e->getMessage().') (line: '.$e->getLine().', file: '.$e->getFile().')');
70 $updraftplus->log(sprintf(__('%s error - failed to upload file', 'updraftplus'), $this->desc).' ('.$e->getMessage().')', 'error');
71 return false;
72 }
73 }
74
75 return array('object' => $this->container_object, 'orig_path' => $opts['path'], 'container' => $this->container);
76
77 }
78
79 private function get_remote_size($file) {
80 try {
81 $response = $this->container_object->getClient()->head($this->container_object->getUrl($file))->send();
82 $response_object = $this->container_object->dataObject()->populateFromResponse($response)->setName($file);
83 return $response_object->getContentLength();
84 } catch (Exception $e) {
85 # Allow caller to distinguish between zero-sized and not-found
86 return false;
87 }
88 }
89
90 public function listfiles($match = 'backup_') {
91 $opts = $this->get_opts();
92 $container = $opts['path'];
93 $path = $container;
94
95 if (empty($opts['user']) || (empty($opts['apikey']) && empty($opts['password']))) return new WP_Error('no_settings', __('No settings were found','updraftplus'));
96
97 try {
98 $service = $this->get_service($opts, UpdraftPlus_Options::get_updraft_option('updraft_ssl_useservercerts'), UpdraftPlus_Options::get_updraft_option('updraft_ssl_disableverify'));
99 } catch (Exception $e) {
100 return new WP_Error('no_access', sprintf(__('%s error - failed to access the container', 'updraftplus'), $this->desc).' ('.$e->getMessage().')');
101 }
102
103 # Get the container
104 try {
105 $this->container_object = $service->getContainer($container);
106 } catch (Exception $e) {
107 return new WP_Error('no_access', sprintf(__('%s error - failed to access the container', 'updraftplus'), $this->desc).' ('.$e->getMessage().')');
108 }
109
110 $results = array();
111 try {
112 $objects = $this->container_object->objectList(array('prefix' => $match));
113 $index = 0;
114 while (false !== ($file = $objects->offsetGet($index)) && !empty($file)) {
115 try {
116 if ((is_object($file) && !empty($file->name))) {
117 $result = array('name' => $file->name);
118 # Rackspace returns the size of a manifested file properly; other OpenStack implementations may not
119 if (!empty($file->bytes)) {
120 $result['size'] = $file->bytes;
121 } else {
122 $size = $this->get_remote_size($file->name);
123 if (false !== $size && $size > 0) $result['size'] = $size;
124 }
125 $results[] = $result;
126 }
127 } catch (Exception $e) {
128 }
129 $index++;
130 }
131 } catch (Exception $e) {
132 }
133
134 return $results;
135 }
136
137 public function chunked_upload_finish($file) {
138
139 $chunk_path = 'chunk-do-not-delete-'.$file;
140 try {
141
142 $headers = array(
143 'Content-Length' => 0,
144 'X-Object-Manifest' => sprintf('%s/%s',
145 $this->container,
146 $chunk_path.'_'
147 )
148 );
149
150 $url = $this->container_object->getUrl($file);
151 $this->container_object->getClient()->put($url, $headers)->send();
152 return true;
153
154 } catch (Exception $e) {
155 global $updraftplus;
156 $updraftplus->log("Error when sending manifest (".get_class($e)."): ".$e->getMessage());
157 return false;
158 }
159 }
160
161 public function chunked_upload($file, $fp, $i, $upload_size, $upload_start, $upload_end) {
162
163 global $updraftplus;
164
165 $upload_remotepath = 'chunk-do-not-delete-'.$file.'_'.$i;
166
167 $remote_size = $this->get_remote_size($upload_remotepath);
168
169 // 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
170 // Didn't make the difference - instead we just check below for actual success even when Curl reports an error
171 // $chunk_object->headers = array('Expect' => '');
172
173 if ($remote_size >= $upload_size) {
174 $updraftplus->log($this->desc.": Chunk $i ($upload_start - $upload_end): already uploaded");
175 } else {
176 $updraftplus->log($this->desc.": Chunk $i ($upload_start - $upload_end): begin upload");
177 // Upload the chunk
178 try {
179 $data = fread($fp, $upload_size);
180 $this->container_object->uploadObject($upload_remotepath, $data);
181 } catch (Exception $e) {
182 $updraftplus->log($this->desc." chunk upload: error: ($file / $i) (".$e->getMessage().") (line: ".$e->getLine().', file: '.$e->getFile().')');
183 // 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.
184
185 $remote_size = $this->get_remote_size($upload_remotepath);
186
187 if ($remote_size >= $upload_size) {
188 $updraftplus->log("$file: Chunk now exists; ignoring error (presuming it was an apparently known curl bug)");
189 } else {
190 $updraftplus->log("$file: ".sprintf(__('%s Error: Failed to upload','updraftplus'), $this->desc), 'error');
191 return false;
192 }
193 }
194 }
195 return true;
196 }
197
198
199 public function delete($files, $data = false, $sizeinfo = array()) {
200
201 global $updraftplus;
202 if (is_string($files)) $files = array($files);
203
204 if (is_array($data)) {
205 $container_object = $data['object'];
206 $container = $data['container'];
207 $path = $data['orig_path'];
208 } else {
209 $opts = $this->get_opts();
210 $container = $opts['path'];
211 $path = $container;
212 try {
213 $service = $this->get_service($opts, UpdraftPlus_Options::get_updraft_option('updraft_ssl_useservercerts'), UpdraftPlus_Options::get_updraft_option('updraft_ssl_disableverify'));
214 } catch(AuthenticationError $e) {
215 $updraftplus->log($this->desc.' authentication failed ('.$e->getMessage().')');
216 $updraftplus->log(sprintf(__('%s authentication failed', 'updraftplus'), $this->desc).' ('.$e->getMessage().')', 'error');
217 return false;
218 } catch (Exception $e) {
219 $updraftplus->log($this->desc.' error - failed to access the container ('.$e->getMessage().')');
220 $updraftplus->log(sprintf(__('%s error - failed to access the container', 'updraftplus'), $this->desc).' ('.$e->getMessage().')', 'error');
221 return false;
222 }
223 # Get the container
224 try {
225 $container_object = $service->getContainer($container);
226 } catch (Exception $e) {
227 $updraftplus->log('Could not access '.$this->desc.' container ('.get_class($e).', '.$e->getMessage().')');
228 $updraftplus->log(sprintf(__('Could not access %s container', 'updraftplus'), $this->desc).' ('.get_class($e).', '.$e->getMessage().')', 'error');
229 return false;
230 }
231
232 }
233
234 $ret = true;
235 foreach ($files as $file) {
236
237 $updraftplus->log($this->desc.": Delete remote: container=$container, path=$file");
238
239 // We need to search for chunks
240 $chunk_path = "chunk-do-not-delete-".$file;
241
242 try {
243 $objects = $container_object->objectList(array('prefix' => $chunk_path));
244 $index = 0;
245 while (false !== ($chunk = $objects->offsetGet($index)) && !empty($chunk)) {
246 try {
247 $name = $chunk->name;
248 $container_object->dataObject()->setName($name)->delete();
249 $updraftplus->log($this->desc.': Chunk deleted: '.$name);
250 } catch (Exception $e) {
251 $updraftplus->log($this->desc." chunk delete failed: $name: ".$e->getMessage());
252 }
253 $index++;
254 }
255 } catch (Exception $e) {
256 $updraftplus->log($this->desc.' chunk delete failed: '.$e->getMessage());
257 }
258
259 # Finally, delete the object itself
260 try {
261 $container_object->dataObject()->setName($file)->delete();
262 $updraftplus->log($this->desc.': Deleted: '.$file);
263 } catch (Exception $e) {
264 $updraftplus->log($this->desc.' delete failed: '.$e->getMessage());
265 $ret = false;
266 }
267 }
268 return $ret;
269 }
270
271 public function download($file) {
272
273 global $updraftplus;
274
275 $opts = $this->get_opts();
276
277 try {
278 $service = $this->get_service($opts, UpdraftPlus_Options::get_updraft_option('updraft_ssl_useservercerts'), UpdraftPlus_Options::get_updraft_option('updraft_ssl_disableverify'));
279 } catch(AuthenticationError $e) {
280 $updraftplus->log($this->desc.' authentication failed ('.$e->getMessage().')');
281 $updraftplus->log(sprintf(__('%s authentication failed', 'updraftplus'), $this->desc).' ('.$e->getMessage().')', 'error');
282 return false;
283 } catch (Exception $e) {
284 $updraftplus->log($this->desc.' error - failed to access the container ('.$e->getMessage().')');
285 $updraftplus->log(sprintf(__('%s error - failed to access the container', 'updraftplus'), $this->desc).' ('.$e->getMessage().')', 'error');
286 return false;
287 }
288
289 $container = untrailingslashit($opts['path']);
290 $updraftplus->log($this->desc." download: ".$this->method."://$container/$file");
291
292 # Get the container
293 try {
294 $this->container_object = $service->getContainer($container);
295 } catch (Exception $e) {
296 $updraftplus->log('Could not access '.$this->desc.' container ('.get_class($e).', '.$e->getMessage().')');
297 $updraftplus->log(sprintf(__('Could not access %s container', 'updraftplus'), $this->desc).' ('.get_class($e).', '.$e->getMessage().')', 'error');
298 return false;
299 }
300
301 # Get information about the object within the container
302 $remote_size = $this->get_remote_size($file);
303 if (false === $remote_size) {
304 $updraftplus->log('Could not access '.$this->desc.' object');
305 $updraftplus->log(sprintf(__('The %s object was not found', 'updraftplus'), $this->desc), 'error');
306 return false;
307 }
308
309 return (!is_bool($remote_size)) ? $updraftplus->chunked_download($file, $this, $remote_size, true, $this->container_object) : false;
310
311 }
312
313 public function chunked_download($file, $headers, $container_object) {
314 try {
315 $dl = $container_object->getObject($file, $headers);
316 } catch (Exception $e) {
317 global $updraftplus;
318 $updraftplus->log("$file: Failed to download (".$e->getMessage().")");
319 $updraftplus->log("$file: ".sprintf(__("%s Error",'updraftplus'), $this->desc).": ".__('Error downloading remote file: Failed to download'.' ('.$e->getMessage().")",'updraftplus'), 'error');
320 return false;
321 }
322 return $dl->getContent();
323 }
324
325 public function credentials_test_go($opts, $path, $useservercerts, $disableverify) {
326
327 if (preg_match("#^([^/]+)/(.*)$#", $path, $bmatches)) {
328 $container = $bmatches[1];
329 $path = $bmatches[2];
330 } else {
331 $container = $path;
332 $path = '';
333 }
334
335 if (empty($container)) {
336 _e('Failure: No container details were given.' ,'updraftplus');
337 return;
338 }
339
340 try {
341 $service = $this->get_service($opts, $useservercerts, $disableverify);
342 } catch(Guzzle\Http\Exception\ClientErrorResponseException $e) {
343 $response = $e->getResponse();
344 $code = $response->getStatusCode();
345 $reason = $response->getReasonPhrase();
346 if (401 == $code && 'Unauthorized' == $reason) {
347 echo __('Authorisation failed (check your credentials)', 'updraftplus');
348 } else {
349 echo __('Authorisation failed (check your credentials)', 'updraftplus')." ($code:$reason)";
350 }
351 return;
352 } catch(AuthenticationError $e) {
353 echo sprintf(__('%s authentication failed', 'updraftplus'), $this->desc).' ('.$e->getMessage().')';
354 return;
355 } catch (Exception $e) {
356 echo sprintf(__('%s authentication failed', 'updraftplus'), $this->desc).' ('.get_class($e).', '.$e->getMessage().')';
357 return;
358 }
359
360 try {
361 $container_object = $service->getContainer($container);
362 } catch(Guzzle\Http\Exception\ClientErrorResponseException $e) {
363 $response = $e->getResponse();
364 $code = $response->getStatusCode();
365 $reason = $response->getReasonPhrase();
366 if (404 == $code) {
367 $container_object = $service->createContainer($container);
368 } else {
369 echo __('Authorisation failed (check your credentials)', 'updraftplus')." ($code:$reason)";
370 return;
371 }
372 } catch (Exception $e) {
373 echo sprintf(__('%s authentication failed', 'updraftplus'), $this->desc).' ('.get_class($e).', '.$e->getMessage().')';
374 return;
375 }
376
377 if (!is_a($container_object, 'OpenCloud\ObjectStore\Resource\Container') && !is_a($container_object, 'Container')) {
378 echo sprintf(__('%s authentication failed', 'updraftplus'), $this->desc).' ('.get_class($container_object).')';
379 return;
380 }
381
382 $try_file = md5(rand()).'.txt';
383
384 try {
385 $object = $container_object->uploadObject($try_file, 'UpdraftPlus test file', array('content-type' => 'text/plain'));
386 } catch (Exception $e) {
387 echo sprintf(__('%s error - we accessed the container, but failed to create a file within it', 'updraftplus'), $this->desc).' ('.get_class($e).', '.$e->getMessage().')';
388 if (!empty($this->region)) echo ' '.sprintf(__('Region: %s', 'updraftplus'), $this->region);
389 return;
390 }
391
392 echo __('Success', 'updraftplus').": ".__('We accessed the container, and were able to create files within it.', 'updraftplus');
393 if (!empty($this->region)) echo ' '.sprintf(__('Region: %s', 'updraftplus'), $this->region);
394
395 try {
396 if (!empty($object)) {
397 # One OpenStack server we tested on did not delete unless we slept... some kind of race condition at their end
398 sleep(1);
399 $object->delete();
400 }
401 } catch (Exception $e) {
402 }
403
404 }
405
406 public function config_print_middlesection() {
407 }
408
409 public function config_print() {
410
411 ?>
412 <tr class="updraftplusmethod <?php echo $this->method;?>">
413 <td></td>
414 <td>
415 <?php
416 if (!empty($this->img_url)) { ?>
417 <img alt="<?php echo $this->long_desc;?>" src="<?php echo UPDRAFTPLUS_URL.$this->img_url ?>">
418 <?php } ?>
419 <p><em><?php printf(__('%s is a great choice, because UpdraftPlus supports chunked uploads - no matter how big your site is, UpdraftPlus can upload it a little at a time, and not get thwarted by timeouts.','updraftplus'),$this->long_desc);?></em></p></td>
420 </tr>
421
422 <tr class="updraftplusmethod <?php echo $this->method;?>">
423 <th></th>
424 <td>
425 <?php
426 // Check requirements.
427 global $updraftplus_admin;
428 if (!function_exists('mb_substr')) {
429 $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);
430 }
431 $updraftplus_admin->curl_check($this->long_desc, false, $this->method);
432 ?>
433 </td>
434 </tr>
435
436 <?php $this->config_print_middlesection(); ?>
437
438 <tr class="updraftplusmethod <?php echo $this->method;?>">
439 <th></th>
440 <td><p><button id="updraft-<?php echo $this->method;?>-test" type="button" data-method="<?php echo $this->method;?>" class="button-primary updraft-test-button" data-method_label="<?php esc_attr_e($this->desc);?>"><?php echo sprintf(__('Test %s Settings','updraftplus'), $this->desc);?></button></p></td>
441 </tr>
442 <?php
443 }
444
445 }
446