PluginProbe
UpdraftPlus: WP Backup & Migration Plugin / 1.8.13
UpdraftPlus: WP Backup & Migration Plugin v1.8.13
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 / cloudfiles-new.php

cloudfiles-new.php in UpdraftPlus: WP Backup & Migration Plugin 1.8.13, at methods/cloudfiles-new.php

494 lines 20.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed.');
4
5 # SDK uses namespacing - requires PHP 5.3 (actually the SDK states its requirements as 5.3.3)
6 use OpenCloud\Rackspace;
7
8 # New SDK - https://github.com/rackspace/php-opencloud and http://docs.rackspace.com/sdks/guide/content/php.html
9 # Uploading: https://github.com/rackspace/php-opencloud/blob/master/docs/userguide/ObjectStore/Storage/Object.md
10
11 # Extends the oldsdk: only in that we re-use a few small functions
12 class UpdraftPlus_BackupModule_cloudfiles_opencloudsdk extends UpdraftPlus_BackupModule_cloudfiles_oldsdk {
13
14 const CHUNK_SIZE = 5242880;
15
16 public $client;
17
18 public function get_service($user, $apikey, $authurl, $useservercerts = false, $disablesslverify = null, $region = null) {
19
20 require_once(UPDRAFTPLUS_DIR.'/oc/autoload.php');
21
22 global $updraftplus;
23
24 # The new authentication APIs don't match the values we were storing before
25 $new_authurl = ('https://lon.auth.api.rackspacecloud.com' == $authurl || 'uk' == $authurl) ? Rackspace::UK_IDENTITY_ENDPOINT : Rackspace::US_IDENTITY_ENDPOINT;
26
27 if (null === $disablesslverify) $disablesslverify = UpdraftPlus_Options::get_updraft_option('updraft_ssl_disableverify');
28
29 $updraftplus->log("Cloud Files authentication URL: ".$new_authurl);
30
31 if (empty($user) || empty($apikey)) throw new Exception(__('Authorisation failed (check your credentials)', 'updraftplus'));
32
33 $client = new Rackspace($new_authurl, array(
34 'username' => $user,
35 'apiKey' => $apikey
36 ));
37 $this->client = $client;
38
39 if ($disablesslverify) {
40 $client->setSslVerification(false);
41 } else {
42 if ($useservercerts) {
43 $client->setConfig(array($client::SSL_CERT_AUTHORITY, 'system'));
44 } else {
45 $client->setSslVerification(UPDRAFTPLUS_DIR.'/includes/cacert.pem', true, 2);
46 }
47 }
48
49 return $client->objectStoreService('cloudFiles', $region);
50
51 }
52
53 public function backup($backup_array) {
54
55 global $updraftplus, $updraftplus_backup;
56
57 $opts = $this->get_opts();
58
59 $this->container = $opts['path'];
60
61 try {
62 $service = $this->get_service($opts['user'], $opts['apikey'], $opts['authurl'], UpdraftPlus_Options::get_updraft_option('updraft_ssl_useservercerts'), UpdraftPlus_Options::get_updraft_option('updraft_ssl_disableverify'), $opts['region']);
63 } catch(AuthenticationError $e) {
64 $updraftplus->log('Cloud Files authentication failed ('.$e->getMessage().')');
65 $updraftplus->log(__('Cloud Files authentication failed', 'updraftplus').' ('.$e->getMessage().')', 'error');
66 return false;
67 } catch (Exception $e) {
68 $updraftplus->log('Cloud Files error - failed to access the container ('.$e->getMessage().') (line: '.$e->getLine().', file: '.$e->getFile().')');
69 $updraftplus->log(__('Cloud Files error - failed to access the container', 'updraftplus').' ('.$e->getMessage().')', 'error');
70 return false;
71 }
72 # Get the container
73 try {
74 $this->container_object = $service->getContainer($this->container);
75 } catch (Exception $e) {
76 $updraftplus->log('Could not access Cloud Files container ('.get_class($e).', '.$e->getMessage().') (line: '.$e->getLine().', file: '.$e->getFile().')');
77 $updraftplus->log(__('Could not access Cloud Files container', 'updraftplus').' ('.get_class($e).', '.$e->getMessage().')', 'error');
78 return false;
79 }
80
81 foreach ($backup_array as $key => $file) {
82
83 # First, see the object's existing size (if any)
84 $uploaded_size = $this->get_remote_size($file);
85
86 try {
87 if (1 === $updraftplus->chunked_upload($this, $file, "cloudfiles://".$this->container."/$file", 'Cloud Files', UpdraftPlus_BackupModule_cloudfiles_opencloudsdk::CHUNK_SIZE, $uploaded_size)) {
88 try {
89 if (false !== ($data = fopen($updraftplus->backups_dir_location().'/'.$file, 'r+'))) {
90 $this->container_object->uploadObject($file, $data);
91 $updraftplus->log("Cloud Files regular upload: success");
92 $updraftplus->uploaded_file($file);
93 } else {
94 throw new Exception('uploadObject failed: fopen failed');
95 }
96 } catch (Exception $e) {
97 $this->log("$logname regular upload: failed ($file) (".$e->getMessage().")");
98 $this->log("$file: ".sprintf(__('%s Error: Failed to upload','updraftplus'),$logname), 'error');
99 }
100 }
101 } catch (Exception $e) {
102 $updraftplus->log(__('Cloud Files error - failed to upload file', 'updraftplus').' ('.$e->getMessage().') (line: '.$e->getLine().', file: '.$e->getFile().')');
103 $updraftplus->log(sprintf(__('%s error - failed to upload file', 'updraftplus'),'Cloud Files').' ('.$e->getMessage().')', 'error');
104 return false;
105 }
106 }
107
108 return array('cloudfiles_object' => $this->container_object, 'cloudfiles_orig_path' => $opts['path'], 'cloudfiles_container' => $this->container);
109
110 }
111
112 function get_remote_size($file) {
113 try {
114 $response = $this->container_object->getClient()->head($this->container_object->getUrl($file))->send();
115 $response_object = $this->container_object->dataObject()->populateFromResponse($response)->setName($file);
116 return $response_object->getContentLength();
117 } catch (Exception $e) {
118 # Allow caller to distinguish between zero-sized and not-found
119 return false;
120 }
121 }
122
123 function chunked_upload_finish($file) {
124
125 $chunk_path = 'chunk-do-not-delete-'.$file;
126 try {
127
128 $headers = array(
129 'Content-Length' => 0,
130 'X-Object-Manifest' => sprintf('%s/%s',
131 $this->container,
132 $chunk_path.'_'
133 )
134 );
135
136 $url = $this->container_object->getUrl($file);
137 $this->container_object->getClient()->put($url, $headers)->send();
138 return true;
139
140 } catch (Exception $e) {
141 return false;
142 }
143 }
144
145 function chunked_upload($file, $fp, $i, $upload_size, $upload_start, $upload_end) {
146
147 global $updraftplus;
148
149 $upload_remotepath = 'chunk-do-not-delete-'.$file.'_'.$i;
150
151 $remote_size = $this->get_remote_size($upload_remotepath);
152
153 // 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
154 // Didn't make the difference - instead we just check below for actual success even when Curl reports an error
155 // $chunk_object->headers = array('Expect' => '');
156
157 if ($remote_size >= $upload_size) {
158 $updraftplus->log("Cloud Files: Chunk $i ($upload_start - $upload_end): already uploaded");
159 } else {
160 $updraftplus->log("Cloud Files: Chunk $i ($upload_start - $upload_end): begin upload");
161 // Upload the chunk
162 try {
163 $data = fread($fp, $upload_size);
164 $this->container_object->uploadObject($upload_remotepath, $data);
165 } catch (Exception $e) {
166 $updraftplus->log("Cloud Files chunk upload: error: ($file / $i) (".$e->getMessage().") (line: ".$e->getLine().', file: '.$e->getFile().')');
167 // 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.
168
169 $remote_size = $this->get_remote_size($upload_remotepath);
170
171 if ($remote_size >= $upload_size) {
172 $updraftplus->log("$file: Chunk now exists; ignoring error (presuming it was an apparently known curl bug)");
173 } else {
174 $updraftplus->log("$file: ".sprintf(__('%s Error: Failed to upload','updraftplus'),'Cloud Files'), 'error');
175 return false;
176 }
177 }
178 }
179 return true;
180 }
181
182
183 public function delete($files, $data = false) {
184
185 global $updraftplus;
186 if (is_string($files)) $files = array($files);
187
188 if (is_array($data)) {
189 $container_object = $data['cloudfiles_object'];
190 $container = $data['cloudfiles_container'];
191 $path = $data['cloudfiles_orig_path'];
192 } else {
193 $opts = $this->get_opts();
194 $container = $opts['path'];
195 $path = $container;
196 try {
197 $service = $this->get_service($opts['user'], $opts['apikey'], $opts['authurl'], UpdraftPlus_Options::get_updraft_option('updraft_ssl_useservercerts'), UpdraftPlus_Options::get_updraft_option('updraft_ssl_disableverify'), $opts['region']);
198 } catch(AuthenticationError $e) {
199 $updraftplus->log('Cloud Files authentication failed ('.$e->getMessage().')');
200 $updraftplus->log(__('Cloud Files authentication failed', 'updraftplus').' ('.$e->getMessage().')', 'error');
201 return false;
202 } catch (Exception $e) {
203 $updraftplus->log('Cloud Files error - failed to access the container ('.$e->getMessage().')');
204 $updraftplus->log(__('Cloud Files error - failed to access the container', 'updraftplus').' ('.$e->getMessage().')', 'error');
205 return false;
206 }
207 # Get the container
208 try {
209 $container_object = $service->getContainer($container);
210 } catch (Exception $e) {
211 $updraftplus->log('Could not access Cloud Files container ('.get_class($e).', '.$e->getMessage().')');
212 $updraftplus->log(__('Could not access Cloud Files container', 'updraftplus').' ('.get_class($e).', '.$e->getMessage().')', 'error');
213 return false;
214 }
215
216 }
217
218 $ret = true;
219 foreach ($files as $file) {
220
221 $updraftplus->log("Cloud Files: Delete remote: container=$container, path=$file");
222
223 // We need to search for chunks
224 $chunk_path = "chunk-do-not-delete-".$file;
225
226 try {
227 $objects = $container_object->objectList(array('prefix' => $chunk_path));
228 $index = 0;
229 while (false !== ($chunk = $objects->offsetGet($index)) && !empty($chunk)) {
230 try {
231 $name = $chunk->name;
232 $container_object->dataObject()->setName($name)->delete();
233 $updraftplus->log('Cloud Files: Chunk deleted: '.$name);
234 } catch (Exception $e) {
235 $updraftplus->log("Cloud Files chunk delete failed: $name: ".$e->getMessage());
236 }
237 $index++;
238 }
239 } catch (Exception $e) {
240 $updraftplus->log('Cloud Files chunk delete failed: '.$e->getMessage());
241 }
242
243 # Finally, delete the object itself
244 try {
245 $container_object->dataObject()->setName($file)->delete();
246 $updraftplus->log('Cloud Files: Deleted: '.$file);
247 } catch (Exception $e) {
248 $updraftplus->log('Cloud Files delete failed: '.$e->getMessage());
249 $ret = false;
250 }
251 }
252 return $ret;
253 }
254
255 function download($file) {
256
257 global $updraftplus;
258
259 $opts = $this->get_opts();
260
261 try {
262 $service = $this->get_service($opts['user'], $opts['apikey'], $opts['authurl'], UpdraftPlus_Options::get_updraft_option('updraft_ssl_useservercerts'), UpdraftPlus_Options::get_updraft_option('updraft_ssl_disableverify'), $opts['region']);
263 } catch(AuthenticationError $e) {
264 $updraftplus->log('Cloud Files authentication failed ('.$e->getMessage().')');
265 $updraftplus->log(__('Cloud Files authentication failed', 'updraftplus').' ('.$e->getMessage().')', 'error');
266 return false;
267 } catch (Exception $e) {
268 $updraftplus->log('Cloud Files error - failed to access the container ('.$e->getMessage().')');
269 $updraftplus->log(__('Cloud Files error - failed to access the container', 'updraftplus').' ('.$e->getMessage().')', 'error');
270 return false;
271 }
272
273 $container = untrailingslashit($opts['path']);
274 $updraftplus->log("Cloud Files download: cloudfiles://$container/$file");
275
276 # Get the container
277 try {
278 $this->container_object = $service->getContainer($container);
279 } catch (Exception $e) {
280 $updraftplus->log('Could not access Cloud Files container ('.get_class($e).', '.$e->getMessage().')');
281 $updraftplus->log(__('Could not access Cloud Files container', 'updraftplus').' ('.get_class($e).', '.$e->getMessage().')', 'error');
282 return false;
283 }
284
285 # Get information about the object within the container
286 $remote_size = $this->get_remote_size($file);
287 if (false === $remote_size) {
288 $updraftplus->log('Could not access Cloud Files object');
289 $updraftplus->log(__('The Cloud Files object was not found', 'error'));
290 return false;
291 }
292
293 return (!is_bool($remote_size)) ? $updraftplus->chunked_download($file, $this, $remote_size, true, $this->container_object) : false;
294
295 }
296
297 public function chunked_download($file, $headers, $container_object) {
298 try {
299 $dl = $container_object->getObject($file, $headers);
300 } catch (Exception $e) {
301 global $updraftplus;
302 $updraftplus->log("$file: Failed to download (".$e->getMessage().")");
303 $updraftplus->log("$file: ".sprintf(__("%s Error",'updraftplus'), 'Cloud Files').": ".__('Error downloading remote file: Failed to download'.' ('.$e->getMessage().")",'updraftplus'), 'error');
304 return false;
305 }
306 return $dl->getContent();
307 }
308
309 public function credentials_test() {
310
311 if (empty($_POST['apikey'])) {
312 printf(__("Failure: No %s was given.",'updraftplus'),__('API key','updraftplus'));
313 die;
314 }
315
316 if (empty($_POST['user'])) {
317 printf(__("Failure: No %s was given.",'updraftplus'),__('Username','updraftplus'));
318 die;
319 }
320
321 $key = stripslashes($_POST['apikey']);
322 $user = $_POST['user'];
323 $path = $_POST['path'];
324 $authurl = $_POST['authurl'];
325 $useservercerts = $_POST['useservercerts'];
326 $disableverify = $_POST['disableverify'];
327 $region = (empty($_POST['region'])) ? null : $_POST['region'];
328
329 if (preg_match("#^([^/]+)/(.*)$#", $path, $bmatches)) {
330 $container = $bmatches[1];
331 $path = $bmatches[2];
332 } else {
333 $container = $path;
334 $path = '';
335 }
336
337 if (empty($container)) {
338 _e('Failure: No container details were given.' ,'updraftplus');
339 return;
340 }
341
342 try {
343 $method = new UpdraftPlus_BackupModule_cloudfiles_opencloudsdk;
344 $service = $method->get_service($user, $key, $authurl, $useservercerts, $disableverify, $region);
345 } catch(Guzzle\Http\Exception\ClientErrorResponseException $e) {
346 $response = $e->getResponse();
347 $code = $response->getStatusCode();
348 $reason = $response->getReasonPhrase();
349 if (401 == $code && 'Unauthorized' == $reason) {
350 echo __('Authorisation failed (check your credentials)', 'updraftplus');
351 } else {
352 echo __('Authorisation failed (check your credentials)', 'updraftplus')." ($code:$reason)";
353 }
354 die;
355 } catch(AuthenticationError $e) {
356 echo __('Cloud Files authentication failed', 'updraftplus').' ('.$e->getMessage().')';
357 die;
358 } catch (Exception $e) {
359 echo __('Cloud Files authentication failed', 'updraftplus').' ('.get_class($e).', '.$e->getMessage().')';
360 die;
361 }
362
363 try {
364 $container_object = $service->getContainer($container);
365 } catch(Guzzle\Http\Exception\ClientErrorResponseException $e) {
366 $response = $e->getResponse();
367 $code = $response->getStatusCode();
368 $reason = $response->getReasonPhrase();
369 if (404 == $code) {
370 $container_object = $service->createContainer($container);
371 } else {
372 echo __('Authorisation failed (check your credentials)', 'updraftplus')." ($code:$reason)";
373 die;
374 }
375 } catch (Exception $e) {
376 echo __('Cloud Files authentication failed', 'updraftplus').' ('.get_class($e).', '.$e->getMessage().')';
377 die;
378 }
379
380 if (!is_a($container_object, 'OpenCloud\ObjectStore\Resource\Container') && !is_a($container_object, 'Container')) {
381 echo __('Cloud Files authentication failed', 'updraftplus').' ('.get_class($container_object).')';
382 die;
383 }
384
385 $try_file = md5(rand()).'.txt';
386
387 try {
388 $object = $container_object->uploadObject($try_file, 'UpdraftPlus test file', array('content-type' => 'text/plain'));
389 } catch (Exception $e) {
390 echo __('Cloud Files error - we accessed the container, but failed to create a file within it', 'updraftplus').' ('.get_class($e).', '.$e->getMessage().')';
391 return;
392 }
393
394 echo __('Success', 'updraftplus').": ".__('We accessed the container, and were able to create files within it.', 'updraftplus');
395
396 try {
397 if (!empty($object)) @$object->delete();
398 } catch (Exception $e) {
399 }
400
401 }
402
403 public function config_print() {
404
405 $opts = $this->get_opts();
406
407 ?>
408 <tr class="updraftplusmethod cloudfiles">
409 <td></td>
410 <td><img alt="Rackspace Cloud Files" src="<?php echo UPDRAFTPLUS_URL.'/images/rackspacecloud-logo.png' ?>">
411 <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'),'Rackspace Cloud Files');?></em></p></td>
412 </tr>
413
414 <tr class="updraftplusmethod cloudfiles">
415 <th></th>
416 <td>
417 <?php
418 // Check requirements.
419 global $updraftplus_admin;
420 if (!function_exists('mb_substr')) {
421 $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');
422 }
423 $updraftplus_admin->curl_check('Rackspace Cloud Files', false, 'cloudfiles');
424 ?>
425 </td>
426 </tr>
427
428 <tr class="updraftplusmethod cloudfiles">
429 <th></th>
430 <td>
431 <p><?php _e('Get your API key <a href="https://mycloud.rackspace.com/">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">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="http://updraftplus.com/faqs/there-appear-to-be-lots-of-extra-files-in-my-rackspace-cloud-files-container/"><?php _e('Also, you should read this important FAQ.', 'updraftplus'); ?></a></p>
432 </td>
433 </tr>
434 <tr class="updraftplusmethod cloudfiles">
435 <th title="<?php _e('Accounts created at rackspacecloud.com are US accounts; accounts created at rackspace.co.uk are UK accounts.', 'updraftplus');?>"><?php _e('US or UK-based Rackspace Account','updraftplus');?>:</th>
436 <td>
437 <select id="updraft_cloudfiles_authurl" name="updraft_cloudfiles[authurl]" title="<?php _e('Accounts created at rackspacecloud.com are US-accounts; accounts created at rackspace.co.uk are UK-based', 'updraftplus');?>">
438 <option <?php if ($opts['authurl'] != 'https://lon.auth.api.rackspacecloud.com') echo 'selected="selected"'; ?> value="https://auth.api.rackspacecloud.com"><?php _e('US (default)','updraftplus'); ?></option>
439 <option <?php if ($opts['authurl'] =='https://lon.auth.api.rackspacecloud.com') echo 'selected="selected"'; ?> value="https://lon.auth.api.rackspacecloud.com"><?php _e('UK', 'updraftplus'); ?></option>
440 </select>
441 </td>
442 </tr>
443
444 <tr class="updraftplusmethod cloudfiles">
445 <th><?php _e('Cloud Files Storage Region','updraftplus');?>:</th>
446 <td>
447 <select id="updraft_cloudfiles_region" name="updraft_cloudfiles[region]">
448 <?php
449 $regions = array(
450 'DFW' => __('Dallas (DFW) (default)', 'updraftplus'),
451 'SYD' => __('Sydney (SYD)', 'updraftplus'),
452 'ORD' => __('Chicago (ORD)', 'updraftplus'),
453 'IAD' => __('Northern Virginia (IAD)', 'updraftplus'),
454 'HKG' => __('Hong Kong (HKG)', 'updraftplus'),
455 'LON' => __('London (LON)', 'updraftplus')
456 );
457 $selregion = (empty($opts['region'])) ? 'DFW' : $opts['region'];
458 foreach ($regions as $reg => $desc) {
459 ?>
460 <option <?php if ($selregion == $reg) echo 'selected="selected"'; ?> value="<?php echo $reg;?>"><?php echo htmlspecialchars($desc); ?></option>
461 <?php
462 }
463 ?>
464 </select>
465 </td>
466 </tr>
467
468 <tr class="updraftplusmethod cloudfiles">
469 <th><?php _e('Cloud Files Username','updraftplus');?>:</th>
470 <td><input type="text" autocomplete="off" style="width: 282px" id="updraft_cloudfiles_user" name="updraft_cloudfiles[user]" value="<?php echo htmlspecialchars($opts['user']) ?>" />
471 <div style="clear:both;">
472 <?php echo apply_filters('updraft_cloudfiles_apikeysetting', '<a href="http://updraftplus.com/shop/cloudfiles-enhanced/"><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>'; ?>
473 </div>
474 </td>
475 </tr>
476 <tr class="updraftplusmethod cloudfiles">
477 <th><?php _e('Cloud Files API Key','updraftplus');?>:</th>
478 <td><input type="<?php echo apply_filters('updraftplus_admin_secret_field_type', 'text'); ?>" autocomplete="off" style="width: 282px" id="updraft_cloudfiles_apikey" name="updraft_cloudfiles[apikey]" value="<?php echo htmlspecialchars($opts['apikey']); ?>" />
479 </td>
480 </tr>
481 <tr class="updraftplusmethod cloudfiles">
482 <th><?php echo apply_filters('updraftplus_cloudfiles_location_description',__('Cloud Files Container','updraftplus'));?>:</th>
483 <td><input type="text" style="width: 282px" name="updraft_cloudfiles[path]" id="updraft_cloudfiles_path" value="<?php echo htmlspecialchars($opts['path']); ?>" /></td>
484 </tr>
485
486 <tr class="updraftplusmethod cloudfiles">
487 <th></th>
488 <td><p><button id="updraft-cloudfiles-test" type="button" class="button-primary" style="font-size:18px !important"><?php echo sprintf(__('Test %s Settings','updraftplus'),'Cloud Files');?></button></p></td>
489 </tr>
490 <?php
491 }
492
493 }
494