PluginProbe
UpdraftPlus: WP Backup & Migration Plugin / 1.16.5
UpdraftPlus: WP Backup & Migration Plugin v1.16.5
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 / includes / S3compat.php

S3compat.php in UpdraftPlus: WP Backup & Migration Plugin 1.16.5, at includes/S3compat.php

813 lines 26.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 // @codingStandardsIgnoreStart
3 // This is a compatibility library, using Amazon's official PHP SDK (PHP 5.3.3+), but providing the methods of Donovan Schönknecht's S3.php library (which we used to always use) - but we've only cared about making code-paths in UpdraftPlus work, so be careful if re-deploying this in another project. And, we have a few bits of UpdraftPlus-specific code below, for logging.
4
5 /**
6 *
7 * Copyright (c) 2012-9, David Anderson (https://www.simbahosting.co.uk). All rights reserved.
8 * Portions copyright (c) 2011, Donovan Schönknecht. All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions are met:
12 *
13 * - Redistributions of source code must retain the above copyright notice,
14 * this list of conditions and the following disclaimer.
15 * - Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 *
31 * Amazon S3 is a trademark of Amazon.com, Inc. or its affiliates.
32 */
33 // @codingStandardsIgnoreEnd
34
35 require_once(UPDRAFTPLUS_DIR.'/vendor/autoload.php');
36
37 // SDK uses namespacing - requires PHP 5.3 (actually the SDK states its requirements as 5.3.3)
38 use Aws\S3;
39
40 /**
41 * Amazon S3 PHP class
42 * http://undesigned.org.za/2007/10/22/amazon-s3-php-cla
43 *
44 * @version Release: 0.5.0-dev
45 */
46 class UpdraftPlus_S3_Compat {
47
48 // ACL flags
49 const ACL_PRIVATE = 'private';
50 const ACL_PUBLIC_READ = 'public-read';
51 const ACL_PUBLIC_READ_WRITE = 'public-read-write';
52 const ACL_AUTHENTICATED_READ = 'authenticated-read';
53
54 const STORAGE_CLASS_STANDARD = 'STANDARD';
55
56 private $config = array('scheme' => 'https', 'service' => 's3');
57
58 private $__access_key = null; // AWS Access key
59
60 private $__secret_key = null; // AWS Secret key
61
62 private $__session_token = null;
63
64 private $__ssl_key = null;
65
66 public $endpoint = 's3.amazonaws.com';
67
68 public $proxy = null;
69
70 private $region = 'us-east-1';
71
72 // Added to cope with a particular situation where the user had no pernmission to check the bucket location, which necessitated using DNS-based endpoints.
73 public $use_dns_bucket_name = false;
74
75 public $use_ssl = false;
76
77 public $use_ssl_validation = true;
78
79 public $use_exceptions = false;
80
81 private $_server_side_encryption = null;
82
83 // SSL CURL SSL options - only needed if you are experiencing problems with your OpenSSL configuration
84 public $ssl_key = null;
85
86 public $ssl_cert = null;
87
88 public $ssl_ca_cert = null;
89
90 // Added at request of a user using a non-default port.
91 public static $port = false;
92
93 /**
94 * Constructor - if you're not using the class statically
95 *
96 * @param string $access_key Access key
97 * @param string $secret_key Secret key
98 * @param boolean $use_ssl Enable SSL
99 * @param string|boolean $ssl_ca_cert Certificate authority (true = bundled Guzzle version; false = no verify, 'system' = system version; otherwise, path)
100 * @param Null|String $endpoint Endpoint (if omitted, it will be set by the SDK using the region)
101 * @param Null|String $session_token The session token returned by AWS for temporary credentials access
102 * @param Null|String $region Region. Currently unused, but harmonised with UpdraftPlus_S3 class
103 * @return void
104 */
105 public function __construct($access_key = null, $secret_key = null, $use_ssl = true, $ssl_ca_cert = true, $endpoint = null, $session_token = null, $region = null) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
106 if (null !== $access_key && null !== $secret_key)
107 $this->setAuth($access_key, $secret_key, $session_token);
108
109 $this->use_ssl = $use_ssl;
110 $this->ssl_ca_cert = $ssl_ca_cert;
111
112 $opts = array(
113 'key' => $access_key,
114 'secret' => $secret_key,
115 'scheme' => ($use_ssl) ? 'https' : 'http',
116 // Using signature v4 requires a region (but see the note below)
117 // 'signature' => 'v4',
118 // 'region' => $this->region
119 // 'endpoint' => 'somethingorother.s3.amazonaws.com'
120 );
121
122 if ($endpoint) {
123 // Can't specify signature v4, as that requires stating the region - which we don't necessarily yet know.
124 // Later comment: however, it looks to me like in current UD (Sep 2017), $endpoint is never used for Amazon S3/Vault, and there may be cases (e.g. DigitalOcean Spaces) where we might prefer v4 (DO support v2 too, currently) without knowing a region.
125 $this->endpoint = $endpoint;
126 $opts['endpoint'] = $endpoint;
127 } else {
128 // Using signature v4 requires a region. Also, some regions (EU Central 1, China) require signature v4 - and all support it, so we may as well use it if we can.
129 $opts['signature'] = 'v4';
130 $opts['region'] = $this->region;
131 }
132
133 if ($session_token) {
134 $opts['token'] = $session_token;
135 }
136
137 if ($use_ssl) $opts['ssl.certificate_authority'] = $ssl_ca_cert;
138
139 $this->client = Aws\S3\S3Client::factory($opts);
140 }
141
142 /**
143 * Set AWS access key and secret key
144 *
145 * @param string $access_key Access key
146 * @param string $secret_key Secret key
147 * @param null|string $session_token The session token returned by AWS for temporary credentials access
148 * @return void
149 */
150 public function setAuth($access_key, $secret_key, $session_token = null) {
151 $this->__access_key = $access_key;
152 $this->__secret_key = $secret_key;
153 $this->__session_token = $session_token;
154 }
155
156 /**
157 * Example value: 'AES256'. See: https://docs.aws.amazon.com/AmazonS3/latest/dev/SSEUsingPHPSDK.html
158 * Or, false to turn off.
159 *
160 * @param boolean $value Set if Value
161 */
162 public function setServerSideEncryption($value) {
163 $this->_serverSideEncryption = $value;
164 }
165
166 /**
167 * Set the service region
168 *
169 * @param string $region Region
170 * @return void
171 */
172 public function setRegion($region) {
173 $this->region = $region;
174 if ('eu-central-1' == $region || 'cn-north-1' == $region) {
175 // $this->config['signature'] = new Aws\S3\S3SignatureV4('s3');
176 // $this->client->setConfig($this->config);
177 }
178 $this->client->setRegion($region);
179 }
180
181 /**
182 * Set the service endpoint
183 *
184 * @param string $host Hostname
185 * @param string $region Region
186 * @return void
187 */
188 public function setEndpoint($host, $region) {
189 $this->endpoint = $host;
190 $this->region = $region;
191 $this->config['endpoint_provider'] = $this->return_provider();
192 $this->client->setConfig($this->config);
193 }
194
195 /**
196 * Set the service port
197 *
198 * @param Integer $port Port number
199 */
200 public function setPort($port) {
201 // Not used with AWS (which is the only thing using this class)
202 self::$port = $port;
203 }
204
205 public function return_provider() {
206 $our_endpoints = array(
207 'endpoint' => $this->endpoint
208 );
209 if ('eu-central-1' == $this->region || 'cn-north-1' == $this->region) $our_endpoints['signatureVersion'] = 'v4';
210 $endpoints = array(
211 'version' => 2,
212 'endpoints' => array(
213 "*/s3" => $our_endpoints
214 )
215 );
216 return new Aws\Common\RulesEndpointProvider($endpoints);
217 }
218
219 /**
220 * Set SSL on or off
221 * This code relies upon the particular pattern of SSL options-setting in s3.php in UpdraftPlus
222 *
223 * @param boolean $enabled SSL enabled
224 * @param boolean $validate SSL certificate validation
225 * @return void
226 */
227 public function setSSL($enabled, $validate = true) {
228 $this->use_ssl = $enabled;
229 $this->use_ssl_validation = $validate;
230 // http://guzzle.readthedocs.org/en/latest/clients.html#verify
231 if ($enabled) {
232
233 // Do nothing - in UpdraftPlus, setSSLAuth will be called later, and we do the calls there
234
235 // $verify_peer = ($validate) ? true : false;
236 // $verify_host = ($validate) ? 2 : 0;
237 //
238 // $this->config['scheme'] = 'https';
239 // $this->client->setConfig($this->config);
240 //
241 // $this->client->setSslVerification($validate, $verify_peer, $verify_host);
242
243
244 } else {
245 $this->config['scheme'] = 'http';
246 // $this->client->setConfig($this->config);
247 }
248 $this->client->setConfig($this->config);
249 }
250
251 public function getuseSSL() {
252 return $this->use_ssl;
253 }
254
255 /**
256 * Set SSL client certificates (experimental)
257 *
258 * @param string $ssl_cert SSL client certificate
259 * @param string $ssl_key SSL client key
260 * @param string $ssl_ca_cert SSL CA cert (only required if you are having problems with your system CA cert)
261 * @return void
262 */
263 public function setSSLAuth($ssl_cert = null, $ssl_key = null, $ssl_ca_cert = null) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
264
265 if (!$this->use_ssl) return;
266
267 if (!$this->use_ssl_validation) {
268 $this->client->setSslVerification(false);
269 } else {
270 if (!$ssl_ca_cert) {
271 $client = $this->client;
272 // "Static class properties and methods, as well as class constants, could not be accessed using a dynamic (variable) classname in PHP 5.2 or earlier." But the present file is not loaded in PHP 5.2.
273 // @codingStandardsIgnoreLine
274 $this->config[$client::SSL_CERT_AUTHORITY] = false;
275 $this->client->setConfig($this->config);
276 } else {
277 $this->client->setSslVerification(realpath($ssl_ca_cert), true, 2);
278 }
279 }
280
281 // $this->client->setSslVerification($ssl_ca_cert, $verify_peer, $verify_host);
282 // $this->config['ssl.certificate_authority'] = $ssl_ca_cert;
283 // $this->client->setConfig($this->config);
284 }
285
286 /**
287 * Set proxy information
288 *
289 * @param string $host Proxy hostname and port (localhost:1234)
290 * @param string $user Proxy username
291 * @param string $pass Proxy password
292 * @param constant $type CURL proxy type
293 * @param integer $port Port number
294 * @return void
295 */
296 public function setProxy($host, $user = null, $pass = null, $type = CURLPROXY_SOCKS5, $port = null) {
297
298 $this->proxy = array('host' => $host, 'type' => $type, 'user' => $user, 'pass' => $pass, 'port' => $port);
299
300 if (!$host) return;
301
302 $wp_proxy = new WP_HTTP_Proxy();
303 if ($wp_proxy->send_through_proxy('https://s3.amazonaws.com')) {
304
305 global $updraftplus;
306 $updraftplus->log("setProxy: host=$host, user=$user, port=$port");
307
308 // N.B. Currently (02-Feb-15), only support for HTTP proxies has ever been requested for S3 in UpdraftPlus
309 $proxy_url = 'http://';
310 if ($user) {
311 $proxy_url .= $user;
312 if ($pass) $proxy_url .= ":$pass";
313 $proxy_url .= "@";
314 }
315
316 $proxy_url .= $host;
317
318 if ($port) $proxy_url .= ":$port";
319
320 $this->client->setDefaultOption('proxy', $proxy_url);
321 }
322
323 }
324
325 /**
326 * Set the error mode to exceptions
327 *
328 * @param boolean $enabled Enable exceptions
329 * @return void
330 */
331 public function setExceptions($enabled = true) {
332 $this->useExceptions = $enabled;
333 }
334
335 /**
336 * A no-op in this compatibility layer (for now - not yet found a use)...
337 *
338 * @param boolean $use Bucket use
339 * @param string $bucket Bucket name
340 * @return boolean
341 */
342 public function useDNSBucketName($use = true, $bucket = '') {
343 $this->use_dns_bucket_name = $use;
344 if ($use && $bucket) {
345 $this->setEndpoint($bucket.'.s3.amazonaws.com', $this->region);
346 }
347 return true;
348 }
349
350 /**
351 * Get contents for a bucket
352 * If max_keys is null this method will loop through truncated result sets
353 * N.B. UpdraftPlus does not use the $delimiter or $return_common_prefixes parameters (nor set $prefix or $marker to anything other than null)
354 * $return_common_prefixes is not implemented below
355 *
356 * @param string $bucket Bucket name
357 * @param string $prefix Prefix
358 * @param string $marker Marker (last file listed)
359 * @param string $max_keys Max keys (maximum number of keys to return)
360 * @param string $delimiter Delimiter
361 * @param boolean $return_common_prefixes Set to true to return CommonPrefixes
362 * @return array
363 */
364 public function getBucket($bucket, $prefix = null, $marker = null, $max_keys = null, $delimiter = null, $return_common_prefixes = false) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
365 try {
366 if (0 == $max_keys) $max_keys = null;
367
368 $vars = array('Bucket' => $bucket);
369 if (null !== $prefix && '' !== $prefix) $vars['Prefix'] = $prefix;
370 if (null !== $marker && '' !== $marker) $vars['Marker'] = $marker;
371 if (null !== $max_keys && '' !== $max_keys) $vars['MaxKeys'] = $max_keys;
372 if (null !== $delimiter && '' !== $delimiter) $vars['Delimiter'] = $delimiter;
373 $result = $this->client->listObjects($vars);
374
375 if (!is_a($result, 'Guzzle\Service\Resource\Model')) {
376 return false;
377 }
378
379 $results = array();
380 $next_marker = null;
381 // http://docs.aws.amazon.com/AmazonS3/latest/dev/ListingObjectKeysUsingPHP.html
382 // UpdraftPlus does not use the 'hash' result
383 if (empty($result['Contents'])) $result['Contents'] = array();
384 foreach ($result['Contents'] as $c) {
385 $results[(string) $c['Key']] = array(
386 'name' => (string) $c['Key'],
387 'time' => strtotime((string) $c['LastModified']),
388 'size' => (int) $c['Size'],
389 // 'hash' => trim((string)$c['ETag'])
390 // 'hash' => substr((string)$c['ETag'], 1, -1)
391 );
392 $next_marker = (string) $c['Key'];
393 }
394
395 if (isset($result['IsTruncated']) && empty($result['IsTruncated'])) return $results;
396
397 if (isset($result['NextMarker'])) $next_marker = (string) $result['NextMarker'];
398
399 // Loop through truncated results if max_keys isn't specified
400 if (null == $max_keys && null !== $next_marker && !empty($result['IsTruncated']))
401 do {
402 $vars['Marker'] = $next_marker;
403 $result = $this->client->listObjects($vars);
404
405 if (!is_a($result, 'Guzzle\Service\Resource\Model') || empty($result['Contents'])) break;
406
407 foreach ($result['Contents'] as $c) {
408 $results[(string) $c['Key']] = array(
409 'name' => (string) $c['Key'],
410 'time' => strtotime((string) $c['LastModified']),
411 'size' => (int) $c['Size'],
412 // 'hash' => trim((string)$c['ETag'])
413 // 'hash' => substr((string)$c['ETag'], 1, -1)
414 );
415 $next_marker = (string) $c['Key'];
416 }
417
418 // if ($return_common_prefixes && isset($response->body, $response->body->CommonPrefixes))
419 // foreach ($response->body->CommonPrefixes as $c)
420 // $results[(string)$c->Prefix] = array('prefix' => (string)$c->Prefix);
421
422 if (isset($response['NextMarker']))
423 $next_marker = (string) $response['NextMarker'];
424
425 } while (is_a($result, 'Guzzle\Service\Resource\Model') && !empty($result['Contents']) && !empty($result['IsTruncated']));
426
427 return $results;
428
429 } catch (Exception $e) {
430 if ($this->useExceptions) {
431 throw $e;
432 } else {
433 return $this->trigger_from_exception($e);
434 }
435 }
436 }
437
438 /**
439 * This is crude - nothing is returned
440 *
441 * @param string $bucket Name of the Bucket
442 * @return array Returns an array of results if bucket exists
443 */
444 public function waitForBucket($bucket) {
445 try {
446 $this->client->waitUntil('BucketExists', array('Bucket' => $bucket));
447 } catch (Exception $e) {
448 if ($this->useExceptions) {
449 throw $e;
450 } else {
451 return $this->trigger_from_exception($e);
452 }
453 }
454 }
455
456 /**
457 * Put a bucket
458 *
459 * @param string $bucket Bucket name
460 * @param constant $acl ACL flag
461 * @param string $location Set as "EU" to create buckets hosted in Europe
462 * @return boolean Returns true or false; or may throw an exception
463 */
464 public function putBucket($bucket, $acl = self::ACL_PRIVATE, $location = false) {
465 if (!$location) {
466 $location = $this->region;
467 } else {
468 $this->setRegion($location);
469 }
470 $bucket_vars = array(
471 'Bucket' => $bucket,
472 'ACL' => $acl,
473 );
474 // http://docs.aws.amazon.com/aws-sdk-php/latest/class-Aws.S3.S3Client.html#_createBucket
475 $location_constraint = apply_filters('updraftplus_s3_putbucket_defaultlocation', $location);
476 if ('us-east-1' != $location_constraint) $bucket_vars['LocationConstraint'] = $location_constraint;
477 try {
478 $result = $this->client->createBucket($bucket_vars);
479 if (is_object($result) && method_exists($result, 'get') && '' != $result->get('RequestId')) {
480 $this->client->waitUntil('BucketExists', array('Bucket' => $bucket));
481 return true;
482 }
483 } catch (Exception $e) {
484 if ($this->useExceptions) {
485 throw $e;
486 } else {
487 return $this->trigger_from_exception($e);
488 }
489 }
490 }
491
492 /**
493 * Initiate a multi-part upload (http://docs.amazonwebservices.com/AmazonS3/latest/API/mpUploadInitiate.html)
494 *
495 * @param string $bucket Bucket name
496 * @param string $uri Object URI
497 * @param constant $acl ACL constant
498 * @param array $meta_headers Array of x-amz-meta-* headers
499 * @param array $request_headers Array of request headers or content type as a string
500 * @param constant $storage_class Storage class constant
501 * @return string | false
502 */
503 public function initiateMultipartUpload($bucket, $uri, $acl = self::ACL_PRIVATE, $meta_headers = array(), $request_headers = array(), $storage_class = self::STORAGE_CLASS_STANDARD) {// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
504 $vars = array(
505 'ACL' => $acl,
506 'Bucket' => $bucket,
507 'Key' => $uri,
508 'Metadata' => $meta_headers,
509 'StorageClass' => $storage_class
510 );
511
512 $vars['ContentType'] = ('.gz' == strtolower(substr($uri, -3, 3))) ? 'application/octet-stream' : 'application/zip';
513
514 if (!empty($this->_serverSideEncryption)) $vars['ServerSideEncryption'] = $this->_serverSideEncryption;
515
516 try {
517 $result = $this->client->createMultipartUpload($vars);
518 if (is_object($result) && method_exists($result, 'get') && '' != $result->get('UploadId')) return $result->get('UploadId');
519 } catch (Exception $e) {
520 if ($this->useExceptions) {
521 throw $e;
522 } else {
523 return $this->trigger_from_exception($e);
524 }
525 }
526 return false;
527 }
528
529 /**
530 * Upload a part of a multi-part set (http://docs.amazonwebservices.com/AmazonS3/latest/API/mpUploadUploadPart.html)
531 * The chunk is read into memory, so make sure that you have enough (or patch this function to work another way!)
532 *
533 * @param string $bucket Bucket name
534 * @param string $uri Object URI
535 * @param string $upload_id upload_id returned previously from initiateMultipartUpload
536 * @param string $file_path file to upload content from
537 * @param integer $part_number sequential part number to upload
538 * @param integer $part_size number of bytes in each part (though final part may have fewer) - pass the same value each time (for this particular upload) - default 5Mb (which is Amazon's minimum)
539 * @return string (ETag) | false]
540 */
541 public function uploadPart($bucket, $uri, $upload_id, $file_path, $part_number, $part_size = 5242880) {
542 $vars = array(
543 'Bucket' => $bucket,
544 'Key' => $uri,
545 'PartNumber' => $part_number,
546 'UploadId' => $upload_id
547 );
548
549 // Where to begin
550 $file_offset = ($part_number - 1 ) * $part_size;
551
552 // Download the smallest of the remaining bytes and the part size
553 $file_bytes = min(filesize($file_path) - $file_offset, $part_size);
554 if ($file_bytes < 0) $file_bytes = 0;
555
556 // $rest->setHeader('Content-Type', 'application/octet-stream');
557 $data = "";
558
559 if ($handle = fopen($file_path, "rb")) {
560 if ($file_offset > 0) fseek($handle, $file_offset);
561 $bytes_read = 0;
562 while ($file_bytes > 0 && $read = fread($handle, max($file_bytes, 131072))) {
563 $file_bytes = $file_bytes - strlen($read);
564 $bytes_read += strlen($read);
565 $data .= $read;
566 }
567 fclose($handle);
568 } else {
569 return false;
570 }
571
572 $vars['Body'] = $data;
573
574 try {
575 $result = $this->client->uploadPart($vars);
576 if (is_object($result) && method_exists($result, 'get') && '' != $result->get('ETag')) return $result->get('ETag');
577 } catch (Exception $e) {
578 if ($this->useExceptions) {
579 throw $e;
580 } else {
581 return $this->trigger_from_exception($e);
582 }
583 }
584 return false;
585
586 }
587
588 /**
589 * Complete a multi-part upload (http://docs.amazonwebservices.com/AmazonS3/latest/API/mpUploadComplete.html)
590 *
591 * @param string $bucket Bucket name
592 * @param string $uri Object URI
593 * @param string $upload_id upload_id returned previously from initiateMultipartUpload
594 * @param array $parts an ordered list of eTags of previously uploaded parts from uploadPart
595 * @return boolean Returns either true of false
596 */
597 public function completeMultipartUpload($bucket, $uri, $upload_id, $parts) {
598 $vars = array(
599 'Bucket' => $bucket,
600 'Key' => $uri,
601 'UploadId' => $upload_id
602 );
603
604 $partno = 1;
605 $send_parts = array();
606 foreach ($parts as $etag) {
607 $send_parts[] = array('ETag' => $etag, 'PartNumber' => $partno);
608 $partno++;
609 }
610
611 $vars['Parts'] = $send_parts;
612
613 try {
614 $result = $this->client->completeMultipartUpload($vars);
615 if (is_object($result) && method_exists($result, 'get') && '' != $result->get('ETag')) return true;
616 } catch (Exception $e) {
617 if ($this->useExceptions) {
618 throw $e;
619 } else {
620 return $this->trigger_from_exception($e);
621 }
622 }
623 return false;
624 }
625
626 /**
627 * Put an object from a file (legacy function)
628 *
629 * @param string $file Input file path
630 * @param string $bucket Bucket name
631 * @param string $uri Object URI
632 * @param constant $acl ACL constant
633 * @param array $meta_headers Array of x-amz-meta-* headers
634 * @param string $content_type Content type
635 * @param string $storage_class STORAGE_CLASS_STANDARD constant
636 * @return boolean returns either true of false
637 */
638 public function putObjectFile($file, $bucket, $uri, $acl = self::ACL_PRIVATE, $meta_headers = array(), $content_type = null, $storage_class = self::STORAGE_CLASS_STANDARD) {
639 try {
640 $options = array(
641 'Bucket' => $bucket,
642 'Key' => $uri,
643 'SourceFile' => $file,
644 'StorageClass' => $storage_class,
645 'ACL' => $acl
646 );
647 if ($content_type) $options['ContentType'] = $content_type;
648 if (!empty($this->_serverSideEncryption)) $options['ServerSideEncryption'] = $this->_serverSideEncryption;
649 if (!empty($meta_headers)) $options['Metadata'] = $meta_headers;
650 $result = $this->client->putObject($options);
651 if (is_object($result) && method_exists($result, 'get') && '' != $result->get('RequestId')) return true;
652 } catch (Exception $e) {
653 if ($this->useExceptions) {
654 throw $e;
655 } else {
656 return $this->trigger_from_exception($e);
657 }
658 }
659 fclose($fh);
660 }
661
662
663 /**
664 * Put an object from a string (legacy function)
665 * Only the first 3 parameters vary in UpdraftPlus
666 *
667 * @param string $string Input data
668 * @param string $bucket Bucket name
669 * @param string $uri Object URI
670 * @param constant $acl ACL constant
671 * @param array $meta_headers Array of x-amz-meta-* headers
672 * @param string $content_type Content type
673 * @return boolean returns either true of false
674 */
675 public function putObjectString($string, $bucket, $uri, $acl = self::ACL_PRIVATE, $meta_headers = array(), $content_type = 'text/plain') { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- legacy function
676 try {
677 $result = $this->client->putObject(array(
678 'Bucket' => $bucket,
679 'Key' => $uri,
680 'Body' => $string,
681 'ContentType' => $content_type
682 ));
683 if (is_object($result) && method_exists($result, 'get') && '' != $result->get('RequestId')) return true;
684 } catch (Exception $e) {
685 if ($this->useExceptions) {
686 throw $e;
687 } else {
688 return $this->trigger_from_exception($e);
689 }
690 }
691 return false;
692 }
693
694
695 /**
696 * Get an object
697 *
698 * @param string $bucket Bucket name
699 * @param string $uri Object URI
700 * @param mixed $save_to Filename or resource to write to
701 * @param mixed $resume - if $save_to is a resource, then this is either false or the value for a Range: header; otherwise, a boolean, indicating whether to resume if possible.
702 * @return mixed
703 */
704 public function getObject($bucket, $uri, $save_to = false, $resume = false) {
705 try {
706 // SaveAs: "Specify where the contents of the object should be downloaded. Can be the path to a file, a resource returned by fopen, or a Guzzle\Http\EntityBodyInterface object." - http://docs.aws.amazon.com/aws-sdk-php/latest/class-Aws.S3.S3Client.html#_getObject
707
708 $range_header = false;
709 if (is_resource($save_to)) {
710 $fp = $save_to;
711 if (!is_bool($resume)) $range_header = $resume;
712 } elseif (file_exists($save_to)) {
713 if ($resume && ($fp = @fopen($save_to, 'ab')) !== false) {
714 $range_header = "bytes=".filesize($save_to).'-';
715 } else {
716 throw new Exception('Unable to open save file for writing: '.$save_to);
717 }
718 } else {
719 if (($fp = @fopen($save_to, 'wb')) !== false) {
720 $range_header = false;
721 } else {
722 throw new Exception('Unable to open save file for writing: '.$save_to);
723 }
724 }
725
726 $vars = array(
727 'Bucket' => $bucket,
728 'Key' => $uri,
729 'SaveAs' => $fp
730 );
731 if (!empty($range_header)) $vars['Range'] = $range_header;
732
733 $result = $this->client->getObject($vars);
734
735 if (is_object($result) && method_exists($result, 'get') && '' != $result->get('RequestId')) return true;
736 } catch (Exception $e) {
737 if ($this->useExceptions) {
738 throw $e;
739 } else {
740 return $this->trigger_from_exception($e);
741 }
742 }
743 return false;
744 }
745
746
747 /**
748 * Get a bucket's location
749 *
750 * @param string $bucket Bucket name
751 * @return string | false
752 */
753 public function getBucketLocation($bucket) {
754 try {
755 $result = $this->client->getBucketLocation(array('Bucket' => $bucket));
756 $location = $result->get('Location');
757 if ($location) return $location;
758 } catch (Aws\S3\Exception\NoSuchBucketException $e) {
759 return false;
760 } catch (Exception $e) {
761 if ($this->useExceptions) {
762 throw $e;
763 } else {
764 return $this->trigger_from_exception($e);
765 }
766 }
767 }
768
769 private function trigger_from_exception($e) {
770 trigger_error($e->getMessage().' ('.get_class($e).') (line: '.$e->getLine().', file: '.$e->getFile().')', E_USER_WARNING);
771 return false;
772 }
773
774 /**
775 * Delete an object
776 *
777 * @param string $bucket Bucket name
778 * @param string $uri Object URI
779 * @return boolean
780 */
781 public function deleteObject($bucket, $uri) {
782 try {
783 $result = $this->client->deleteObject(array(
784 'Bucket' => $bucket,
785 'Key' => $uri
786 ));
787 if (is_object($result) && method_exists($result, 'get') && '' != $result->get('RequestId')) return true;
788 } catch (Exception $e) {
789 if ($this->useExceptions) {
790 throw $e;
791 } else {
792 return $this->trigger_from_exception($e);
793 }
794 }
795 return false;
796 }
797
798 public function setCORS($policy) {
799 try {
800 $cors = $this->client->putBucketCors($policy);
801 if (is_object($cors) && method_exists($cors, 'get') && '' != $cors->get('RequestId')) return true;
802 } catch (Exception $e) {
803 if ($this->useExceptions) {
804 throw $e;
805 } else {
806 return $this->trigger_from_exception($e);
807 }
808 }
809 return false;
810
811 }
812 }
813