PluginProbe
InfiniteWP Client / trunk
InfiniteWP Client vtrunk
1.13.10 1.13.7 trunk 0.1.4 0.1.5 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.1.0 1.1.1 1.1.10 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 1.1.9 1.11.0 1.11.1 1.12.1 1.12.3 All 92 releases
iwp-client / lib / S3compat.php

S3compat.php in InfiniteWP Client trunk, at lib/S3compat.php

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