You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
788 lines
24 KiB
788 lines
24 KiB
<?php
|
|
|
|
// 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.
|
|
|
|
/**
|
|
*
|
|
* Copyright (c) 2012-5, David Anderson (https://www.simbahosting.co.uk). All rights reserved.
|
|
* Portions copyright (c) 2011, Donovan Schönknecht. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* - Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
* - Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* Amazon S3 is a trademark of Amazon.com, Inc. or its affiliates.
|
|
*/
|
|
|
|
require_once(UPDRAFTPLUS_DIR.'/vendor/autoload.php');
|
|
|
|
# SDK uses namespacing - requires PHP 5.3 (actually the SDK states its requirements as 5.3.3)
|
|
use Aws\S3;
|
|
|
|
/**
|
|
* Amazon S3 PHP class
|
|
*
|
|
* @link http://undesigned.org.za/2007/10/22/amazon-s3-php-class
|
|
* @version 0.5.0-dev
|
|
*/
|
|
class UpdraftPlus_S3_Compat
|
|
{
|
|
// ACL flags
|
|
const ACL_PRIVATE = 'private';
|
|
const ACL_PUBLIC_READ = 'public-read';
|
|
const ACL_PUBLIC_READ_WRITE = 'public-read-write';
|
|
const ACL_AUTHENTICATED_READ = 'authenticated-read';
|
|
|
|
const STORAGE_CLASS_STANDARD = 'STANDARD';
|
|
const STORAGE_CLASS_RRS = 'REDUCED_REDUNDANCY';
|
|
|
|
private $config = array('scheme' => 'https', 'service' => 's3');
|
|
|
|
private $__accessKey = null; // AWS Access key
|
|
private $__secretKey = null; // AWS Secret key
|
|
private $__sslKey = null;
|
|
|
|
public $endpoint = 's3.amazonaws.com';
|
|
public $proxy = null;
|
|
private $region = 'us-east-1';
|
|
|
|
// Added to cope with a particular situation where the user had no pernmission to check the bucket location, which necessitated using DNS-based endpoints.
|
|
public $use_dns_bucket_name = false;
|
|
|
|
public $useSSL = false;
|
|
public $useSSLValidation = true;
|
|
public $useExceptions = false;
|
|
|
|
private $_serverSideEncryption = null;
|
|
|
|
// SSL CURL SSL options - only needed if you are experiencing problems with your OpenSSL configuration
|
|
public $sslKey = null;
|
|
public $sslCert = null;
|
|
public $sslCACert = null;
|
|
|
|
/**
|
|
* Constructor - if you're not using the class statically
|
|
*
|
|
* @param string $accessKey Access key
|
|
* @param string $secretKey Secret key
|
|
* @param boolean $useSSL Enable SSL
|
|
* @param string|boolean $sslCACert - certificate authority (true = bundled Guzzle version; false = no verify, 'system' = system version; otherwise, path)
|
|
* @return void
|
|
*/
|
|
public function __construct($accessKey = null, $secretKey = null, $useSSL = true, $sslCACert = true, $endpoint = null)
|
|
{
|
|
if ($accessKey !== null && $secretKey !== null)
|
|
$this->setAuth($accessKey, $secretKey);
|
|
|
|
$this->useSSL = $useSSL;
|
|
$this->sslCACert = $sslCACert;
|
|
|
|
$opts = array(
|
|
'key' => $accessKey,
|
|
'secret' => $secretKey,
|
|
'scheme' => ($useSSL) ? 'https' : 'http',
|
|
// Using signature v4 requires a region
|
|
// 'signature' => 'v4',
|
|
// 'region' => $this->region
|
|
// 'endpoint' => 'somethingorother.s3.amazonaws.com'
|
|
);
|
|
|
|
if ($endpoint) {
|
|
// Can't specify signature v4, as that requires stating the region - which we don't necessarily yet know.
|
|
$this->endpoint = $endpoint;
|
|
$opts['endpoint'] = $endpoint;
|
|
} else {
|
|
// 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.
|
|
$opts['signature'] = 'v4';
|
|
$opts['region'] = $this->region;
|
|
}
|
|
|
|
if ($useSSL) $opts['ssl.certificate_authority'] = $sslCACert;
|
|
|
|
$this->client = Aws\S3\S3Client::factory($opts);
|
|
}
|
|
|
|
/**
|
|
* Set AWS access key and secret key
|
|
*
|
|
* @param string $accessKey Access key
|
|
* @param string $secretKey Secret key
|
|
* @return void
|
|
*/
|
|
public function setAuth($accessKey, $secretKey)
|
|
{
|
|
$this->__accessKey = $accessKey;
|
|
$this->__secretKey = $secretKey;
|
|
}
|
|
|
|
// Example value: 'AES256'. See: https://docs.aws.amazon.com/AmazonS3/latest/dev/SSEUsingPHPSDK.html
|
|
// Or, false to turn off.
|
|
public function setServerSideEncryption($value)
|
|
{
|
|
$this->_serverSideEncryption = $value;
|
|
}
|
|
|
|
/**
|
|
* Set the service region
|
|
*
|
|
* @param string $region Region
|
|
* @return void
|
|
*/
|
|
public function setRegion($region)
|
|
{
|
|
$this->region = $region;
|
|
if ('eu-central-1' == $region || 'cn-north-1' == $region) {
|
|
// $this->config['signature'] = new Aws\S3\S3SignatureV4('s3');
|
|
// $this->client->setConfig($this->config);
|
|
}
|
|
$this->client->setRegion($region);
|
|
}
|
|
|
|
/**
|
|
* Set the service endpoint
|
|
*
|
|
* @param string $host Hostname
|
|
* @return void
|
|
*/
|
|
public function setEndpoint($host, $region)
|
|
{
|
|
$this->endpoint = $host;
|
|
$this->region = $region;
|
|
$this->config['endpoint_provider'] = $this->return_provider();
|
|
$this->client->setConfig($this->config);
|
|
}
|
|
|
|
public function return_provider() {
|
|
$our_endpoints = array(
|
|
'endpoint' => $this->endpoint
|
|
);
|
|
if ($this->region == 'eu-central-1' || $this->region == 'cn-north-1') $our_endpoints['signatureVersion'] = 'v4';
|
|
$endpoints = array(
|
|
'version' => 2,
|
|
'endpoints' => array(
|
|
"*/s3" => $our_endpoints
|
|
)
|
|
);
|
|
return new Aws\Common\RulesEndpointProvider($endpoints);
|
|
}
|
|
|
|
/**
|
|
* Set SSL on or off
|
|
*
|
|
* @param boolean $enabled SSL enabled
|
|
* @param boolean $validate SSL certificate validation
|
|
* @return void
|
|
*/
|
|
// This code relies upon the particular pattern of SSL options-setting in s3.php in UpdraftPlus
|
|
public function setSSL($enabled, $validate = true)
|
|
{
|
|
$this->useSSL = $enabled;
|
|
$this->useSSLValidation = $validate;
|
|
// http://guzzle.readthedocs.org/en/latest/clients.html#verify
|
|
if ($enabled) {
|
|
|
|
// Do nothing - in UpdraftPlus, setSSLAuth will be called later, and we do the calls there
|
|
|
|
// $verify_peer = ($validate) ? true : false;
|
|
// $verify_host = ($validate) ? 2 : 0;
|
|
//
|
|
// $this->config['scheme'] = 'https';
|
|
// $this->client->setConfig($this->config);
|
|
//
|
|
// $this->client->setSslVerification($validate, $verify_peer, $verify_host);
|
|
|
|
|
|
} else {
|
|
$this->config['scheme'] = 'http';
|
|
// $this->client->setConfig($this->config);
|
|
}
|
|
$this->client->setConfig($this->config);
|
|
}
|
|
|
|
public function getuseSSL()
|
|
{
|
|
return $this->useSSL;
|
|
}
|
|
|
|
/**
|
|
* Set SSL client certificates (experimental)
|
|
*
|
|
* @param string $sslCert SSL client certificate
|
|
* @param string $sslKey SSL client key
|
|
* @param string $sslCACert SSL CA cert (only required if you are having problems with your system CA cert)
|
|
* @return void
|
|
*/
|
|
public function setSSLAuth($sslCert = null, $sslKey = null, $sslCACert = null)
|
|
{
|
|
if (!$this->useSSL) return;
|
|
|
|
if (!$this->useSSLValidation) {
|
|
$this->client->setSslVerification(false);
|
|
} else {
|
|
if (!$sslCACert) {
|
|
$client = $this->client;
|
|
$this->config[$client::SSL_CERT_AUTHORITY] = false;
|
|
$this->client->setConfig($this->config);
|
|
} else {
|
|
$this->client->setSslVerification(realpath($sslCACert), true, 2);
|
|
}
|
|
}
|
|
|
|
// $this->client->setSslVerification($sslCACert, $verify_peer, $verify_host);
|
|
// $this->config['ssl.certificate_authority'] = $sslCACert;
|
|
// $this->client->setConfig($this->config);
|
|
}
|
|
|
|
/**
|
|
* Set proxy information
|
|
*
|
|
* @param string $host Proxy hostname and port (localhost:1234)
|
|
* @param string $user Proxy username
|
|
* @param string $pass Proxy password
|
|
* @param constant $type CURL proxy type
|
|
* @return void
|
|
*/
|
|
public function setProxy($host, $user = null, $pass = null, $type = CURLPROXY_SOCKS5, $port = null)
|
|
{
|
|
|
|
$this->proxy = array('host' => $host, 'type' => $type, 'user' => $user, 'pass' => $pass, 'port' => $port);
|
|
|
|
if (!$host) return;
|
|
|
|
$wp_proxy = new WP_HTTP_Proxy();
|
|
if ($wp_proxy->send_through_proxy('https://s3.amazonaws.com'))
|
|
{
|
|
|
|
global $updraftplus;
|
|
$updraftplus->log("setProxy: host=$host, user=$user, port=$port");
|
|
|
|
// N.B. Currently (02-Feb-15), only support for HTTP proxies has ever been requested for S3 in UpdraftPlus
|
|
$proxy_url = 'http://';
|
|
if ($user) {
|
|
$proxy_url .= $user;
|
|
if ($pass) $proxy_url .= ":$pass";
|
|
$proxy_url .= "@";
|
|
}
|
|
|
|
$proxy_url .= $host;
|
|
|
|
if ($port) $proxy_url .= ":$port";
|
|
|
|
$this->client->setDefaultOption('proxy', $proxy_url);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Set the error mode to exceptions
|
|
*
|
|
* @param boolean $enabled Enable exceptions
|
|
* @return void
|
|
*/
|
|
public function setExceptions($enabled = true)
|
|
{
|
|
$this->useExceptions = $enabled;
|
|
}
|
|
|
|
// A no-op in this compatibility layer (for now - not yet found a use)...
|
|
public function useDNSBucketName($use = true, $bucket = '')
|
|
{
|
|
$this->use_dns_bucket_name = $use;
|
|
if ($use && $bucket) {
|
|
$this->setEndpoint($bucket.'.s3.amazonaws.com', $this->region);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Get contents for a bucket
|
|
*
|
|
* If maxKeys is null this method will loop through truncated result sets
|
|
*
|
|
* @param string $bucket Bucket name
|
|
* @param string $prefix Prefix
|
|
* @param string $marker Marker (last file listed)
|
|
* @param string $maxKeys Max keys (maximum number of keys to return)
|
|
* @param string $delimiter Delimiter
|
|
* @param boolean $returnCommonPrefixes Set to true to return CommonPrefixes
|
|
* @return array | false
|
|
*/
|
|
// N.B. UpdraftPlus does not use the $delimiter or $returnCommonPrefixes parameters (nor set $prefix or $marker to anything other than null)
|
|
// $returnCommonPrefixes is not implemented below
|
|
public function getBucket($bucket, $prefix = null, $marker = null, $maxKeys = null, $delimiter = null, $returnCommonPrefixes = false)
|
|
{
|
|
try {
|
|
if ($maxKeys == 0) $maxKeys = null;
|
|
|
|
$vars = array('Bucket' => $bucket);
|
|
if ($prefix !== null && $prefix !== '') $vars['Prefix'] = $prefix;
|
|
if ($marker !== null && $marker !== '') $vars['Marker'] = $marker;
|
|
if ($maxKeys !== null && $maxKeys !== '') $vars['MaxKeys'] = $maxKeys;
|
|
if ($delimiter !== null && $delimiter !== '') $vars['Delimiter'] = $delimiter;
|
|
$result = $this->client->listObjects($vars);
|
|
|
|
if (!is_a($result, 'Guzzle\Service\Resource\Model')) {
|
|
return false;
|
|
}
|
|
|
|
$results = array();
|
|
$nextMarker = null;
|
|
// http://docs.aws.amazon.com/AmazonS3/latest/dev/ListingObjectKeysUsingPHP.html
|
|
// UpdraftPlus does not use the 'hash' result
|
|
if (empty($result['Contents'])) $result['Contents'] = array();
|
|
foreach ($result['Contents'] as $c)
|
|
{
|
|
$results[(string)$c['Key']] = array(
|
|
'name' => (string)$c['Key'],
|
|
'time' => strtotime((string)$c['LastModified']),
|
|
'size' => (int)$c['Size'],
|
|
// 'hash' => trim((string)$c['ETag'])
|
|
// 'hash' => substr((string)$c['ETag'], 1, -1)
|
|
);
|
|
$nextMarker = (string)$c['Key'];
|
|
}
|
|
|
|
if (isset($result['IsTruncated']) && empty($result['IsTruncated'])) return $results;
|
|
|
|
if (isset($result['NextMarker'])) $nextMarker = (string)$result['NextMarker'];
|
|
|
|
// Loop through truncated results if maxKeys isn't specified
|
|
if ($maxKeys == null && $nextMarker !== null && !empty($result['IsTruncated']))
|
|
do
|
|
{
|
|
$vars['Marker'] = $nextMarker;
|
|
$result = $this->client->listObjects($vars);
|
|
|
|
if (!is_a($result, 'Guzzle\Service\Resource\Model') || empty($result['Contents'])) break;
|
|
|
|
foreach ($result['Contents'] as $c)
|
|
{
|
|
$results[(string)$c['Key']] = array(
|
|
'name' => (string)$c['Key'],
|
|
'time' => strtotime((string)$c['LastModified']),
|
|
'size' => (int)$c['Size'],
|
|
// 'hash' => trim((string)$c['ETag'])
|
|
// 'hash' => substr((string)$c['ETag'], 1, -1)
|
|
);
|
|
$nextMarker = (string)$c['Key'];
|
|
}
|
|
|
|
// if ($returnCommonPrefixes && isset($response->body, $response->body->CommonPrefixes))
|
|
// foreach ($response->body->CommonPrefixes as $c)
|
|
// $results[(string)$c->Prefix] = array('prefix' => (string)$c->Prefix);
|
|
|
|
if (isset($response['NextMarker']))
|
|
$nextMarker = (string)$response['NextMarker'];
|
|
|
|
} while (is_a($result, 'Guzzle\Service\Resource\Model') && !empty($result['Contents']) && !empty($result['IsTruncated']));
|
|
|
|
return $results;
|
|
|
|
} catch (Exception $e) {
|
|
if ($this->useExceptions) {
|
|
throw $e;
|
|
} else {
|
|
return $this->trigger_from_exception($e);
|
|
}
|
|
}
|
|
}
|
|
|
|
// This is crude - nothing is returned
|
|
public function waitForBucket($bucket) {
|
|
try {
|
|
$this->client->waitUntil('BucketExists', array('Bucket' => $bucket));
|
|
} catch (Exception $e) {
|
|
if ($this->useExceptions) {
|
|
throw $e;
|
|
} else {
|
|
return $this->trigger_from_exception($e);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Put a bucket
|
|
*
|
|
* @param string $bucket Bucket name
|
|
* @param constant $acl ACL flag
|
|
* @param string $location Set as "EU" to create buckets hosted in Europe
|
|
* @return boolean
|
|
*/
|
|
public function putBucket($bucket, $acl = self::ACL_PRIVATE, $location = false)
|
|
{
|
|
if (!$location) {
|
|
$location = $this->region;
|
|
} else {
|
|
$this->setRegion($location);
|
|
}
|
|
$bucket_vars = array(
|
|
'Bucket' => $bucket,
|
|
'ACL' => $acl,
|
|
);
|
|
// http://docs.aws.amazon.com/aws-sdk-php/latest/class-Aws.S3.S3Client.html#_createBucket
|
|
$location_constraint = apply_filters('updraftplus_s3_putbucket_defaultlocation', $location);
|
|
if ('us-east-1' != $location_constraint) $bucket_vars['LocationConstraint'] = $location_constraint;
|
|
try {
|
|
$result = $this->client->createBucket($bucket_vars);
|
|
if (is_object($result) && method_exists($result, 'get') && '' != $result->get('RequestId')) {
|
|
$this->client->waitUntil('BucketExists', array('Bucket' => $bucket));
|
|
return true;
|
|
}
|
|
} catch (Exception $e) {
|
|
if ($this->useExceptions) {
|
|
throw $e;
|
|
} else {
|
|
return $this->trigger_from_exception($e);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initiate a multi-part upload (http://docs.amazonwebservices.com/AmazonS3/latest/API/mpUploadInitiate.html)
|
|
*
|
|
* @param string $bucket Bucket name
|
|
* @param string $uri Object URI
|
|
* @param constant $acl ACL constant
|
|
* @param array $metaHeaders Array of x-amz-meta-* headers
|
|
* @param array $requestHeaders Array of request headers or content type as a string
|
|
* @param constant $storageClass Storage class constant
|
|
* @return string | false
|
|
*/
|
|
public function initiateMultipartUpload ($bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $requestHeaders = array(), $storageClass = self::STORAGE_CLASS_STANDARD)
|
|
{
|
|
$vars = array(
|
|
'ACL' => $acl,
|
|
'Bucket' => $bucket,
|
|
'Key' => $uri,
|
|
'Metadata' => $metaHeaders,
|
|
'StorageClass' => $storageClass
|
|
);
|
|
|
|
$vars['ContentType'] = ('.gz' == strtolower(substr($uri, -3, 3))) ? 'application/octet-stream' : 'application/zip';
|
|
|
|
if (!empty($this->_serverSideEncryption)) $vars['ServerSideEncryption'] = $this->_serverSideEncryption;
|
|
|
|
try {
|
|
$result = $this->client->createMultipartUpload($vars);
|
|
if (is_object($result) && method_exists($result, 'get') && '' != $result->get('UploadId')) return $result->get('UploadId');
|
|
} catch (Exception $e) {
|
|
if ($this->useExceptions) {
|
|
throw $e;
|
|
} else {
|
|
return $this->trigger_from_exception($e);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
/* Upload a part of a multi-part set (http://docs.amazonwebservices.com/AmazonS3/latest/API/mpUploadUploadPart.html)
|
|
* The chunk is read into memory, so make sure that you have enough (or patch this function to work another way!)
|
|
*
|
|
* @param string $bucket Bucket name
|
|
* @param string $uri Object URI
|
|
* @param string $uploadId uploadId returned previously from initiateMultipartUpload
|
|
* @param integer $partNumber sequential part number to upload
|
|
* @param string $filePath file to upload content from
|
|
* @param integer $partSize 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)
|
|
* @return string (ETag) | false
|
|
*/
|
|
public function uploadPart ($bucket, $uri, $uploadId, $filePath, $partNumber, $partSize = 5242880)
|
|
{
|
|
$vars = array(
|
|
'Bucket' => $bucket,
|
|
'Key' => $uri,
|
|
'PartNumber' => $partNumber,
|
|
'UploadId' => $uploadId
|
|
);
|
|
|
|
// Where to begin
|
|
$fileOffset = ($partNumber - 1 ) * $partSize;
|
|
|
|
// Download the smallest of the remaining bytes and the part size
|
|
$fileBytes = min(filesize($filePath) - $fileOffset, $partSize);
|
|
if ($fileBytes < 0) $fileBytes = 0;
|
|
|
|
// $rest->setHeader('Content-Type', 'application/octet-stream');
|
|
$data = "";
|
|
|
|
if ($handle = fopen($filePath, "rb")) {
|
|
if ($fileOffset >0) fseek($handle, $fileOffset);
|
|
$bytes_read = 0;
|
|
while ($fileBytes>0 && $read = fread($handle, max($fileBytes, 131072))) {
|
|
$fileBytes = $fileBytes - strlen($read);
|
|
$bytes_read += strlen($read);
|
|
$data .= $read;
|
|
}
|
|
fclose($handle);
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
$vars['Body'] = $data;
|
|
|
|
try {
|
|
$result = $this->client->uploadPart($vars);
|
|
if (is_object($result) && method_exists($result, 'get') && '' != $result->get('ETag')) return $result->get('ETag');
|
|
} catch (Exception $e) {
|
|
if ($this->useExceptions) {
|
|
throw $e;
|
|
} else {
|
|
return $this->trigger_from_exception($e);
|
|
}
|
|
}
|
|
return false;
|
|
|
|
}
|
|
|
|
/**
|
|
* Complete a multi-part upload (http://docs.amazonwebservices.com/AmazonS3/latest/API/mpUploadComplete.html)
|
|
*
|
|
* @param string $bucket Bucket name
|
|
* @param string $uri Object URI
|
|
* @param string $uploadId uploadId returned previously from initiateMultipartUpload
|
|
* @param array $parts an ordered list of eTags of previously uploaded parts from uploadPart
|
|
* @return boolean
|
|
*/
|
|
public function completeMultipartUpload ($bucket, $uri, $uploadId, $parts)
|
|
{
|
|
$vars = array(
|
|
'Bucket' => $bucket,
|
|
'Key' => $uri,
|
|
'UploadId' => $uploadId
|
|
);
|
|
|
|
$partno = 1;
|
|
$send_parts = array();
|
|
foreach ($parts as $etag) {
|
|
$send_parts[] = array('ETag' => $etag, 'PartNumber' => $partno);
|
|
$partno++;
|
|
}
|
|
|
|
$vars['Parts'] = $send_parts;
|
|
|
|
try {
|
|
$result = $this->client->completeMultipartUpload($vars);
|
|
if (is_object($result) && method_exists($result, 'get') && '' != $result->get('ETag')) return true;
|
|
} catch (Exception $e) {
|
|
if ($this->useExceptions) {
|
|
throw $e;
|
|
} else {
|
|
return $this->trigger_from_exception($e);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Put an object from a file (legacy function)
|
|
*
|
|
* @param string $file Input file path
|
|
* @param string $bucket Bucket name
|
|
* @param string $uri Object URI
|
|
* @param constant $acl ACL constant
|
|
* @param array $metaHeaders Array of x-amz-meta-* headers
|
|
* @param string $contentType Content type
|
|
* @return boolean
|
|
*/
|
|
public function putObjectFile($file, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $contentType = null, $storageClass = self::STORAGE_CLASS_STANDARD)
|
|
{
|
|
try {
|
|
$options = array(
|
|
'Bucket' => $bucket,
|
|
'Key' => $uri,
|
|
'SourceFile' => $file,
|
|
'StorageClass' => $storageClass,
|
|
'ACL' => $acl
|
|
);
|
|
if ($contentType) $options['ContentType'] = $contentType;
|
|
if (!empty($this->_serverSideEncryption)) $options['ServerSideEncryption'] = $this->_serverSideEncryption;
|
|
if (!empty($metaHeaders)) $options['Metadata'] = $metaHeaders;
|
|
$result = $this->client->putObject($options);
|
|
if (is_object($result) && method_exists($result, 'get') && '' != $result->get('RequestId')) return true;
|
|
} catch (Exception $e) {
|
|
if ($this->useExceptions) {
|
|
throw $e;
|
|
} else {
|
|
return $this->trigger_from_exception($e);
|
|
}
|
|
}
|
|
fclose($fh);
|
|
}
|
|
|
|
|
|
/**
|
|
* Put an object from a string (legacy function)
|
|
*
|
|
* @param string $string Input data
|
|
* @param string $bucket Bucket name
|
|
* @param string $uri Object URI
|
|
* @param constant $acl ACL constant
|
|
* @param array $metaHeaders Array of x-amz-meta-* headers
|
|
* @param string $contentType Content type
|
|
* @return boolean
|
|
*/
|
|
// Only the first 3 parameters vary in UpdraftPlus
|
|
public function putObjectString($string, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $contentType = 'text/plain')
|
|
{
|
|
try {
|
|
$result = $this->client->putObject(array(
|
|
'Bucket' => $bucket,
|
|
'Key' => $uri,
|
|
'Body' => $string,
|
|
'ContentType' => $contentType
|
|
));
|
|
if (is_object($result) && method_exists($result, 'get') && '' != $result->get('RequestId')) return true;
|
|
} catch (Exception $e) {
|
|
if ($this->useExceptions) {
|
|
throw $e;
|
|
} else {
|
|
return $this->trigger_from_exception($e);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get an object
|
|
*
|
|
* @param string $bucket Bucket name
|
|
* @param string $uri Object URI
|
|
* @param mixed $saveTo Filename or resource to write to
|
|
* @param mixed $resume - if $saveTo is a resource, then this is either false or the value for a Range: header; otherwise, a boolean, indicating whether to resume if possible.
|
|
* @return mixed
|
|
*/
|
|
public function getObject($bucket, $uri, $saveTo = false, $resume = false)
|
|
{
|
|
try {
|
|
// 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
|
|
|
|
$range_header = false;
|
|
if (is_resource($saveTo)) {
|
|
$fp = $saveTo;
|
|
if (!is_bool($resume)) $range_header = $resume;
|
|
} elseif (file_exists($saveTo)) {
|
|
if ($resume && ($fp = @fopen($saveTo, 'ab')) !== false) {
|
|
$range_header = "bytes=".filesize($saveTo).'-';
|
|
} else {
|
|
throw new Exception('Unable to open save file for writing: '.$saveTo);
|
|
}
|
|
} else {
|
|
if (($fp = @fopen($saveTo, 'wb')) !== false) {
|
|
$range_header = false;
|
|
} else {
|
|
throw new Exception('Unable to open save file for writing: '.$saveTo);
|
|
}
|
|
}
|
|
|
|
$vars = array(
|
|
'Bucket' => $bucket,
|
|
'Key' => $uri,
|
|
'SaveAs' => $fp
|
|
);
|
|
if (!empty($range_header)) $vars['Range'] = $range_header;
|
|
|
|
$result = $this->client->getObject($vars);
|
|
|
|
if (is_object($result) && method_exists($result, 'get') && '' != $result->get('RequestId')) return true;
|
|
} catch (Exception $e) {
|
|
if ($this->useExceptions) {
|
|
throw $e;
|
|
} else {
|
|
return $this->trigger_from_exception($e);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get a bucket's location
|
|
*
|
|
* @param string $bucket Bucket name
|
|
* @return string | false
|
|
*/
|
|
public function getBucketLocation($bucket)
|
|
{
|
|
try {
|
|
$result = $this->client->getBucketLocation(array('Bucket' => $bucket));
|
|
$location = $result->get('Location');
|
|
if ($location) return $location;
|
|
} catch (Aws\S3\Exception\NoSuchBucketException $e) {
|
|
return false;
|
|
} catch (Exception $e) {
|
|
if ($this->useExceptions) {
|
|
throw $e;
|
|
} else {
|
|
return $this->trigger_from_exception($e);
|
|
}
|
|
}
|
|
}
|
|
|
|
private function trigger_from_exception($e) {
|
|
trigger_error($e->getMessage().' ('.get_class($e).') (line: '.$e->getLine().', file: '.$e->getFile().')', E_USER_WARNING);
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Delete an object
|
|
*
|
|
* @param string $bucket Bucket name
|
|
* @param string $uri Object URI
|
|
* @return boolean
|
|
*/
|
|
public function deleteObject($bucket, $uri)
|
|
{
|
|
try {
|
|
$result = $this->client->deleteObject(array(
|
|
'Bucket' => $bucket,
|
|
'Key' => $uri
|
|
));
|
|
if (is_object($result) && method_exists($result, 'get') && '' != $result->get('RequestId')) return true;
|
|
} catch (Exception $e) {
|
|
if ($this->useExceptions) {
|
|
throw $e;
|
|
} else {
|
|
return $this->trigger_from_exception($e);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function setCORS($policy)
|
|
{
|
|
try {
|
|
$cors = $this->client->putBucketCors($policy);
|
|
if (is_object($cors) && method_exists($cors, 'get') && '' != $cors->get('RequestId')) return true;
|
|
} catch (Exception $e) {
|
|
if ($this->useExceptions) {
|
|
throw $e;
|
|
} else {
|
|
return $this->trigger_from_exception($e);
|
|
}
|
|
}
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|