2016-11-28 21:52:15 -08:00
< ? php
/**
2018-01-26 15:50:15 +01:00
* $Id $
*
* Copyright ( c ) 2011 , Donovan Schönknecht . All rights reserved .
* Portions copyright ( c ) 2012 - 2018 , David Anderson ( https :// david . dw - perspective . org . uk ) . 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 .
*/
2016-11-28 21:52:15 -08:00
/**
2018-01-26 15:50:15 +01:00
* Amazon S3 PHP class
*
* @ link http :// undesigned . org . za / 2007 / 10 / 22 / amazon - s3 - php - class
* @ version 0.5 . 0 - dev
*/
class UpdraftPlus_S3 {
2016-11-28 21:52:15 -08:00
// 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' ;
2018-01-26 15:50:15 +01:00
private $__accessKey = null ; // AWS Access key
private $__secretKey = null ; // AWS Secret key
private $__sslKey = null ;
private $_serverSideEncryption = false ;
2016-11-28 21:52:15 -08:00
2018-01-26 15:50:15 +01:00
public $endpoint = 's3.amazonaws.com' ;
public $region = '' ;
public $proxy = null ;
2016-11-28 21:52:15 -08:00
2018-01-26 15:50:15 +01:00
// Added to cope with a particular situation where the user had no permission to check the bucket location, which necessitated using DNS-based endpoints.
public $use_dns_bucket_name = false ;
2016-11-28 21:52:15 -08:00
2018-01-26 15:50:15 +01:00
public $useSSL = false ;
public $useSSLValidation = true ;
public $useExceptions = false ;
// Added at request of a user using a non-default port.
public $port = false ;
2016-11-28 21:52:15 -08:00
// SSL CURL SSL options - only needed if you are experiencing problems with your OpenSSL configuration
2018-01-26 15:50:15 +01:00
public $sslKey = null ;
public $sslCert = null ;
public $sslCACert = null ;
private $__signingKeyPairId = null ; // AWS Key Pair ID
private $__signingKeyResource = false ; // Key resource, freeSigningKey() must be called to clear it from memory
public $signVer = 'v2' ;
/**
* 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 boolean $sslCACert SSL Certificate
* @ param string | null $endpoint Endpoint
* @ param string $region Region
* @ throws Exception
*
* @ return self
*/
public function __construct ( $accessKey = null , $secretKey = null , $useSSL = true , $sslCACert = true , $endpoint = null , $region = '' ) {
if ( null !== $accessKey && null !== $secretKey ) {
$this -> setAuth ( $accessKey , $secretKey );
}
$this -> useSSL = $useSSL ;
$this -> sslCACert = $sslCACert ;
if ( ! empty ( $endpoint )) {
$this -> endpoint = $endpoint ;
}
$this -> region = $region ;
2016-11-28 21:52:15 -08:00
if ( ! function_exists ( 'curl_init' )) {
global $updraftplus ;
$updraftplus -> log ( 'The PHP cURL extension must be installed and enabled to use this remote storage method' );
throw new Exception ( 'The PHP cURL extension must be installed and enabled to use this remote storage method' );
}
2018-01-26 15:50:15 +01:00
}
2016-11-28 21:52:15 -08:00
2018-01-26 15:50:15 +01:00
/**
* Set the service endpoint
*
* @ param string $host Hostname
*
* @ return void
*/
public function setEndpoint ( $host ) {
$this -> endpoint = $host ;
}
/**
* Set Server Side Encryption
* Example value : 'AES256' . See : https :// docs . aws . amazon . com / AmazonS3 / latest / dev / SSEUsingPHPSDK . html
*
* @ param string | boolean $sse Server side encryption standard ; or false for none
* @ return void
*/
public function setServerSideEncryption ( $value ) {
$this -> _serverSideEncryption = $value ;
}
/**
* Set the service region
*
* @ param string $region Region
* @ return void
*/
public function setRegion ( $region ) {
$this -> region = $region ;
2016-11-28 21:52:15 -08:00
}
2018-01-26 15:50:15 +01:00
/**
* Get the service region
* Note : Region calculation will be done in methods / s3 . php file
*
* @ return string Region
*/
public function getRegion () {
return $this -> region ;
}
2016-11-28 21:52:15 -08:00
/**
2018-01-26 15:50:15 +01:00
* Set the service port
*
* @ param Integer $port Port number
*
* @ return void
*/
public function setPort ( $port ) {
$this -> port = $port ;
2016-11-28 21:52:15 -08:00
}
/**
2018-01-26 15:50:15 +01:00
* 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 ;
2016-11-28 21:52:15 -08:00
}
/**
2018-01-26 15:50:15 +01:00
* Check if AWS keys have been set
*
* @ return boolean
*/
public function hasAuth () {
return ( null !== $this -> __accessKey && null !== $this -> __secretKey );
2016-11-28 21:52:15 -08:00
}
/**
2018-01-26 15:50:15 +01:00
* Set SSL on or off
*
* @ param boolean $enabled SSL enabled
* @ param boolean $validate SSL certificate validation
*
* @ return void
*/
public function setSSL ( $enabled , $validate = true ) {
$this -> useSSL = $enabled ;
$this -> useSSLValidation = $validate ;
2016-11-28 21:52:15 -08:00
}
2018-01-26 15:50:15 +01:00
/**
* Get SSL value . Determines whether use it or not .
*
* @ return bool
*/
public function getuseSSL () {
return $this -> useSSL ;
2016-11-28 21:52:15 -08:00
}
/**
2018-01-26 15:50:15 +01:00
* 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 ) {
$this -> sslCert = $sslCert ;
$this -> sslKey = $sslKey ;
$this -> sslCACert = $sslCACert ;
2016-11-28 21:52:15 -08:00
}
/**
2018-01-26 15:50:15 +01:00
* Set proxy information
*
* @ param string $host Proxy hostname and port ( localhost : 1234 )
* @ param string $user Proxy username
* @ param string $pass Proxy password
* @ param integer $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 );
2016-11-28 21:52:15 -08:00
}
/**
2018-01-26 15:50:15 +01:00
* Set the error mode to exceptions
*
* @ param boolean $enabled Enable exceptions
*
* @ return void
*/
public function setExceptions ( $enabled = true ) {
$this -> useExceptions = $enabled ;
2016-11-28 21:52:15 -08:00
}
/**
2018-01-26 15:50:15 +01:00
* Set signing key
*
* @ param string $keyPairId AWS Key Pair ID
* @ param string $signingKey Private Key
* @ param boolean $isFile Load private key from file , set to false to load string
*
* @ return boolean
*/
public function setSigningKey ( $keyPairId , $signingKey , $isFile = true ) {
$this -> __signingKeyPairId = $keyPairId ;
if (( $this -> __signingKeyResource = openssl_pkey_get_private ( $isFile ?
2016-11-28 21:52:15 -08:00
file_get_contents ( $signingKey ) : $signingKey )) !== false ) return true ;
2018-01-26 15:50:15 +01:00
$this -> __triggerError ( 'UpdraftPlus_S3::setSigningKey(): Unable to open load private key: ' . $signingKey , __FILE__ , __LINE__ );
2016-11-28 21:52:15 -08:00
return false ;
}
/**
2018-01-26 15:50:15 +01:00
* Free signing key from memory , MUST be called if you are using setSigningKey ()
*
* @ return void
*/
public function freeSigningKey () {
if ( false !== $this -> __signingKeyResource ) {
openssl_free_key ( $this -> __signingKeyResource );
}
2016-11-28 21:52:15 -08:00
}
2018-01-26 15:50:15 +01:00
/**
* Set Signature Version
*
* @ param string $version
* @ return void
*/
public function setSignatureVersion ( $version = 'v2' ) {
$this -> signVer = $version ;
}
2016-11-28 21:52:15 -08:00
/**
2018-01-26 15:50:15 +01:00
* Internal error handler
*
* @ param string $message Error message
* @ param string $file Filename
* @ param integer $line Line number
* @ param integer $code Error code
*
* @ internal Internal error handler
* @ throws UpdraftPlus_S3Exception
*
* @ return void
*/
private function __triggerError ( $message , $file , $line , $code = 0 ) {
if ( $this -> useExceptions ) {
2016-11-28 21:52:15 -08:00
throw new UpdraftPlus_S3Exception ( $message , $file , $line , $code );
2018-01-26 15:50:15 +01:00
} else {
2016-11-28 21:52:15 -08:00
trigger_error ( $message , E_USER_WARNING );
}
}
/**
2018-01-26 15:50:15 +01:00
* Get a list of buckets
*
* @ param boolean $detailed Returns detailed bucket list when true
*
* @ return array | false
*/
public function listBuckets ( $detailed = false ) {
$rest = new UpdraftPlus_S3Request ( 'GET' , '' , '' , $this -> endpoint , $this -> use_dns_bucket_name , $this );
2016-11-28 21:52:15 -08:00
$rest = $rest -> getResponse ();
2018-01-26 15:50:15 +01:00
if ( false === $rest -> error && 200 !== $rest -> code ) {
2016-11-28 21:52:15 -08:00
$rest -> error = array ( 'code' => $rest -> code , 'message' => 'Unexpected HTTP status' );
2018-01-26 15:50:15 +01:00
}
if ( false !== $rest -> error ) {
$this -> __triggerError ( sprintf ( " UpdraftPlus_S3::listBuckets(): [%s] %s " , $rest -> error [ 'code' ],
2016-11-28 21:52:15 -08:00
$rest -> error [ 'message' ]), __FILE__ , __LINE__ );
return false ;
}
$results = array ();
if ( ! isset ( $rest -> body -> Buckets )) return $results ;
2018-01-26 15:50:15 +01:00
if ( $detailed ) {
2016-11-28 21:52:15 -08:00
if ( isset ( $rest -> body -> Owner , $rest -> body -> Owner -> ID , $rest -> body -> Owner -> DisplayName ))
$results [ 'owner' ] = array (
'id' => ( string ) $rest -> body -> Owner -> ID , 'name' => ( string ) $rest -> body -> Owner -> ID
);
$results [ 'buckets' ] = array ();
2018-01-26 15:50:15 +01:00
foreach ( $rest -> body -> Buckets -> Bucket as $b ) {
2016-11-28 21:52:15 -08:00
$results [ 'buckets' ][] = array (
'name' => ( string ) $b -> Name , 'time' => strtotime (( string ) $b -> CreationDate )
);
2018-01-26 15:50:15 +01:00
}
} else {
2016-11-28 21:52:15 -08:00
foreach ( $rest -> body -> Buckets -> Bucket as $b ) $results [] = ( string ) $b -> Name ;
2018-01-26 15:50:15 +01:00
}
2016-11-28 21:52:15 -08:00
return $results ;
}
2018-01-26 15:50:15 +01:00
public function useDNSBucketName ( $use = true , $bucket = '' ) {
$this -> use_dns_bucket_name = $use ;
2016-11-28 21:52:15 -08:00
return true ;
}
2018-01-26 15:50:15 +01:00
/**
* 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
*/
public function getBucket ( $bucket , $prefix = null , $marker = null , $maxKeys = null , $delimiter = null , $returnCommonPrefixes = false ) {
$rest = new UpdraftPlus_S3Request ( 'GET' , $bucket , '' , $this -> endpoint , $this -> use_dns_bucket_name , $this );
if ( 0 == $maxKeys ) $maxKeys = null ;
if ( ! empty ( $prefix )) $rest -> setParameter ( 'prefix' , $prefix );
if ( ! empty ( $marker )) $rest -> setParameter ( 'marker' , $marker );
if ( ! empty ( $maxKeys )) $rest -> setParameter ( 'max-keys' , $maxKeys );
if ( ! empty ( $delimiter )) $rest -> setParameter ( 'delimiter' , $delimiter );
2016-11-28 21:52:15 -08:00
$response = $rest -> getResponse ();
2018-01-26 15:50:15 +01:00
if ( false === $response -> error && 200 !== $response -> code ) {
2016-11-28 21:52:15 -08:00
$response -> error = array ( 'code' => $response -> code , 'message' => 'Unexpected HTTP status' );
2018-01-26 15:50:15 +01:00
}
if ( false !== $response -> error ) {
$this -> __triggerError ( sprintf ( " UpdraftPlus_S3::getBucket(): [%s] %s " ,
2016-11-28 21:52:15 -08:00
$response -> error [ 'code' ], $response -> error [ 'message' ]), __FILE__ , __LINE__ );
return false ;
}
$results = array ();
$nextMarker = null ;
if ( isset ( $response -> body , $response -> body -> Contents ))
2018-01-26 15:50:15 +01:00
foreach ( $response -> body -> Contents as $c ) {
2016-11-28 21:52:15 -08:00
$results [( string ) $c -> Key ] = array (
'name' => ( string ) $c -> Key ,
'time' => strtotime (( string ) $c -> LastModified ),
'size' => ( int ) $c -> Size ,
'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 -> body , $response -> body -> IsTruncated ) &&
( string ) $response -> body -> IsTruncated == 'false' ) return $results ;
if ( isset ( $response -> body , $response -> body -> NextMarker ))
$nextMarker = ( string ) $response -> body -> NextMarker ;
// Loop through truncated results if maxKeys isn't specified
2018-01-26 15:50:15 +01:00
if ( null == $maxKeys && null !== $nextMarker && 'true' == ( string ) $response -> body -> IsTruncated )
2016-11-28 21:52:15 -08:00
do
{
2018-01-26 15:50:15 +01:00
$rest = new UpdraftPlus_S3Request ( 'GET' , $bucket , '' , $this -> endpoint , $this -> use_dns_bucket_name , $this );
if ( ! empty ( $prefix )) $rest -> setParameter ( 'prefix' , $prefix );
2016-11-28 21:52:15 -08:00
$rest -> setParameter ( 'marker' , $nextMarker );
2018-01-26 15:50:15 +01:00
if ( ! empty ( $delimiter )) $rest -> setParameter ( 'delimiter' , $delimiter );
2016-11-28 21:52:15 -08:00
2018-01-26 15:50:15 +01:00
if ( false == ( $response = $rest -> getResponse ()) || 200 !== $response -> code ) break ;
2016-11-28 21:52:15 -08:00
if ( isset ( $response -> body , $response -> body -> Contents ))
foreach ( $response -> body -> Contents as $c )
{
$results [( string ) $c -> Key ] = array (
'name' => ( string ) $c -> Key ,
'time' => strtotime (( string ) $c -> LastModified ),
'size' => ( int ) $c -> Size ,
'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 -> body , $response -> body -> NextMarker ))
$nextMarker = ( string ) $response -> body -> NextMarker ;
2018-01-26 15:50:15 +01:00
} while ( false !== $response && 'true' == ( string ) $response -> body -> IsTruncated );
2016-11-28 21:52:15 -08:00
return $results ;
}
/**
2018-01-26 15:50:15 +01:00
* Put a bucket
*
* @ param string $bucket Bucket name
* @ param string ACL_PRIVATE ACL flag
* @ param mixed $location Set as " EU " to create buckets hosted in Europe
* @ return boolean
*/
public function putBucket ( $bucket , $acl = self :: ACL_PRIVATE , $location = false ) {
$rest = new UpdraftPlus_S3Request ( 'PUT' , $bucket , '' , $this -> endpoint , $this -> use_dns_bucket_name , $this );
2016-11-28 21:52:15 -08:00
$rest -> setAmzHeader ( 'x-amz-acl' , $acl );
2018-01-26 15:50:15 +01:00
if ( false === $location ) $location = $this -> getRegion ();
if ( false !== $location && 'us-east-1' !== $location ) {
2016-11-28 21:52:15 -08:00
$dom = new DOMDocument ;
$createBucketConfiguration = $dom -> createElement ( 'CreateBucketConfiguration' );
$locationConstraint = $dom -> createElement ( 'LocationConstraint' , $location );
$createBucketConfiguration -> appendChild ( $locationConstraint );
$dom -> appendChild ( $createBucketConfiguration );
$rest -> data = $dom -> saveXML ();
$rest -> size = strlen ( $rest -> data );
$rest -> setHeader ( 'Content-Type' , 'application/xml' );
}
$rest = $rest -> getResponse ();
2018-01-26 15:50:15 +01:00
if ( false === $rest -> error && 200 !== $rest -> code ) {
2016-11-28 21:52:15 -08:00
$rest -> error = array ( 'code' => $rest -> code , 'message' => 'Unexpected HTTP status' );
2018-01-26 15:50:15 +01:00
}
if ( false !== $rest -> error ) {
$this -> __triggerError ( sprintf ( " UpdraftPlus_S3::putBucket( { $bucket } , { $acl } , { $location } ): [%s] %s " ,
2016-11-28 21:52:15 -08:00
$rest -> error [ 'code' ], $rest -> error [ 'message' ]), __FILE__ , __LINE__ );
return false ;
}
return true ;
}
/**
2018-01-26 15:50:15 +01:00
* Delete an empty bucket
*
* @ param string $bucket Bucket name
*
* @ return boolean
*/
public function deleteBucket ( $bucket ) {
$rest = new UpdraftPlus_S3Request ( 'DELETE' , $bucket , '' , $this -> endpoint , $this -> use_dns_bucket_name , $this );
2016-11-28 21:52:15 -08:00
$rest = $rest -> getResponse ();
2018-01-26 15:50:15 +01:00
if ( false === $rest -> error && 204 !== $rest -> code ) {
2016-11-28 21:52:15 -08:00
$rest -> error = array ( 'code' => $rest -> code , 'message' => 'Unexpected HTTP status' );
2018-01-26 15:50:15 +01:00
}
if ( false !== $rest -> error ) {
$this -> __triggerError ( sprintf ( " UpdraftPlus_S3::deleteBucket( { $bucket } ): [%s] %s " ,
2016-11-28 21:52:15 -08:00
$rest -> error [ 'code' ], $rest -> error [ 'message' ]), __FILE__ , __LINE__ );
return false ;
}
return true ;
}
/**
2018-01-26 15:50:15 +01:00
* Create input info array for putObject ()
*
* @ param string $file Input file
* @ param mixed $md5sum Use MD5 hash ( supply a string if you want to use your own )
* @ return array | false
*/
public function inputFile ( $file , $md5sum = true ) {
if ( ! file_exists ( $file ) || ! is_file ( $file ) || ! is_readable ( $file )) {
$this -> __triggerError ( 'UpdraftPlus_S3::inputFile(): Unable to open input file: ' . $file , __FILE__ , __LINE__ );
2016-11-28 21:52:15 -08:00
return false ;
}
return array ( 'file' => $file , 'size' => filesize ( $file ), 'md5sum' => $md5sum !== false ?
2018-01-26 15:50:15 +01:00
( is_string ( $md5sum ) ? $md5sum : base64_encode ( md5_file ( $file , true ))) : '' , 'sha256sum' => hash_file ( 'sha256' , $file ));
2016-11-28 21:52:15 -08:00
}
/**
2018-01-26 15:50:15 +01:00
* Create input array info for putObject () with a resource
*
* @ param string $resource Input resource to read from
* @ param integer $bufferSize Input byte size
* @ param string $md5sum MD5 hash to send ( optional )
* @ return array | false
*/
public function inputResource ( & $resource , $bufferSize , $md5sum = '' ) {
if ( ! is_resource ( $resource ) || $bufferSize < 0 ) {
$this -> __triggerError ( 'UpdraftPlus_S3::inputResource(): Invalid resource or buffer size' , __FILE__ , __LINE__ );
2016-11-28 21:52:15 -08:00
return false ;
}
$input = array ( 'size' => $bufferSize , 'md5sum' => $md5sum );
$input [ 'fp' ] =& $resource ;
return $input ;
}
/**
2018-01-26 15:50:15 +01:00
* 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 string $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 string $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 ) {
$rest = new UpdraftPlus_S3Request ( 'POST' , $bucket , $uri , $this -> endpoint , $this -> use_dns_bucket_name , $this );
2016-11-28 21:52:15 -08:00
$rest -> setParameter ( 'uploads' , '' );
// Custom request headers (Content-Type, Content-Disposition, Content-Encoding)
if ( is_array ( $requestHeaders ))
foreach ( $requestHeaders as $h => $v ) $rest -> setHeader ( $h , $v );
// Set storage class
if ( $storageClass !== self :: STORAGE_CLASS_STANDARD ) // Storage class
$rest -> setAmzHeader ( 'x-amz-storage-class' , $storageClass );
// Set ACL headers
$rest -> setAmzHeader ( 'x-amz-acl' , $acl );
foreach ( $metaHeaders as $h => $v ) $rest -> setAmzHeader ( 'x-amz-meta-' . $h , $v );
// Carry out the HTTP operation
$rest -> getResponse ();
2018-01-26 15:50:15 +01:00
if ( false === $rest -> response -> error && 200 !== $rest -> response -> code ) {
2016-11-28 21:52:15 -08:00
$rest -> response -> error = array ( 'code' => $rest -> response -> code , 'message' => 'Unexpected HTTP status' );
2018-01-26 15:50:15 +01:00
}
if ( false !== $rest -> response -> error ) {
$this -> __triggerError ( sprintf ( " UpdraftPlus_S3::initiateMultipartUpload(): [%s] %s " ,
2016-11-28 21:52:15 -08:00
$rest -> response -> error [ 'code' ], $rest -> response -> error [ 'message' ]), __FILE__ , __LINE__ );
return false ;
2018-01-26 15:50:15 +01:00
} elseif ( isset ( $rest -> response -> body )) {
2016-11-28 21:52:15 -08:00
// DreamObjects already returns a SimpleXMLElement here. Not sure how that works.
if ( is_a ( $rest -> response -> body , 'SimpleXMLElement' )) {
$body = $rest -> response -> body ;
} else {
$body = new SimpleXMLElement ( $rest -> response -> body );
}
return ( string ) $body -> UploadId ;
}
// It is a programming error if we reach this line
return false ;
}
/**
2018-01-26 15:50:15 +01:00
* 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 5 Mb ( which is Amazon ' s minimum )
* @ return string ( ETag ) | false
*/
public function uploadPart ( $bucket , $uri , $uploadId , $filePath , $partNumber , $partSize = 5242880 ) {
$rest = new UpdraftPlus_S3Request ( 'PUT' , $bucket , $uri , $this -> endpoint , $this -> use_dns_bucket_name , $this );
2016-11-28 21:52:15 -08:00
$rest -> setParameter ( 'partNumber' , $partNumber );
$rest -> setParameter ( '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' );
$rest -> 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 );
$rest -> data = $rest -> data . $read ;
}
fclose ( $handle );
} else {
return false ;
}
$rest -> setHeader ( 'Content-MD5' , base64_encode ( md5 ( $rest -> data , true )));
$rest -> size = $bytes_read ;
$rest = $rest -> getResponse ();
2018-01-26 15:50:15 +01:00
if ( false === $rest -> error && 200 !== $rest -> code ) {
2016-11-28 21:52:15 -08:00
$rest -> error = array ( 'code' => $rest -> code , 'message' => 'Unexpected HTTP status' );
2018-01-26 15:50:15 +01:00
}
if ( false !== $rest -> error ) {
$this -> __triggerError ( sprintf ( " UpdraftPlus_S3::uploadPart(): [%s] %s " ,
2016-11-28 21:52:15 -08:00
$rest -> error [ 'code' ], $rest -> error [ 'message' ]), __FILE__ , __LINE__ );
return false ;
}
return $rest -> headers [ 'hash' ];
}
/**
2018-01-26 15:50:15 +01:00
* 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 ) {
$rest = new UpdraftPlus_S3Request ( 'POST' , $bucket , $uri , $this -> endpoint , $this -> use_dns_bucket_name , $this );
2016-11-28 21:52:15 -08:00
$rest -> setParameter ( 'uploadId' , $uploadId );
$xml = " <CompleteMultipartUpload> \n " ;
$partno = 1 ;
foreach ( $parts as $etag ) {
$xml .= " <Part><PartNumber> $partno </PartNumber><ETag> $etag </ETag></Part> \n " ;
$partno ++ ;
}
$xml .= " </CompleteMultipartUpload> " ;
$rest -> data = $xml ;
$rest -> size = strlen ( $rest -> data );
$rest -> setHeader ( 'Content-Type' , 'application/xml' );
$rest = $rest -> getResponse ();
2018-01-26 15:50:15 +01:00
if ( false === $rest -> error && 200 !== $rest -> code ) {
2016-11-28 21:52:15 -08:00
$rest -> error = array ( 'code' => $rest -> code , 'message' => 'Unexpected HTTP status' );
2018-01-26 15:50:15 +01:00
}
if ( false !== $rest -> error ) {
// Special case: when the error means "you've already done that". Turn it into success. See in: https://trello.com/c/6jJoiCG5
if ( 'InternalError' == $rest -> error [ 'code' ] && 'This multipart completion is already in progress' == $rest -> error [ 'message' ]) {
return true ;
}
$this -> __triggerError ( sprintf ( " UpdraftPlus_S3::completeMultipartUpload(): [%s] %s " ,
2016-11-28 21:52:15 -08:00
$rest -> error [ 'code' ], $rest -> error [ 'message' ]), __FILE__ , __LINE__ );
return false ;
}
return true ;
}
/**
2018-01-26 15:50:15 +01:00
* Abort a multi - part upload ( http :// docs . amazonwebservices . com / AmazonS3 / latest / API / mpUploadAbort . html )
*
* @ param string $bucket Bucket name
* @ param string $uri Object URI
* @ param string $uploadId uploadId returned previously from initiateMultipartUpload
* @ return boolean
*/
// TODO: From this line
public function abortMultipartUpload ( $bucket , $uri , $uploadId ) {
$rest = new UpdraftPlus_S3Request ( 'DELETE' , $bucket , $uri , $this -> endpoint , $this -> use_dns_bucket_name , $this );
2016-11-28 21:52:15 -08:00
$rest -> setParameter ( 'uploadId' , $uploadId );
$rest = $rest -> getResponse ();
2018-01-26 15:50:15 +01:00
if ( false === $rest -> error && 204 !== $rest -> code ) {
2016-11-28 21:52:15 -08:00
$rest -> error = array ( 'code' => $rest -> code , 'message' => 'Unexpected HTTP status' );
2018-01-26 15:50:15 +01:00
}
if ( false !== $rest -> error ) {
$this -> __triggerError ( sprintf ( " UpdraftPlus_S3::abortMultipartUpload(): [%s] %s " ,
2016-11-28 21:52:15 -08:00
$rest -> error [ 'code' ], $rest -> error [ 'message' ]), __FILE__ , __LINE__ );
return false ;
}
return true ;
}
/**
2018-01-26 15:50:15 +01:00
* Put an object
*
* @ param mixed $input Input data
* @ param string $bucket Bucket name
* @ param string $uri Object URI
* @ param string $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 string $storageClass Storage class constant
*
* @ return boolean
*/
public function putObject ( $input , $bucket , $uri , $acl = self :: ACL_PRIVATE , $metaHeaders = array (), $requestHeaders = array (), $storageClass = self :: STORAGE_CLASS_STANDARD ) {
2016-11-28 21:52:15 -08:00
if ( $input === false ) return false ;
2018-01-26 15:50:15 +01:00
$rest = new UpdraftPlus_S3Request ( 'PUT' , $bucket , $uri , $this -> endpoint , $this -> use_dns_bucket_name , $this );
2016-11-28 21:52:15 -08:00
if ( ! is_array ( $input )) $input = array (
'data' => $input , 'size' => strlen ( $input ),
2018-01-26 15:50:15 +01:00
'md5sum' => base64_encode ( md5 ( $input , true )),
'sha256sum' => hash ( 'sha256' , $input )
2016-11-28 21:52:15 -08:00
);
// Data
if ( isset ( $input [ 'fp' ]))
$rest -> fp =& $input [ 'fp' ];
elseif ( isset ( $input [ 'file' ]) && is_file ( $input [ 'file' ]))
$rest -> fp = @ fopen ( $input [ 'file' ], 'rb' );
elseif ( isset ( $input [ 'data' ]))
$rest -> data = $input [ 'data' ];
// Content-Length (required)
2018-01-26 15:50:15 +01:00
if ( isset ( $input [ 'size' ]) && $input [ 'size' ] >= 0 ) {
2016-11-28 21:52:15 -08:00
$rest -> size = $input [ 'size' ];
2018-01-26 15:50:15 +01:00
} else {
2016-11-28 21:52:15 -08:00
if ( isset ( $input [ 'file' ]))
$rest -> size = filesize ( $input [ 'file' ]);
elseif ( isset ( $input [ 'data' ]))
$rest -> size = strlen ( $input [ 'data' ]);
}
// Custom request headers (Content-Type, Content-Disposition, Content-Encoding)
if ( is_array ( $requestHeaders ))
foreach ( $requestHeaders as $h => $v ) $rest -> setHeader ( $h , $v );
elseif ( is_string ( $requestHeaders )) // Support for legacy contentType parameter
$input [ 'type' ] = $requestHeaders ;
// Content-Type
2018-01-26 15:50:15 +01:00
if ( ! isset ( $input [ 'type' ])) {
2016-11-28 21:52:15 -08:00
if ( isset ( $requestHeaders [ 'Content-Type' ]))
$input [ 'type' ] =& $requestHeaders [ 'Content-Type' ];
elseif ( isset ( $input [ 'file' ]))
2018-01-26 15:50:15 +01:00
$input [ 'type' ] = $this -> __getMimeType ( $input [ 'file' ]);
2016-11-28 21:52:15 -08:00
else
$input [ 'type' ] = 'application/octet-stream' ;
}
if ( $storageClass !== self :: STORAGE_CLASS_STANDARD ) // Storage class
$rest -> setAmzHeader ( 'x-amz-storage-class' , $storageClass );
2018-01-26 15:50:15 +01:00
if ( ! empty ( $this -> _serverSideEncryption )) {
$rest -> setAmzHeader ( 'x-amz-server-side-encryption' , $this -> _serverSideEncryption );
}
2016-11-28 21:52:15 -08:00
// We need to post with Content-Length and Content-Type, MD5 is optional
2018-01-26 15:50:15 +01:00
if ( $rest -> size >= 0 && ( false !== $rest -> fp || false !== $rest -> data )) {
2016-11-28 21:52:15 -08:00
$rest -> setHeader ( 'Content-Type' , $input [ 'type' ]);
if ( isset ( $input [ 'md5sum' ])) $rest -> setHeader ( 'Content-MD5' , $input [ 'md5sum' ]);
2018-01-26 15:50:15 +01:00
if ( isset ( $input [ 'sha256sum' ])) $rest -> setAmzHeader ( 'x-amz-content-sha256' , $input [ 'sha256sum' ]);
2016-11-28 21:52:15 -08:00
$rest -> setAmzHeader ( 'x-amz-acl' , $acl );
foreach ( $metaHeaders as $h => $v ) $rest -> setAmzHeader ( 'x-amz-meta-' . $h , $v );
$rest -> getResponse ();
2018-01-26 15:50:15 +01:00
} else {
2016-11-28 21:52:15 -08:00
$rest -> response -> error = array ( 'code' => 0 , 'message' => 'Missing input parameters' );
2018-01-26 15:50:15 +01:00
}
2016-11-28 21:52:15 -08:00
2018-01-26 15:50:15 +01:00
if ( false === $rest -> response -> error && 200 !== $rest -> response -> code ) {
2016-11-28 21:52:15 -08:00
$rest -> response -> error = array ( 'code' => $rest -> response -> code , 'message' => 'Unexpected HTTP status' );
2018-01-26 15:50:15 +01:00
}
if ( false !== $rest -> response -> error ) {
$this -> __triggerError ( sprintf ( " UpdraftPlus_S3::putObject(): [%s] %s " ,
2016-11-28 21:52:15 -08:00
$rest -> response -> error [ 'code' ], $rest -> response -> error [ 'message' ]), __FILE__ , __LINE__ );
return false ;
}
return true ;
}
/**
2018-01-26 15:50:15 +01:00
* 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 string $acl ACL constant
* @ param array $metaHeaders Array of x - amz - meta -* headers
* @ param string $contentType Content type
* @ param string $storageClass
*
* @ return boolean
*/
public function putObjectFile ( $file , $bucket , $uri , $acl = self :: ACL_PRIVATE , $metaHeaders = array (), $contentType = null , $storageClass = self :: STORAGE_CLASS_STANDARD ) {
return $this -> putObject ( $this -> inputFile ( $file ), $bucket , $uri , $acl , $metaHeaders , $contentType , $storageClass );
2016-11-28 21:52:15 -08:00
}
/**
2018-01-26 15:50:15 +01:00
* Put an object from a string ( legacy function )
*
* @ param string $string Input data
* @ param string $bucket Bucket name
* @ param string $uri Object URI
* @ param string $acl ACL constant
* @ param array $metaHeaders Array of x - amz - meta -* headers
* @ param string $contentType Content type
* @ return boolean
*/
public function putObjectString ( $string , $bucket , $uri , $acl = self :: ACL_PRIVATE , $metaHeaders = array (), $contentType = 'text/plain' ) {
return $this -> putObject ( $string , $bucket , $uri , $acl , $metaHeaders , $contentType );
2016-11-28 21:52:15 -08:00
}
/**
2018-01-26 15:50:15 +01:00
* 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 ) {
$rest = new UpdraftPlus_S3Request ( 'GET' , $bucket , $uri , $this -> endpoint , $this -> use_dns_bucket_name , $this );
if ( false !== $saveTo ) {
2016-11-28 21:52:15 -08:00
if ( is_resource ( $saveTo )) {
$rest -> fp = $saveTo ;
if ( ! is_bool ( $resume )) $rest -> setHeader ( 'Range' , $resume );
} else {
if ( $resume && file_exists ( $saveTo )) {
2018-01-26 15:50:15 +01:00
if ( false !== ( $rest -> fp = @ fopen ( $saveTo , 'ab' ))) {
2016-11-28 21:52:15 -08:00
$rest -> setHeader ( 'Range' , " bytes= " . filesize ( $saveTo ) . '-' );
$rest -> file = realpath ( $saveTo );
} else {
$rest -> response -> error = array ( 'code' => 0 , 'message' => 'Unable to open save file for writing: ' . $saveTo );
}
} else {
2018-01-26 15:50:15 +01:00
if ( false !== ( $rest -> fp = @ fopen ( $saveTo , 'wb' )))
2016-11-28 21:52:15 -08:00
$rest -> file = realpath ( $saveTo );
else
$rest -> response -> error = array ( 'code' => 0 , 'message' => 'Unable to open save file for writing: ' . $saveTo );
}
}
}
2018-01-26 15:50:15 +01:00
if ( false === $rest -> response -> error ) $rest -> getResponse ();
2016-11-28 21:52:15 -08:00
2018-01-26 15:50:15 +01:00
if ( false === $rest -> response -> error && ( ! $resume && 200 != $rest -> response -> code ) || ( $resume && 206 != $rest -> response -> code && 200 != $rest -> response -> code ))
2016-11-28 21:52:15 -08:00
$rest -> response -> error = array ( 'code' => $rest -> response -> code , 'message' => 'Unexpected HTTP status' );
2018-01-26 15:50:15 +01:00
if ( false !== $rest -> response -> error ) {
$this -> __triggerError ( sprintf ( " UpdraftPlus_S3::getObject( { $bucket } , { $uri } ): [%s] %s " ,
2016-11-28 21:52:15 -08:00
$rest -> response -> error [ 'code' ], $rest -> response -> error [ 'message' ]), __FILE__ , __LINE__ );
return false ;
}
return $rest -> response ;
}
/**
2018-01-26 15:50:15 +01:00
* Get object information
*
* @ param string $bucket Bucket name
* @ param string $uri Object URI
* @ param boolean $returnInfo Return response information
*
* @ return mixed | false
*/
public function getObjectInfo ( $bucket , $uri , $returnInfo = true ) {
$rest = new UpdraftPlus_S3Request ( 'HEAD' , $bucket , $uri , $this -> endpoint , $this -> use_dns_bucket_name , $this );
2016-11-28 21:52:15 -08:00
$rest = $rest -> getResponse ();
2018-01-26 15:50:15 +01:00
if ( false === $rest -> error && ( 200 !== $rest -> code && 404 !== $rest -> code ))
2016-11-28 21:52:15 -08:00
$rest -> error = array ( 'code' => $rest -> code , 'message' => 'Unexpected HTTP status' );
2018-01-26 15:50:15 +01:00
if ( false !== $rest -> error ) {
$this -> __triggerError ( sprintf ( " UpdraftPlus_S3::getObjectInfo( { $bucket } , { $uri } ): [%s] %s " ,
2016-11-28 21:52:15 -08:00
$rest -> error [ 'code' ], $rest -> error [ 'message' ]), __FILE__ , __LINE__ );
return false ;
}
2018-01-26 15:50:15 +01:00
return ( 200 == $rest -> code ) ? ( $returnInfo ? $rest -> headers : true ) : false ;
2016-11-28 21:52:15 -08:00
}
/**
2018-01-26 15:50:15 +01:00
* Copy an object
*
* @ param string $bucket Source bucket name
* @ param string $uri Source object URI
* @ param string $bucket Destination bucket name
* @ param string $uri Destination object URI
* @ param string $acl ACL constant
* @ param array $metaHeaders Optional array of x - amz - meta -* headers
* @ param array $requestHeaders Optional array of request headers ( content type , disposition , etc . )
* @ param string $storageClass Storage class constant
*
* @ return mixed | false
*/
public function copyObject ( $srcBucket , $srcUri , $bucket , $uri , $acl = self :: ACL_PRIVATE , $metaHeaders = array (), $requestHeaders = array (), $storageClass = self :: STORAGE_CLASS_STANDARD ) {
$rest = new UpdraftPlus_S3Request ( 'PUT' , $bucket , $uri , $this -> endpoint , $this -> use_dns_bucket_name , $this );
2016-11-28 21:52:15 -08:00
$rest -> setHeader ( 'Content-Length' , 0 );
foreach ( $requestHeaders as $h => $v ) $rest -> setHeader ( $h , $v );
foreach ( $metaHeaders as $h => $v ) $rest -> setAmzHeader ( 'x-amz-meta-' . $h , $v );
2018-01-26 15:50:15 +01:00
if ( self :: STORAGE_CLASS_STANDARD !== $storageClass ) // Storage class
2016-11-28 21:52:15 -08:00
$rest -> setAmzHeader ( 'x-amz-storage-class' , $storageClass );
$rest -> setAmzHeader ( 'x-amz-acl' , $acl );
$rest -> setAmzHeader ( 'x-amz-copy-source' , sprintf ( '/%s/%s' , $srcBucket , rawurlencode ( $srcUri )));
if ( sizeof ( $requestHeaders ) > 0 || sizeof ( $metaHeaders ) > 0 )
$rest -> setAmzHeader ( 'x-amz-metadata-directive' , 'REPLACE' );
$rest = $rest -> getResponse ();
2018-01-26 15:50:15 +01:00
if ( false === $rest -> error && 200 !== $rest -> code ) {
2016-11-28 21:52:15 -08:00
$rest -> error = array ( 'code' => $rest -> code , 'message' => 'Unexpected HTTP status' );
2018-01-26 15:50:15 +01:00
}
if ( false !== $rest -> error ) {
$this -> __triggerError ( sprintf ( " UpdraftPlus_S3::copyObject( { $srcBucket } , { $srcUri } , { $bucket } , { $uri } ): [%s] %s " ,
2016-11-28 21:52:15 -08:00
$rest -> error [ 'code' ], $rest -> error [ 'message' ]), __FILE__ , __LINE__ );
return false ;
}
return isset ( $rest -> body -> LastModified , $rest -> body -> ETag ) ? array (
'time' => strtotime (( string ) $rest -> body -> LastModified ),
'hash' => substr (( string ) $rest -> body -> ETag , 1 , - 1 )
) : false ;
}
/**
2018-01-26 15:50:15 +01:00
* Set logging for a bucket
*
* @ param string $bucket Bucket name
* @ param string $targetBucket Target bucket ( where logs are stored )
* @ param string $targetPrefix Log prefix ( e , g ; domain . com - )
*
* @ return boolean
*/
public function setBucketLogging ( $bucket , $targetBucket , $targetPrefix = null ) {
2016-11-28 21:52:15 -08:00
// The S3 log delivery group has to be added to the target bucket's ACP
2018-01-26 15:50:15 +01:00
if ( null !== $targetBucket && false !== ( $acp = $this -> getAccessControlPolicy ( $targetBucket , '' ))) {
2016-11-28 21:52:15 -08:00
// Only add permissions to the target bucket when they do not exist
$aclWriteSet = false ;
$aclReadSet = false ;
foreach ( $acp [ 'acl' ] as $acl )
2018-01-26 15:50:15 +01:00
if ( 'Group' == $acl [ 'type' ] && 'http://acs.amazonaws.com/groups/s3/LogDelivery' == $acl [ 'uri' ]) {
2016-11-28 21:52:15 -08:00
if ( $acl [ 'permission' ] == 'WRITE' ) $aclWriteSet = true ;
elseif ( $acl [ 'permission' ] == 'READ_ACP' ) $aclReadSet = true ;
}
if ( ! $aclWriteSet ) $acp [ 'acl' ][] = array (
'type' => 'Group' , 'uri' => 'http://acs.amazonaws.com/groups/s3/LogDelivery' , 'permission' => 'WRITE'
);
if ( ! $aclReadSet ) $acp [ 'acl' ][] = array (
'type' => 'Group' , 'uri' => 'http://acs.amazonaws.com/groups/s3/LogDelivery' , 'permission' => 'READ_ACP'
);
2018-01-26 15:50:15 +01:00
if ( ! $aclReadSet || ! $aclWriteSet ) $this -> setAccessControlPolicy ( $targetBucket , '' , $acp );
2016-11-28 21:52:15 -08:00
}
$dom = new DOMDocument ;
$bucketLoggingStatus = $dom -> createElement ( 'BucketLoggingStatus' );
$bucketLoggingStatus -> setAttribute ( 'xmlns' , 'http://s3.amazonaws.com/doc/2006-03-01/' );
2018-01-26 15:50:15 +01:00
if ( null !== $targetBucket ) {
if ( null == $targetPrefix ) $targetPrefix = $bucket . '-' ;
2016-11-28 21:52:15 -08:00
$loggingEnabled = $dom -> createElement ( 'LoggingEnabled' );
$loggingEnabled -> appendChild ( $dom -> createElement ( 'TargetBucket' , $targetBucket ));
$loggingEnabled -> appendChild ( $dom -> createElement ( 'TargetPrefix' , $targetPrefix ));
// TODO: Add TargetGrants?
$bucketLoggingStatus -> appendChild ( $loggingEnabled );
}
$dom -> appendChild ( $bucketLoggingStatus );
2018-01-26 15:50:15 +01:00
$rest = new UpdraftPlus_S3Request ( 'PUT' , $bucket , '' , $this -> endpoint , $this -> use_dns_bucket_name , $this );
2016-11-28 21:52:15 -08:00
$rest -> setParameter ( 'logging' , null );
$rest -> data = $dom -> saveXML ();
$rest -> size = strlen ( $rest -> data );
$rest -> setHeader ( 'Content-Type' , 'application/xml' );
$rest = $rest -> getResponse ();
2018-01-26 15:50:15 +01:00
if ( false === $rest -> error && 200 !== $rest -> code ) {
2016-11-28 21:52:15 -08:00
$rest -> error = array ( 'code' => $rest -> code , 'message' => 'Unexpected HTTP status' );
2018-01-26 15:50:15 +01:00
}
if ( false !== $rest -> error ) {
$this -> __triggerError ( sprintf ( " UpdraftPlus_S3::setBucketLogging( { $bucket } , { $targetBucket } ): [%s] %s " ,
2016-11-28 21:52:15 -08:00
$rest -> error [ 'code' ], $rest -> error [ 'message' ]), __FILE__ , __LINE__ );
return false ;
}
return true ;
}
/**
2018-01-26 15:50:15 +01:00
* Get logging status for a bucket
*
* This will return false if logging is not enabled .
* Note : To enable logging , you also need to grant write access to the log group
*
* @ param string $bucket Bucket name
*
* @ return array | false
*/
public function getBucketLogging ( $bucket ) {
$rest = new UpdraftPlus_S3Request ( 'GET' , $bucket , '' , $this -> endpoint , $this -> use_dns_bucket_name , $this );
2016-11-28 21:52:15 -08:00
$rest -> setParameter ( 'logging' , null );
$rest = $rest -> getResponse ();
2018-01-26 15:50:15 +01:00
if ( false === $rest -> error && 200 !== $rest -> code ) {
2016-11-28 21:52:15 -08:00
$rest -> error = array ( 'code' => $rest -> code , 'message' => 'Unexpected HTTP status' );
2018-01-26 15:50:15 +01:00
}
if ( false !== $rest -> error ) {
$this -> __triggerError ( sprintf ( " UpdraftPlus_S3::getBucketLogging( { $bucket } ): [%s] %s " ,
2016-11-28 21:52:15 -08:00
$rest -> error [ 'code' ], $rest -> error [ 'message' ]), __FILE__ , __LINE__ );
return false ;
}
if ( ! isset ( $rest -> body -> LoggingEnabled )) return false ; // No logging
return array (
'targetBucket' => ( string ) $rest -> body -> LoggingEnabled -> TargetBucket ,
'targetPrefix' => ( string ) $rest -> body -> LoggingEnabled -> TargetPrefix ,
);
}
/**
2018-01-26 15:50:15 +01:00
* Disable bucket logging
*
* @ param string $bucket Bucket name
*
* @ return boolean
*/
public function disableBucketLogging ( $bucket ) {
return $this -> setBucketLogging ( $bucket , null );
2016-11-28 21:52:15 -08:00
}
/**
2018-01-26 15:50:15 +01:00
* Get a bucket ' s location
*
* @ param string $bucket Bucket name
*
* @ return string | false
*/
public function getBucketLocation ( $bucket ) {
$rest = new UpdraftPlus_S3Request ( 'GET' , $bucket , '' , $this -> endpoint , $this -> use_dns_bucket_name , $this );
2016-11-28 21:52:15 -08:00
$rest -> setParameter ( 'location' , null );
$rest = $rest -> getResponse ();
2018-01-26 15:50:15 +01:00
if ( false === $rest -> error && 200 !== $rest -> code ) {
2016-11-28 21:52:15 -08:00
$rest -> error = array ( 'code' => $rest -> code , 'message' => 'Unexpected HTTP status' );
2018-01-26 15:50:15 +01:00
}
if ( false !== $rest -> error ) {
$this -> __triggerError ( sprintf ( " UpdraftPlus_S3::getBucketLocation( { $bucket } ): [%s] %s " ,
2016-11-28 21:52:15 -08:00
$rest -> error [ 'code' ], $rest -> error [ 'message' ]), __FILE__ , __LINE__ );
return false ;
}
return ( isset ( $rest -> body [ 0 ]) && ( string ) $rest -> body [ 0 ] !== '' ) ? ( string ) $rest -> body [ 0 ] : 'US' ;
}
/**
2018-01-26 15:50:15 +01:00
* Set object or bucket Access Control Policy
*
* @ param string $bucket Bucket name
* @ param string $uri Object URI
* @ param array $acp Access Control Policy Data ( same as the data returned from getAccessControlPolicy )
*
* @ return boolean
*/
public function setAccessControlPolicy ( $bucket , $uri = '' , $acp = array ()) {
2016-11-28 21:52:15 -08:00
$dom = new DOMDocument ;
$dom -> formatOutput = true ;
$accessControlPolicy = $dom -> createElement ( 'AccessControlPolicy' );
$accessControlList = $dom -> createElement ( 'AccessControlList' );
// It seems the owner has to be passed along too
$owner = $dom -> createElement ( 'Owner' );
$owner -> appendChild ( $dom -> createElement ( 'ID' , $acp [ 'owner' ][ 'id' ]));
$owner -> appendChild ( $dom -> createElement ( 'DisplayName' , $acp [ 'owner' ][ 'name' ]));
$accessControlPolicy -> appendChild ( $owner );
2018-01-26 15:50:15 +01:00
foreach ( $acp [ 'acl' ] as $g ) {
2016-11-28 21:52:15 -08:00
$grant = $dom -> createElement ( 'Grant' );
$grantee = $dom -> createElement ( 'Grantee' );
$grantee -> setAttribute ( 'xmlns:xsi' , 'http://www.w3.org/2001/XMLSchema-instance' );
2018-01-26 15:50:15 +01:00
if ( isset ( $g [ 'id' ])) {
// CanonicalUser (DisplayName is omitted)
2016-11-28 21:52:15 -08:00
$grantee -> setAttribute ( 'xsi:type' , 'CanonicalUser' );
$grantee -> appendChild ( $dom -> createElement ( 'ID' , $g [ 'id' ]));
2018-01-26 15:50:15 +01:00
} elseif ( isset ( $g [ 'email' ])) {
// AmazonCustomerByEmail
2016-11-28 21:52:15 -08:00
$grantee -> setAttribute ( 'xsi:type' , 'AmazonCustomerByEmail' );
$grantee -> appendChild ( $dom -> createElement ( 'EmailAddress' , $g [ 'email' ]));
2018-01-26 15:50:15 +01:00
} elseif ( 'Group' == $g [ 'type' ]) {
// Group
2016-11-28 21:52:15 -08:00
$grantee -> setAttribute ( 'xsi:type' , 'Group' );
$grantee -> appendChild ( $dom -> createElement ( 'URI' , $g [ 'uri' ]));
}
$grant -> appendChild ( $grantee );
$grant -> appendChild ( $dom -> createElement ( 'Permission' , $g [ 'permission' ]));
$accessControlList -> appendChild ( $grant );
}
$accessControlPolicy -> appendChild ( $accessControlList );
$dom -> appendChild ( $accessControlPolicy );
2018-01-26 15:50:15 +01:00
$rest = new UpdraftPlus_S3Request ( 'PUT' , $bucket , $uri , $this -> endpoint , $this -> use_dns_bucket_name , $this );
2016-11-28 21:52:15 -08:00
$rest -> setParameter ( 'acl' , null );
$rest -> data = $dom -> saveXML ();
$rest -> size = strlen ( $rest -> data );
$rest -> setHeader ( 'Content-Type' , 'application/xml' );
$rest = $rest -> getResponse ();
2018-01-26 15:50:15 +01:00
if ( false === $rest -> error && 200 !== $rest -> code ) {
2016-11-28 21:52:15 -08:00
$rest -> error = array ( 'code' => $rest -> code , 'message' => 'Unexpected HTTP status' );
2018-01-26 15:50:15 +01:00
}
if ( false !== $rest -> error ) {
$this -> __triggerError ( sprintf ( " UpdraftPlus_S3::setAccessControlPolicy( { $bucket } , { $uri } ): [%s] %s " ,
2016-11-28 21:52:15 -08:00
$rest -> error [ 'code' ], $rest -> error [ 'message' ]), __FILE__ , __LINE__ );
return false ;
}
return true ;
}
/**
2018-01-26 15:50:15 +01:00
* Get object or bucket Access Control Policy
*
* @ param string $bucket Bucket name
* @ param string $uri Object URI
* @ return mixed | false
*/
public function getAccessControlPolicy ( $bucket , $uri = '' ) {
$rest = new UpdraftPlus_S3Request ( 'GET' , $bucket , $uri , $this -> endpoint , $this -> use_dns_bucket_name , $this );
2016-11-28 21:52:15 -08:00
$rest -> setParameter ( 'acl' , null );
$rest = $rest -> getResponse ();
2018-01-26 15:50:15 +01:00
if ( false === $rest -> error && 200 !== $rest -> code ) {
2016-11-28 21:52:15 -08:00
$rest -> error = array ( 'code' => $rest -> code , 'message' => 'Unexpected HTTP status' );
2018-01-26 15:50:15 +01:00
}
if ( false !== $rest -> error ) {
$this -> __triggerError ( sprintf ( " UpdraftPlus_S3::getAccessControlPolicy( { $bucket } , { $uri } ): [%s] %s " ,
2016-11-28 21:52:15 -08:00
$rest -> error [ 'code' ], $rest -> error [ 'message' ]), __FILE__ , __LINE__ );
return false ;
}
$acp = array ();
if ( isset ( $rest -> body -> Owner , $rest -> body -> Owner -> ID , $rest -> body -> Owner -> DisplayName ))
$acp [ 'owner' ] = array (
'id' => ( string ) $rest -> body -> Owner -> ID , 'name' => ( string ) $rest -> body -> Owner -> DisplayName
);
2018-01-26 15:50:15 +01:00
if ( isset ( $rest -> body -> AccessControlList )) {
2016-11-28 21:52:15 -08:00
$acp [ 'acl' ] = array ();
2018-01-26 15:50:15 +01:00
foreach ( $rest -> body -> AccessControlList -> Grant as $grant ) {
foreach ( $grant -> Grantee as $grantee ) {
2016-11-28 21:52:15 -08:00
if ( isset ( $grantee -> ID , $grantee -> DisplayName )) // CanonicalUser
$acp [ 'acl' ][] = array (
'type' => 'CanonicalUser' ,
'id' => ( string ) $grantee -> ID ,
'name' => ( string ) $grantee -> DisplayName ,
'permission' => ( string ) $grant -> Permission
);
elseif ( isset ( $grantee -> EmailAddress )) // AmazonCustomerByEmail
$acp [ 'acl' ][] = array (
'type' => 'AmazonCustomerByEmail' ,
'email' => ( string ) $grantee -> EmailAddress ,
'permission' => ( string ) $grant -> Permission
);
elseif ( isset ( $grantee -> URI )) // Group
$acp [ 'acl' ][] = array (
'type' => 'Group' ,
'uri' => ( string ) $grantee -> URI ,
'permission' => ( string ) $grant -> Permission
);
else continue ;
}
}
}
return $acp ;
}
/**
2018-01-26 15:50:15 +01:00
* Delete an object
*
* @ param string $bucket Bucket name
* @ param string $uri Object URI
*
* @ return boolean
*/
public function deleteObject ( $bucket , $uri ) {
$rest = new UpdraftPlus_S3Request ( 'DELETE' , $bucket , $uri , $this -> endpoint , $this -> use_dns_bucket_name , $this );
2016-11-28 21:52:15 -08:00
$rest = $rest -> getResponse ();
2018-01-26 15:50:15 +01:00
if ( false === $rest -> error && 204 !== $rest -> code ) {
2016-11-28 21:52:15 -08:00
$rest -> error = array ( 'code' => $rest -> code , 'message' => 'Unexpected HTTP status' );
2018-01-26 15:50:15 +01:00
}
if ( false !== $rest -> error ) {
$this -> __triggerError ( sprintf ( " UpdraftPlus_S3::deleteObject(): [%s] %s " ,
2016-11-28 21:52:15 -08:00
$rest -> error [ 'code' ], $rest -> error [ 'message' ]), __FILE__ , __LINE__ );
return false ;
}
return true ;
}
/**
2018-01-26 15:50:15 +01:00
* Get a query string authenticated URL
*
* @ param string $bucket Bucket name
* @ param string $uri Object URI
* @ param integer $lifetime Lifetime in seconds
* @ param boolean $hostBucket Use the bucket name as the hostname
* @ param boolean $https Use HTTPS ( $hostBucket should be false for SSL verification )
*
* @ return string
*/
public function getAuthenticatedURL ( $bucket , $uri , $lifetime , $hostBucket = false , $https = false ) {
2016-11-28 21:52:15 -08:00
$expires = time () + $lifetime ;
$uri = str_replace ( array ( '%2F' , '%2B' ), array ( '/' , '+' ), rawurlencode ( $uri ));
return sprintf (( $https ? 'https' : 'http' ) . '://%s/%s?AWSAccessKeyId=%s&Expires=%u&Signature=%s' ,
2018-01-26 15:50:15 +01:00
// $hostBucket ? $bucket : $bucket.'.s3.amazonaws.com', $uri, $this->__accessKey, $expires,
$hostBucket ? $bucket : 's3.amazonaws.com/' . $bucket , $uri , $this -> __accessKey , $expires ,
urlencode ( $this -> __getHash ( " GET \n \n \n { $expires } \n / { $bucket } / { $uri } " )));
2016-11-28 21:52:15 -08:00
}
/**
2018-01-26 15:50:15 +01:00
* Get a CloudFront signed policy URL
*
* @ param array $policy Policy
*
* @ return string
*/
public function getSignedPolicyURL ( $policy ) {
2016-11-28 21:52:15 -08:00
$data = json_encode ( $policy );
$signature = '' ;
2018-01-26 15:50:15 +01:00
if ( ! openssl_sign ( $data , $signature , $this -> __signingKeyResource )) return false ;
2016-11-28 21:52:15 -08:00
$encoded = str_replace ( array ( '+' , '=' ), array ( '-' , '_' , '~' ), base64_encode ( $data ));
$signature = str_replace ( array ( '+' , '=' ), array ( '-' , '_' , '~' ), base64_encode ( $signature ));
$url = $policy [ 'Statement' ][ 0 ][ 'Resource' ] . '?' ;
2018-01-26 15:50:15 +01:00
foreach ( array ( 'Policy' => $encoded , 'Signature' => $signature , 'Key-Pair-Id' => $this -> __signingKeyPairId ) as $k => $v )
2016-11-28 21:52:15 -08:00
$url .= $k . '=' . str_replace ( '%2F' , '/' , rawurlencode ( $v )) . '&' ;
return substr ( $url , 0 , - 1 );
}
/**
2018-01-26 15:50:15 +01:00
* Get a CloudFront canned policy URL
*
* @ param string $url URL to sign
* @ param integer $lifetime URL lifetime
*
* @ return string
*/
public function getSignedCannedURL ( $url , $lifetime ) {
return $this -> getSignedPolicyURL ( array (
2016-11-28 21:52:15 -08:00
'Statement' => array (
array ( 'Resource' => $url , 'Condition' => array (
'DateLessThan' => array ( 'AWS:EpochTime' => time () + $lifetime )
))
)
));
}
/**
2018-01-26 15:50:15 +01:00
* Get upload POST parameters for form uploads
*
* @ param string $bucket Bucket name
* @ param string $uriPrefix Object URI prefix
* @ param string $acl ACL constant
* @ param integer $lifetime Lifetime in seconds
* @ param integer $maxFileSize Maximum file size in bytes ( default 5 MB )
* @ param string $successRedirect Redirect URL or 200 / 201 status code
* @ param array $amzHeaders Array of x - amz - meta -* headers
* @ param array $headers Array of request headers or content type as a string
* @ param boolean $flashVars Includes additional " Filename " variable posted by Flash
*
* @ return object
*/
public function getHttpUploadPostParams ( $bucket , $uriPrefix = '' , $acl = self :: ACL_PRIVATE , $lifetime = 3600 ,
$maxFileSize = 5242880 , $successRedirect = " 201 " , $amzHeaders = array (), $headers = array (), $flashVars = false ) {
2016-11-28 21:52:15 -08:00
// Create policy object
$policy = new stdClass ;
$policy -> expiration = gmdate ( 'Y-m-d\TH:i:s\Z' , ( time () + $lifetime ));
$policy -> conditions = array ();
$obj = new stdClass ; $obj -> bucket = $bucket ; array_push ( $policy -> conditions , $obj );
$obj = new stdClass ; $obj -> acl = $acl ; array_push ( $policy -> conditions , $obj );
$obj = new stdClass ; // 200 for non-redirect uploads
if ( is_numeric ( $successRedirect ) && in_array (( int ) $successRedirect , array ( 200 , 201 )))
$obj -> success_action_status = ( string ) $successRedirect ;
else // URL
$obj -> success_action_redirect = $successRedirect ;
array_push ( $policy -> conditions , $obj );
2018-01-26 15:50:15 +01:00
if ( self :: ACL_PUBLIC_READ !== $acl )
2016-11-28 21:52:15 -08:00
array_push ( $policy -> conditions , array ( 'eq' , '$acl' , $acl ));
array_push ( $policy -> conditions , array ( 'starts-with' , '$key' , $uriPrefix ));
if ( $flashVars ) array_push ( $policy -> conditions , array ( 'starts-with' , '$Filename' , '' ));
foreach ( array_keys ( $headers ) as $headerKey )
array_push ( $policy -> conditions , array ( 'starts-with' , '$' . $headerKey , '' ));
2018-01-26 15:50:15 +01:00
foreach ( $amzHeaders as $headerKey => $headerVal ) {
2016-11-28 21:52:15 -08:00
$obj = new stdClass ;
$obj -> { $headerKey } = ( string ) $headerVal ;
array_push ( $policy -> conditions , $obj );
}
array_push ( $policy -> conditions , array ( 'content-length-range' , 0 , $maxFileSize ));
$policy = base64_encode ( str_replace ( '\/' , '/' , json_encode ( $policy )));
// Create parameters
$params = new stdClass ;
2018-01-26 15:50:15 +01:00
$params -> AWSAccessKeyId = $this -> __accessKey ;
2016-11-28 21:52:15 -08:00
$params -> key = $uriPrefix . '${filename}' ;
$params -> acl = $acl ;
$params -> policy = $policy ; unset ( $policy );
2018-01-26 15:50:15 +01:00
$params -> signature = $this -> __getHash ( $params -> policy );
2016-11-28 21:52:15 -08:00
if ( is_numeric ( $successRedirect ) && in_array (( int ) $successRedirect , array ( 200 , 201 )))
$params -> success_action_status = ( string ) $successRedirect ;
else
$params -> success_action_redirect = $successRedirect ;
foreach ( $headers as $headerKey => $headerVal ) $params -> { $headerKey } = ( string ) $headerVal ;
foreach ( $amzHeaders as $headerKey => $headerVal ) $params -> { $headerKey } = ( string ) $headerVal ;
return $params ;
}
/**
2018-01-26 15:50:15 +01:00
* Create a CloudFront distribution
*
* @ param string $bucket Bucket name
* @ param boolean $enabled Enabled ( true / false )
* @ param array $cnames Array containing CNAME aliases
* @ param string $comment Use the bucket name as the hostname
* @ param string $defaultRootObject Default root object
* @ param string $originAccessIdentity Origin access identity
* @ param array $trustedSigners Array of trusted signers
*
* @ return array | false
*/
public function createDistribution ( $bucket , $enabled = true , $cnames = array (), $comment = null , $defaultRootObject = null , $originAccessIdentity = null , $trustedSigners = array ()) {
if ( ! extension_loaded ( 'openssl' )) {
$this -> __triggerError ( sprintf ( " UpdraftPlus_S3::createDistribution( { $bucket } , " . ( int ) $enabled . " , [], ' $comment '): %s " ,
2016-11-28 21:52:15 -08:00
" CloudFront functionality requires SSL " ), __FILE__ , __LINE__ );
return false ;
}
2018-01-26 15:50:15 +01:00
$useSSL = $this -> useSSL ;
2016-11-28 21:52:15 -08:00
2018-01-26 15:50:15 +01:00
$this -> useSSL = true ; // CloudFront requires SSL
2016-11-28 21:52:15 -08:00
$rest = new UpdraftPlus_S3Request ( 'POST' , '' , '2010-11-01/distribution' , 'cloudfront.amazonaws.com' );
2018-01-26 15:50:15 +01:00
$rest -> data = $this -> __getCloudFrontDistributionConfigXML (
2016-11-28 21:52:15 -08:00
$bucket . '.s3.amazonaws.com' ,
$enabled ,
( string ) $comment ,
( string ) microtime ( true ),
$cnames ,
$defaultRootObject ,
$originAccessIdentity ,
$trustedSigners
);
$rest -> size = strlen ( $rest -> data );
$rest -> setHeader ( 'Content-Type' , 'application/xml' );
2018-01-26 15:50:15 +01:00
$rest = $this -> __getCloudFrontResponse ( $rest );
2016-11-28 21:52:15 -08:00
2018-01-26 15:50:15 +01:00
$this -> useSSL = $useSSL ;
2016-11-28 21:52:15 -08:00
2018-01-26 15:50:15 +01:00
if ( false === $rest -> error && 201 !== $rest -> code ) {
2016-11-28 21:52:15 -08:00
$rest -> error = array ( 'code' => $rest -> code , 'message' => 'Unexpected HTTP status' );
2018-01-26 15:50:15 +01:00
}
if ( false !== $rest -> error ) {
$this -> __triggerError ( sprintf ( " UpdraftPlus_S3::createDistribution( { $bucket } , " . ( int ) $enabled . " , [], ' $comment '): [%s] %s " ,
2016-11-28 21:52:15 -08:00
$rest -> error [ 'code' ], $rest -> error [ 'message' ]), __FILE__ , __LINE__ );
return false ;
2018-01-26 15:50:15 +01:00
} elseif ( $rest -> body instanceof SimpleXMLElement ) {
return $this -> __parseCloudFrontDistributionConfig ( $rest -> body );
}
2016-11-28 21:52:15 -08:00
return false ;
}
/**
2018-01-26 15:50:15 +01:00
* Get CloudFront distribution info
*
* @ param string $distributionId Distribution ID from listDistributions ()
* @ return array | false
*/
public function getDistribution ( $distributionId ) {
if ( ! extension_loaded ( 'openssl' )) {
$this -> __triggerError ( sprintf ( " UpdraftPlus_S3::getDistribution( $distributionId ): %s " ,
2016-11-28 21:52:15 -08:00
" CloudFront functionality requires SSL " ), __FILE__ , __LINE__ );
return false ;
}
2018-01-26 15:50:15 +01:00
$useSSL = $this -> useSSL ;
2016-11-28 21:52:15 -08:00
2018-01-26 15:50:15 +01:00
$this -> useSSL = true ; // CloudFront requires SSL
2016-11-28 21:52:15 -08:00
$rest = new UpdraftPlus_S3Request ( 'GET' , '' , '2010-11-01/distribution/' . $distributionId , 'cloudfront.amazonaws.com' );
2018-01-26 15:50:15 +01:00
$rest = $this -> __getCloudFrontResponse ( $rest );
2016-11-28 21:52:15 -08:00
2018-01-26 15:50:15 +01:00
$this -> useSSL = $useSSL ;
2016-11-28 21:52:15 -08:00
2018-01-26 15:50:15 +01:00
if ( false === $rest -> error && 200 !== $rest -> code ) {
2016-11-28 21:52:15 -08:00
$rest -> error = array ( 'code' => $rest -> code , 'message' => 'Unexpected HTTP status' );
2018-01-26 15:50:15 +01:00
}
if ( false !== $rest -> error ) {
$this -> __triggerError ( sprintf ( " UpdraftPlus_S3::getDistribution( $distributionId ): [%s] %s " ,
2016-11-28 21:52:15 -08:00
$rest -> error [ 'code' ], $rest -> error [ 'message' ]), __FILE__ , __LINE__ );
return false ;
2018-01-26 15:50:15 +01:00
} elseif ( $rest -> body instanceof SimpleXMLElement ) {
$dist = $this -> __parseCloudFrontDistributionConfig ( $rest -> body );
2016-11-28 21:52:15 -08:00
$dist [ 'hash' ] = $rest -> headers [ 'hash' ];
$dist [ 'id' ] = $distributionId ;
return $dist ;
}
return false ;
}
/**
2018-01-26 15:50:15 +01:00
* Update a CloudFront distribution
*
* @ param array $dist Distribution array info identical to output of getDistribution ()
*
* @ return array | false
*/
public function updateDistribution ( $dist ) {
if ( ! extension_loaded ( 'openssl' )) {
$this -> __triggerError ( sprintf ( " UpdraftPlus_S3::updateDistribution( { $dist [ 'id' ] } ): %s " ,
2016-11-28 21:52:15 -08:00
" CloudFront functionality requires SSL " ), __FILE__ , __LINE__ );
return false ;
}
2018-01-26 15:50:15 +01:00
$useSSL = $this -> useSSL ;
2016-11-28 21:52:15 -08:00
2018-01-26 15:50:15 +01:00
$this -> useSSL = true ; // CloudFront requires SSL
2016-11-28 21:52:15 -08:00
$rest = new UpdraftPlus_S3Request ( 'PUT' , '' , '2010-11-01/distribution/' . $dist [ 'id' ] . '/config' , 'cloudfront.amazonaws.com' );
2018-01-26 15:50:15 +01:00
$rest -> data = $this -> __getCloudFrontDistributionConfigXML (
2016-11-28 21:52:15 -08:00
$dist [ 'origin' ],
$dist [ 'enabled' ],
$dist [ 'comment' ],
$dist [ 'callerReference' ],
$dist [ 'cnames' ],
$dist [ 'defaultRootObject' ],
$dist [ 'originAccessIdentity' ],
$dist [ 'trustedSigners' ]
);
$rest -> size = strlen ( $rest -> data );
$rest -> setHeader ( 'If-Match' , $dist [ 'hash' ]);
2018-01-26 15:50:15 +01:00
$rest = $this -> __getCloudFrontResponse ( $rest );
2016-11-28 21:52:15 -08:00
2018-01-26 15:50:15 +01:00
$this -> useSSL = $useSSL ;
2016-11-28 21:52:15 -08:00
2018-01-26 15:50:15 +01:00
if ( false === $rest -> error && 200 !== $rest -> code ) {
2016-11-28 21:52:15 -08:00
$rest -> error = array ( 'code' => $rest -> code , 'message' => 'Unexpected HTTP status' );
2018-01-26 15:50:15 +01:00
}
if ( false !== $rest -> error ) {
$this -> __triggerError ( sprintf ( " UpdraftPlus_S3::updateDistribution( { $dist [ 'id' ] } ): [%s] %s " ,
2016-11-28 21:52:15 -08:00
$rest -> error [ 'code' ], $rest -> error [ 'message' ]), __FILE__ , __LINE__ );
return false ;
} else {
2018-01-26 15:50:15 +01:00
$dist = $this -> __parseCloudFrontDistributionConfig ( $rest -> body );
2016-11-28 21:52:15 -08:00
$dist [ 'hash' ] = $rest -> headers [ 'hash' ];
return $dist ;
}
return false ;
}
/**
2018-01-26 15:50:15 +01:00
* Delete a CloudFront distribution
*
* @ param array $dist Distribution array info identical to output of getDistribution ()
*
* @ return boolean
*/
public function deleteDistribution ( $dist ) {
if ( ! extension_loaded ( 'openssl' )) {
$this -> __triggerError ( sprintf ( " UpdraftPlus_S3::deleteDistribution( { $dist [ 'id' ] } ): %s " ,
2016-11-28 21:52:15 -08:00
" CloudFront functionality requires SSL " ), __FILE__ , __LINE__ );
return false ;
}
2018-01-26 15:50:15 +01:00
$useSSL = $this -> useSSL ;
2016-11-28 21:52:15 -08:00
2018-01-26 15:50:15 +01:00
$this -> useSSL = true ; // CloudFront requires SSL
2016-11-28 21:52:15 -08:00
$rest = new UpdraftPlus_S3Request ( 'DELETE' , '' , '2008-06-30/distribution/' . $dist [ 'id' ], 'cloudfront.amazonaws.com' );
$rest -> setHeader ( 'If-Match' , $dist [ 'hash' ]);
2018-01-26 15:50:15 +01:00
$rest = $this -> __getCloudFrontResponse ( $rest );
2016-11-28 21:52:15 -08:00
2018-01-26 15:50:15 +01:00
$this -> useSSL = $useSSL ;
2016-11-28 21:52:15 -08:00
2018-01-26 15:50:15 +01:00
if ( false === $rest -> error && 204 !== $rest -> code ) {
2016-11-28 21:52:15 -08:00
$rest -> error = array ( 'code' => $rest -> code , 'message' => 'Unexpected HTTP status' );
2018-01-26 15:50:15 +01:00
}
if ( false !== $rest -> error ) {
$this -> __triggerError ( sprintf ( " UpdraftPlus_S3::deleteDistribution( { $dist [ 'id' ] } ): [%s] %s " ,
2016-11-28 21:52:15 -08:00
$rest -> error [ 'code' ], $rest -> error [ 'message' ]), __FILE__ , __LINE__ );
return false ;
}
return true ;
}
/**
2018-01-26 15:50:15 +01:00
* Get a list of CloudFront distributions
*
* @ return array
*/
public function listDistributions () {
if ( ! extension_loaded ( 'openssl' )) {
$this -> __triggerError ( sprintf ( " UpdraftPlus_S3::listDistributions(): [%s] %s " ,
2016-11-28 21:52:15 -08:00
" CloudFront functionality requires SSL " ), __FILE__ , __LINE__ );
return false ;
}
2018-01-26 15:50:15 +01:00
$useSSL = $this -> useSSL ;
$this -> useSSL = true ; // CloudFront requires SSL
2016-11-28 21:52:15 -08:00
$rest = new UpdraftPlus_S3Request ( 'GET' , '' , '2010-11-01/distribution' , 'cloudfront.amazonaws.com' );
2018-01-26 15:50:15 +01:00
$rest = $this -> __getCloudFrontResponse ( $rest );
$this -> useSSL = $useSSL ;
2016-11-28 21:52:15 -08:00
2018-01-26 15:50:15 +01:00
if ( false === $rest -> error && 200 !== $rest -> code ) {
2016-11-28 21:52:15 -08:00
$rest -> error = array ( 'code' => $rest -> code , 'message' => 'Unexpected HTTP status' );
2018-01-26 15:50:15 +01:00
}
if ( false !== $rest -> error ) {
$this -> __triggerError ( sprintf ( " UpdraftPlus_S3::listDistributions(): [%s] %s " ,
2016-11-28 21:52:15 -08:00
$rest -> error [ 'code' ], $rest -> error [ 'message' ]), __FILE__ , __LINE__ );
return false ;
2018-01-26 15:50:15 +01:00
} elseif ( $rest -> body instanceof SimpleXMLElement && isset ( $rest -> body -> DistributionSummary )) {
2016-11-28 21:52:15 -08:00
$list = array ();
2018-01-26 15:50:15 +01:00
if ( isset ( $rest -> body -> Marker , $rest -> body -> MaxItems , $rest -> body -> IsTruncated )) {
2016-11-28 21:52:15 -08:00
//$info['marker'] = (string)$rest->body->Marker;
//$info['maxItems'] = (int)$rest->body->MaxItems;
//$info['isTruncated'] = (string)$rest->body->IsTruncated == 'true' ? true : false;
}
foreach ( $rest -> body -> DistributionSummary as $summary )
2018-01-26 15:50:15 +01:00
$list [( string ) $summary -> Id ] = $this -> __parseCloudFrontDistributionConfig ( $summary );
2016-11-28 21:52:15 -08:00
return $list ;
}
return array ();
}
/**
2018-01-26 15:50:15 +01:00
* List CloudFront Origin Access Identities
*
* @ return array
*/
public function listOriginAccessIdentities () {
if ( ! extension_loaded ( 'openssl' )) {
$this -> __triggerError ( sprintf ( " UpdraftPlus_S3::listOriginAccessIdentities(): [%s] %s " ,
2016-11-28 21:52:15 -08:00
" CloudFront functionality requires SSL " ), __FILE__ , __LINE__ );
return false ;
}
2018-01-26 15:50:15 +01:00
$useSSL = $this -> useSSL ;
$this -> useSSL = true ; // CloudFront requires SSL
2016-11-28 21:52:15 -08:00
$rest = new UpdraftPlus_S3Request ( 'GET' , '' , '2010-11-01/origin-access-identity/cloudfront' , 'cloudfront.amazonaws.com' );
2018-01-26 15:50:15 +01:00
$rest = $this -> __getCloudFrontResponse ( $rest );
$this -> useSSL = $useSSL ;
2016-11-28 21:52:15 -08:00
2018-01-26 15:50:15 +01:00
if ( false === $rest -> error && 200 !== $rest -> code ) {
2016-11-28 21:52:15 -08:00
$rest -> error = array ( 'code' => $rest -> code , 'message' => 'Unexpected HTTP status' );
2018-01-26 15:50:15 +01:00
}
if ( false !== $rest -> error ) {
2016-11-28 21:52:15 -08:00
trigger_error ( sprintf ( " UpdraftPlus_S3::listOriginAccessIdentities(): [%s] %s " ,
$rest -> error [ 'code' ], $rest -> error [ 'message' ]), E_USER_WARNING );
return false ;
}
2018-01-26 15:50:15 +01:00
if ( isset ( $rest -> body -> CloudFrontOriginAccessIdentitySummary )) {
2016-11-28 21:52:15 -08:00
$identities = array ();
foreach ( $rest -> body -> CloudFrontOriginAccessIdentitySummary as $identity )
if ( isset ( $identity -> S3CanonicalUserId ))
$identities [( string ) $identity -> Id ] = array ( 'id' => ( string ) $identity -> Id , 's3CanonicalUserId' => ( string ) $identity -> S3CanonicalUserId );
return $identities ;
}
return false ;
}
/**
2018-01-26 15:50:15 +01:00
* Invalidate objects in a CloudFront distribution
*
* Thanks to Martin Lindkvist for $this -> s3 -> invalidateDistribution ()
*
* @ param string $distributionId Distribution ID from listDistributions ()
* @ param array $paths Array of object paths to invalidate
*
* @ return boolean
*/
public function invalidateDistribution ( $distributionId , $paths ) {
if ( ! extension_loaded ( 'openssl' )) {
$this -> __triggerError ( sprintf ( " UpdraftPlus_S3::invalidateDistribution(): [%s] %s " ,
2016-11-28 21:52:15 -08:00
" CloudFront functionality requires SSL " ), __FILE__ , __LINE__ );
return false ;
}
2018-01-26 15:50:15 +01:00
$useSSL = $this -> useSSL ;
$this -> useSSL = true ; // CloudFront requires SSL
2016-11-28 21:52:15 -08:00
$rest = new UpdraftPlus_S3Request ( 'POST' , '' , '2010-08-01/distribution/' . $distributionId . '/invalidation' , 'cloudfront.amazonaws.com' );
2018-01-26 15:50:15 +01:00
$rest -> data = $this -> __getCloudFrontInvalidationBatchXML ( $paths , ( string ) microtime ( true ));
2016-11-28 21:52:15 -08:00
$rest -> size = strlen ( $rest -> data );
2018-01-26 15:50:15 +01:00
$rest = $this -> __getCloudFrontResponse ( $rest );
$this -> useSSL = $useSSL ;
2016-11-28 21:52:15 -08:00
2018-01-26 15:50:15 +01:00
if ( false === $rest -> error && 201 !== $rest -> code ) {
2016-11-28 21:52:15 -08:00
$rest -> error = array ( 'code' => $rest -> code , 'message' => 'Unexpected HTTP status' );
2018-01-26 15:50:15 +01:00
}
if ( false !== $rest -> error ) {
trigger_error ( sprintf ( " UpdraftPlus_S3::invalidateDistribution(' { $distributionId } ', { $paths } ): [%s] %s " ,
2016-11-28 21:52:15 -08:00
$rest -> error [ 'code' ], $rest -> error [ 'message' ]), E_USER_WARNING );
return false ;
}
return true ;
}
/**
2018-01-26 15:50:15 +01:00
* Get a InvalidationBatch DOMDocument
*
* @ internal Used to create XML in invalidateDistribution ()
*
* @ param array $paths Paths to objects to invalidateDistribution
* @ param string $callerReference
*
* @ return string
*/
private function __getCloudFrontInvalidationBatchXML ( $paths , $callerReference = '0' ) {
2016-11-28 21:52:15 -08:00
$dom = new DOMDocument ( '1.0' , 'UTF-8' );
$dom -> formatOutput = true ;
$invalidationBatch = $dom -> createElement ( 'InvalidationBatch' );
foreach ( $paths as $path )
$invalidationBatch -> appendChild ( $dom -> createElement ( 'Path' , $path ));
$invalidationBatch -> appendChild ( $dom -> createElement ( 'CallerReference' , $callerReference ));
$dom -> appendChild ( $invalidationBatch );
return $dom -> saveXML ();
}
/**
2018-01-26 15:50:15 +01:00
* List your invalidation batches for invalidateDistribution () in a CloudFront distribution
*
* http :// docs . amazonwebservices . com / AmazonCloudFront / latest / APIReference / ListInvalidation . html
* returned array looks like this :
* Array
* (
* [ I31TWB0CN9V6XD ] => InProgress
* [ IT3TFE31M0IHZ ] => Completed
* [ I12HK7MPO1UQDA ] => Completed
* [ I1IA7R6JKTC3L2 ] => Completed
* )
*
* @ param string $distributionId Distribution ID from listDistributions ()
*
* @ return array
*/
public function getDistributionInvalidationList ( $distributionId ) {
if ( ! extension_loaded ( 'openssl' )) {
$this -> __triggerError ( sprintf ( " UpdraftPlus_S3::getDistributionInvalidationList(): [%s] %s " ,
2016-11-28 21:52:15 -08:00
" CloudFront functionality requires SSL " ), __FILE__ , __LINE__ );
return false ;
}
2018-01-26 15:50:15 +01:00
$useSSL = $this -> useSSL ;
$this -> useSSL = true ; // CloudFront requires SSL
2016-11-28 21:52:15 -08:00
$rest = new UpdraftPlus_S3Request ( 'GET' , '' , '2010-11-01/distribution/' . $distributionId . '/invalidation' , 'cloudfront.amazonaws.com' );
2018-01-26 15:50:15 +01:00
$rest = $this -> __getCloudFrontResponse ( $rest );
$this -> useSSL = $useSSL ;
2016-11-28 21:52:15 -08:00
2018-01-26 15:50:15 +01:00
if ( false === $rest -> error && 200 !== $rest -> code ) {
2016-11-28 21:52:15 -08:00
$rest -> error = array ( 'code' => $rest -> code , 'message' => 'Unexpected HTTP status' );
2018-01-26 15:50:15 +01:00
}
if ( false !== $rest -> error ) {
2016-11-28 21:52:15 -08:00
trigger_error ( sprintf ( " UpdraftPlus_S3::getDistributionInvalidationList(' { $distributionId } '): [%s] " ,
$rest -> error [ 'code' ], $rest -> error [ 'message' ]), E_USER_WARNING );
return false ;
2018-01-26 15:50:15 +01:00
} elseif ( $rest -> body instanceof SimpleXMLElement && isset ( $rest -> body -> InvalidationSummary )) {
2016-11-28 21:52:15 -08:00
$list = array ();
foreach ( $rest -> body -> InvalidationSummary as $summary )
$list [( string ) $summary -> Id ] = ( string ) $summary -> Status ;
return $list ;
}
return array ();
}
/**
2018-01-26 15:50:15 +01:00
* Get a DistributionConfig DOMDocument
*
* http :// docs . amazonwebservices . com / AmazonCloudFront / latest / APIReference / index . html ? PutConfig . html
*
* @ internal Used to create XML in createDistribution () and updateDistribution ()
* @ param string $bucket S3 Origin bucket
* @ param boolean $enabled Enabled ( true / false )
* @ param string $comment Comment to append
* @ param string $callerReference Caller reference
* @ param array $cnames Array of CNAME aliases
* @ param string $defaultRootObject Default root object
* @ param string $originAccessIdentity Origin access identity
* @ param array $trustedSigners Array of trusted signers
*
* @ return string
*/
private function __getCloudFrontDistributionConfigXML ( $bucket , $enabled , $comment , $callerReference = '0' , $cnames = array (), $defaultRootObject = null , $originAccessIdentity = null , $trustedSigners = array ()) {
2016-11-28 21:52:15 -08:00
$dom = new DOMDocument ( '1.0' , 'UTF-8' );
$dom -> formatOutput = true ;
$distributionConfig = $dom -> createElement ( 'DistributionConfig' );
$distributionConfig -> setAttribute ( 'xmlns' , 'http://cloudfront.amazonaws.com/doc/2010-11-01/' );
$origin = $dom -> createElement ( 'S3Origin' );
$origin -> appendChild ( $dom -> createElement ( 'DNSName' , $bucket ));
2018-01-26 15:50:15 +01:00
if ( null !== $originAccessIdentity ) $origin -> appendChild ( $dom -> createElement ( 'OriginAccessIdentity' , $originAccessIdentity ));
2016-11-28 21:52:15 -08:00
$distributionConfig -> appendChild ( $origin );
2018-01-26 15:50:15 +01:00
if ( null !== $defaultRootObject ) $distributionConfig -> appendChild ( $dom -> createElement ( 'DefaultRootObject' , $defaultRootObject ));
2016-11-28 21:52:15 -08:00
$distributionConfig -> appendChild ( $dom -> createElement ( 'CallerReference' , $callerReference ));
foreach ( $cnames as $cname )
$distributionConfig -> appendChild ( $dom -> createElement ( 'CNAME' , $cname ));
2018-01-26 15:50:15 +01:00
if ( '' !== $comment ) $distributionConfig -> appendChild ( $dom -> createElement ( 'Comment' , $comment ));
2016-11-28 21:52:15 -08:00
$distributionConfig -> appendChild ( $dom -> createElement ( 'Enabled' , $enabled ? 'true' : 'false' ));
2018-01-26 15:50:15 +01:00
if ( ! empty ( $trustedSigners )) {
$trusted = $dom -> createElement ( 'TrustedSigners' );
foreach ( $trustedSigners as $id => $type ) {
$trusted -> appendChild (( '' !== $id ) ? $dom -> createElement ( $type , $id ) : $dom -> createElement ( $type ));
}
$distributionConfig -> appendChild ( $trusted );
}
2016-11-28 21:52:15 -08:00
$dom -> appendChild ( $distributionConfig );
//var_dump($dom->saveXML());
return $dom -> saveXML ();
}
/**
2018-01-26 15:50:15 +01:00
* Parse a CloudFront distribution config
*
* See http :// docs . amazonwebservices . com / AmazonCloudFront / latest / APIReference / index . html ? GetDistribution . html
*
* @ internal Used to parse the CloudFront DistributionConfig node to an array
*
* @ param object & $node DOMNode
*
* @ return array
*/
private function __parseCloudFrontDistributionConfig ( & $node ) {
2016-11-28 21:52:15 -08:00
if ( isset ( $node -> DistributionConfig ))
2018-01-26 15:50:15 +01:00
return $this -> __parseCloudFrontDistributionConfig ( $node -> DistributionConfig );
2016-11-28 21:52:15 -08:00
$dist = array ();
2018-01-26 15:50:15 +01:00
if ( isset ( $node -> Id , $node -> Status , $node -> LastModifiedTime , $node -> DomainName )) {
2016-11-28 21:52:15 -08:00
$dist [ 'id' ] = ( string ) $node -> Id ;
$dist [ 'status' ] = ( string ) $node -> Status ;
$dist [ 'time' ] = strtotime (( string ) $node -> LastModifiedTime );
$dist [ 'domain' ] = ( string ) $node -> DomainName ;
}
if ( isset ( $node -> CallerReference ))
$dist [ 'callerReference' ] = ( string ) $node -> CallerReference ;
if ( isset ( $node -> Enabled ))
$dist [ 'enabled' ] = ( string ) $node -> Enabled == 'true' ? true : false ;
2018-01-26 15:50:15 +01:00
if ( isset ( $node -> S3Origin )) {
2016-11-28 21:52:15 -08:00
if ( isset ( $node -> S3Origin -> DNSName ))
$dist [ 'origin' ] = ( string ) $node -> S3Origin -> DNSName ;
$dist [ 'originAccessIdentity' ] = isset ( $node -> S3Origin -> OriginAccessIdentity ) ?
( string ) $node -> S3Origin -> OriginAccessIdentity : null ;
}
$dist [ 'defaultRootObject' ] = isset ( $node -> DefaultRootObject ) ? ( string ) $node -> DefaultRootObject : null ;
$dist [ 'cnames' ] = array ();
if ( isset ( $node -> CNAME ))
foreach ( $node -> CNAME as $cname )
$dist [ 'cnames' ][( string ) $cname ] = ( string ) $cname ;
$dist [ 'trustedSigners' ] = array ();
if ( isset ( $node -> TrustedSigners ))
2018-01-26 15:50:15 +01:00
foreach ( $node -> TrustedSigners as $signer ) {
2016-11-28 21:52:15 -08:00
if ( isset ( $signer -> Self ))
$dist [ 'trustedSigners' ][ '' ] = 'Self' ;
elseif ( isset ( $signer -> KeyPairId ))
$dist [ 'trustedSigners' ][( string ) $signer -> KeyPairId ] = 'KeyPairId' ;
elseif ( isset ( $signer -> AwsAccountNumber ))
$dist [ 'trustedSigners' ][( string ) $signer -> AwsAccountNumber ] = 'AwsAccountNumber' ;
}
$dist [ 'comment' ] = isset ( $node -> Comment ) ? ( string ) $node -> Comment : null ;
return $dist ;
}
/**
2018-01-26 15:50:15 +01:00
* Grab CloudFront response
*
* @ internal Used to parse the CloudFront UpdraftPlus_S3Request :: getResponse () output
*
* @ param object & $rest UpdraftPlus_S3Request instance
*
* @ return object
*/
private function __getCloudFrontResponse ( & $rest ) {
2016-11-28 21:52:15 -08:00
$rest -> getResponse ();
2018-01-26 15:50:15 +01:00
if ( false === $rest -> response -> error && isset ( $rest -> response -> body ) &&
is_string ( $rest -> response -> body ) && '<?xml' == substr ( $rest -> response -> body , 0 , 5 )) {
2016-11-28 21:52:15 -08:00
$rest -> response -> body = simplexml_load_string ( $rest -> response -> body );
// Grab CloudFront errors
if ( isset ( $rest -> response -> body -> Error , $rest -> response -> body -> Error -> Code ,
2018-01-26 15:50:15 +01:00
$rest -> response -> body -> Error -> Message )) {
2016-11-28 21:52:15 -08:00
$rest -> response -> error = array (
'code' => ( string ) $rest -> response -> body -> Error -> Code ,
'message' => ( string ) $rest -> response -> body -> Error -> Message
);
unset ( $rest -> response -> body );
}
}
return $rest -> response ;
}
/**
2018-01-26 15:50:15 +01:00
* Get MIME type for file
*
* @ internal Used to get mime types
*
* @ param string & $file File path
*
* @ return string
*/
public function __getMimeType ( & $file ) {
2016-11-28 21:52:15 -08:00
$type = false ;
// Fileinfo documentation says fileinfo_open() will use the
// MAGIC env var for the magic file
if ( extension_loaded ( 'fileinfo' ) && isset ( $_ENV [ 'MAGIC' ]) &&
2018-01-26 15:50:15 +01:00
false !== ( $finfo = finfo_open ( FILEINFO_MIME , $_ENV [ 'MAGIC' ]))) {
if ( false !== ( $type = finfo_file ( $finfo , $file ))) {
2016-11-28 21:52:15 -08:00
// Remove the charset and grab the last content-type
$type = explode ( ' ' , str_replace ( '; charset=' , ';charset=' , $type ));
$type = array_pop ( $type );
$type = explode ( ';' , $type );
$type = trim ( array_shift ( $type ));
}
finfo_close ( $finfo );
// If anyone is still using mime_content_type()
2018-01-26 15:50:15 +01:00
} elseif ( function_exists ( 'mime_content_type' )) {
2016-11-28 21:52:15 -08:00
$type = trim ( mime_content_type ( $file ));
2018-01-26 15:50:15 +01:00
}
2016-11-28 21:52:15 -08:00
2018-01-26 15:50:15 +01:00
if ( false !== $type && strlen ( $type ) > 0 ) return $type ;
2016-11-28 21:52:15 -08:00
// Otherwise do it the old fashioned way
static $exts = array (
'jpg' => 'image/jpeg' , 'gif' => 'image/gif' , 'png' => 'image/png' ,
'tif' => 'image/tiff' , 'tiff' => 'image/tiff' , 'ico' => 'image/x-icon' ,
'swf' => 'application/x-shockwave-flash' , 'pdf' => 'application/pdf' ,
'zip' => 'application/zip' , 'gz' => 'application/x-gzip' ,
'tar' => 'application/x-tar' , 'bz' => 'application/x-bzip' ,
'bz2' => 'application/x-bzip2' , 'txt' => 'text/plain' ,
'asc' => 'text/plain' , 'htm' => 'text/html' , 'html' => 'text/html' ,
'css' => 'text/css' , 'js' => 'text/javascript' ,
'xml' => 'text/xml' , 'xsl' => 'application/xsl+xml' ,
'ogg' => 'application/ogg' , 'mp3' => 'audio/mpeg' , 'wav' => 'audio/x-wav' ,
'avi' => 'video/x-msvideo' , 'mpg' => 'video/mpeg' , 'mpeg' => 'video/mpeg' ,
'mov' => 'video/quicktime' , 'flv' => 'video/x-flv' , 'php' => 'text/x-php'
);
$ext = strtolower ( pathInfo ( $file , PATHINFO_EXTENSION ));
return isset ( $exts [ $ext ]) ? $exts [ $ext ] : 'application/octet-stream' ;
}
/**
2018-01-26 15:50:15 +01:00
* Generate the auth string : " AWS AccessKey:Signature "
*
* @ internal Used by UpdraftPlus_S3Request :: getResponse ()
*
* @ param string $string String to sign
*
* @ return string
*/
public function __getSignature ( $string ) {
return 'AWS ' . $this -> __accessKey . ':' . $this -> __getHash ( $string );
2016-11-28 21:52:15 -08:00
}
/**
2018-01-26 15:50:15 +01:00
* Creates a HMAC - SHA1 hash
*
* This uses the hash extension if loaded
*
* @ internal Used by __getSignature ()
*
* @ param string $string String to sign
*
* @ return string
*/
private function __getHash ( $string ) {
2016-11-28 21:52:15 -08:00
return base64_encode ( extension_loaded ( 'hash' ) ?
2018-01-26 15:50:15 +01:00
hash_hmac ( 'sha1' , $string , $this -> __secretKey , true ) : pack ( 'H*' , sha1 (
( str_pad ( $this -> __secretKey , 64 , chr ( 0x00 )) ^ ( str_repeat ( chr ( 0x5c ), 64 ))) .
pack ( 'H*' , sha1 (( str_pad ( $this -> __secretKey , 64 , chr ( 0x00 )) ^
2016-11-28 21:52:15 -08:00
( str_repeat ( chr ( 0x36 ), 64 ))) . $string )))));
}
2018-01-26 15:50:15 +01:00
/**
* Generate the headers for AWS Signature V4
*
* @ internal Used by UpdraftPlus_S3Request :: getResponse ()
* @ param array $aHeaders amzHeaders
* @ param array $headers
* @ param string $method
* @ param string $uri
* @ param string $data
*
* @ return array $headers
*/
public function __getSignatureV4 ( $aHeaders , $headers , $method = 'GET' , $uri = '' , $data = '' ) {
$service = 's3' ;
$region = $this -> getRegion ();
$algorithm = 'AWS4-HMAC-SHA256' ;
$amzHeaders = array ();
$amzRequests = array ();
$amzDate = gmdate ( 'Ymd\THis\Z' );
$amzDateStamp = gmdate ( 'Ymd' );
// amz-date ISO8601 format? for aws request
$amzHeaders [ 'x-amz-date' ] = $amzDate ;
// CanonicalHeaders
foreach ( $headers as $k => $v ) {
$amzHeaders [ strtolower ( $k )] = trim ( $v );
}
foreach ( $aHeaders as $k => $v ) {
$amzHeaders [ strtolower ( $k )] = trim ( $v );
}
uksort ( $amzHeaders , 'strcmp' );
// payload
$payloadHash = isset ( $amzHeaders [ 'x-amz-content-sha256' ]) ? $amzHeaders [ 'x-amz-content-sha256' ] : hash ( 'sha256' , $data );
// parameters
$parameters = array ();
if ( strpos ( $uri , '?' )) {
list ( $uri , $query_str ) = @ explode ( '?' , $uri );
parse_str ( $query_str , $parameters );
}
// Canonical Requests
$amzRequests [] = $method ;
$uriQmPos = strpos ( $uri , '?' );
$amzRequests [] = ( false === $uriQmPos ? $uri : substr ( $uri , 0 , $uriQmPos ));
$amzRequests [] = http_build_query ( $parameters );
// add headers as string to requests
foreach ( $amzHeaders as $k => $v ) {
$amzRequests [] = $k . ':' . $v ;
}
// add a blank entry so we end up with an extra line break
$amzRequests [] = '' ;
// SignedHeaders
$amzRequests [] = implode ( ';' , array_keys ( $amzHeaders ));
// payload hash
$amzRequests [] = $payloadHash ;
// request as string
$amzRequestStr = implode ( " \n " , $amzRequests );
// CredentialScope
$credentialScope = array ();
$credentialScope [] = $amzDateStamp ;
$credentialScope [] = $region ;
$credentialScope [] = $service ;
$credentialScope [] = 'aws4_request' ;
// stringToSign
$stringToSign = array ();
$stringToSign [] = $algorithm ;
$stringToSign [] = $amzDate ;
$stringToSign [] = implode ( '/' , $credentialScope );
$stringToSign [] = hash ( 'sha256' , $amzRequestStr );
// as string
$stringToSignStr = implode ( " \n " , $stringToSign );
// Make Signature
$kSecret = 'AWS4' . $this -> __secretKey ;
$kDate = hash_hmac ( 'sha256' , $amzDateStamp , $kSecret , true );
$kRegion = hash_hmac ( 'sha256' , $region , $kDate , true );
$kService = hash_hmac ( 'sha256' , $service , $kRegion , true );
$kSigning = hash_hmac ( 'sha256' , 'aws4_request' , $kService , true );
$signature = hash_hmac ( 'sha256' , $stringToSignStr , $kSigning );
$authorization = array (
'Credential=' . $this -> __accessKey . '/' . implode ( '/' , $credentialScope ),
'SignedHeaders=' . implode ( ';' , array_keys ( $amzHeaders )),
'Signature=' . $signature ,
);
$authorizationStr = $algorithm . ' ' . implode ( ',' , $authorization );
$resultHeaders = array (
'X-AMZ-DATE' => $amzDate ,
'Authorization' => $authorizationStr
);
if ( ! isset ( $aHeaders [ 'x-amz-content-sha256' ])) {
$resultHeaders [ 'x-amz-content-sha256' ] = $payloadHash ;
}
return $resultHeaders ;
}
2016-11-28 21:52:15 -08:00
}
2018-01-26 15:50:15 +01:00
final class UpdraftPlus_S3Request {
2016-11-28 21:52:15 -08:00
private $endpoint , $verb , $bucket , $uri , $resource = '' , $parameters = array (),
$amzHeaders = array (), $headers = array (
'Host' => '' , 'Date' => '' , 'Content-MD5' => '' , 'Content-Type' => ''
);
public $fp = false , $size = 0 , $data = false , $response ;
2018-01-26 15:50:15 +01:00
private $s3 ;
2016-11-28 21:52:15 -08:00
/**
2018-01-26 15:50:15 +01:00
* Constructor
*
* @ param string $verb Verb
* @ param string $bucket Bucket name
* @ param string $uri Object URI
* @ param string $endpoint Endpoint of storage
* @ param boolean $use_dns_bucket_name
* @ param object $s3 S3 Object that calls these requests
*
* @ return mixed
*/
function __construct ( $verb , $bucket = '' , $uri = '' , $endpoint = 's3.amazonaws.com' , $use_dns_bucket_name = false , $s3 = null ) {
2016-11-28 21:52:15 -08:00
$this -> endpoint = $endpoint ;
$this -> verb = $verb ;
$this -> bucket = $bucket ;
$this -> uri = $uri !== '' ? '/' . str_replace ( '%2F' , '/' , rawurlencode ( $uri )) : '/' ;
2018-01-26 15:50:15 +01:00
$this -> s3 = $s3 ;
2016-11-28 21:52:15 -08:00
//if ($this->bucket !== '')
// $this->resource = '/'.$this->bucket.$this->uri;
//else
// $this->resource = $this->uri;
2018-01-26 15:50:15 +01:00
if ( '' !== $this -> bucket ) {
if ( $this -> __dnsBucketName ( $this -> bucket ) || $use_dns_bucket_name ) {
2016-11-28 21:52:15 -08:00
$this -> headers [ 'Host' ] = $this -> bucket . '.' . $this -> endpoint ;
$this -> resource = '/' . $this -> bucket . $this -> uri ;
2018-01-26 15:50:15 +01:00
} else {
2016-11-28 21:52:15 -08:00
$this -> headers [ 'Host' ] = $this -> endpoint ;
$this -> uri = $this -> uri ;
2018-01-26 15:50:15 +01:00
if ( '' !== $this -> bucket ) $this -> uri = '/' . $this -> bucket . $this -> uri ;
2016-11-28 21:52:15 -08:00
$this -> bucket = '' ;
$this -> resource = $this -> uri ;
}
2018-01-26 15:50:15 +01:00
} else {
2016-11-28 21:52:15 -08:00
$this -> headers [ 'Host' ] = $this -> endpoint ;
$this -> resource = $this -> uri ;
}
$this -> headers [ 'Date' ] = gmdate ( 'D, d M Y H:i:s T' );
$this -> response = new STDClass ;
$this -> response -> error = false ;
$this -> response -> body = null ;
}
/**
2018-01-26 15:50:15 +01:00
* Set request parameter
*
* @ param string $key Key
* @ param string $value Value
*
* @ return void
*/
public function setParameter ( $key , $value ) {
2016-11-28 21:52:15 -08:00
$this -> parameters [ $key ] = $value ;
}
/**
2018-01-26 15:50:15 +01:00
* Set request header
*
* @ param string $key Key
* @ param string $value Value
*
* @ return void
*/
public function setHeader ( $key , $value ) {
2016-11-28 21:52:15 -08:00
$this -> headers [ $key ] = $value ;
}
/**
2018-01-26 15:50:15 +01:00
* Set x - amz - meta -* header
*
* @ param string $key Key
* @ param string $value Value
*
* @ return void
*/
public function setAmzHeader ( $key , $value ) {
2016-11-28 21:52:15 -08:00
$this -> amzHeaders [ $key ] = $value ;
}
2018-01-26 15:50:15 +01:00
2016-11-28 21:52:15 -08:00
/**
2018-01-26 15:50:15 +01:00
* Get the S3 response
*
* @ return object | false
*/
public function getResponse () {
2016-11-28 21:52:15 -08:00
$query = '' ;
2018-01-26 15:50:15 +01:00
if ( sizeof ( $this -> parameters ) > 0 ) {
$query = ( '?' !== substr ( $this -> uri , - 1 )) ? '?' : '&' ;
2016-11-28 21:52:15 -08:00
foreach ( $this -> parameters as $var => $value )
2018-01-26 15:50:15 +01:00
if ( null == $value || '' == $value ) $query .= $var . '&' ;
2016-11-28 21:52:15 -08:00
else $query .= $var . '=' . rawurlencode ( $value ) . '&' ;
$query = substr ( $query , 0 , - 1 );
$this -> uri .= $query ;
if ( array_key_exists ( 'acl' , $this -> parameters ) ||
array_key_exists ( 'location' , $this -> parameters ) ||
array_key_exists ( 'torrent' , $this -> parameters ) ||
array_key_exists ( 'logging' , $this -> parameters ) ||
array_key_exists ( 'partNumber' , $this -> parameters ) ||
array_key_exists ( 'uploads' , $this -> parameters ) ||
array_key_exists ( 'uploadId' , $this -> parameters ))
$this -> resource .= $query ;
}
2018-01-26 15:50:15 +01:00
$url = ( $this -> s3 -> useSSL ? 'https://' : 'http://' ) . ( '' !== $this -> headers [ 'Host' ] ? $this -> headers [ 'Host' ] : $this -> endpoint ) . $this -> uri ;
2016-11-28 21:52:15 -08:00
//var_dump('bucket: ' . $this->bucket, 'uri: ' . $this->uri, 'resource: ' . $this->resource, 'url: ' . $url);
$curl = curl_init ();
curl_setopt ( $curl , CURLOPT_USERAGENT , 'S3/php' );
2018-01-26 15:50:15 +01:00
if ( $this -> s3 -> useSSL ) {
2016-11-28 21:52:15 -08:00
// SSL Validation can now be optional for those with broken OpenSSL installations
2018-01-26 15:50:15 +01:00
curl_setopt ( $curl , CURLOPT_SSL_VERIFYHOST , $this -> s3 -> useSSLValidation ? 2 : 0 );
curl_setopt ( $curl , CURLOPT_SSL_VERIFYPEER , $this -> s3 -> useSSLValidation ? 1 : 0 );
2016-11-28 21:52:15 -08:00
2018-01-26 15:50:15 +01:00
if ( null !== $this -> s3 -> sslKey ) curl_setopt ( $curl , CURLOPT_SSLKEY , $this -> s3 -> sslKey );
if ( null !== $this -> s3 -> sslCert ) curl_setopt ( $curl , CURLOPT_SSLCERT , $this -> s3 -> sslCert );
if ( null !== $this -> s3 -> sslCACert ) curl_setopt ( $curl , CURLOPT_CAINFO , $this -> s3 -> sslCACert );
2016-11-28 21:52:15 -08:00
}
curl_setopt ( $curl , CURLOPT_URL , $url );
$wp_proxy = new WP_HTTP_Proxy ();
2018-01-26 15:50:15 +01:00
if ( null != $this -> s3 -> proxy && isset ( $this -> s3 -> proxy [ 'host' ]) && $wp_proxy -> send_through_proxy ( $url )) {
curl_setopt ( $curl , CURLOPT_PROXY , $this -> s3 -> proxy [ 'host' ]);
curl_setopt ( $curl , CURLOPT_PROXYTYPE , $this -> s3 -> proxy [ 'type' ]);
if ( ! empty ( $this -> s3 -> proxy [ 'port' ])) curl_setopt ( $curl , CURLOPT_PROXYPORT , $this -> s3 -> proxy [ 'port' ]);
if ( isset ( $this -> s3 -> proxy [ 'user' ], $this -> s3 -> proxy [ 'pass' ]) && null != $this -> s3 -> proxy [ 'user' ] && null != $this -> s3 -> proxy [ 'pass' ]) {
2016-11-28 21:52:15 -08:00
curl_setopt ( $curl , CURLOPT_PROXYAUTH , CURLAUTH_ANY );
2018-01-26 15:50:15 +01:00
curl_setopt ( $curl , CURLOPT_PROXYUSERPWD , sprintf ( '%s:%s' , $this -> s3 -> proxy [ 'user' ], $this -> s3 -> proxy [ 'pass' ]));
2016-11-28 21:52:15 -08:00
}
}
// Headers
$headers = array (); $amz = array ();
foreach ( $this -> amzHeaders as $header => $value )
if ( strlen ( $value ) > 0 ) $headers [] = $header . ': ' . $value ;
foreach ( $this -> headers as $header => $value )
if ( strlen ( $value ) > 0 ) $headers [] = $header . ': ' . $value ;
// Collect AMZ headers for signature
foreach ( $this -> amzHeaders as $header => $value )
if ( strlen ( $value ) > 0 ) $amz [] = strtolower ( $header ) . ':' . $value ;
// AMZ headers must be sorted
2018-01-26 15:50:15 +01:00
if ( sizeof ( $amz ) > 0 ) {
2016-11-28 21:52:15 -08:00
//sort($amz);
usort ( $amz , array ( & $this , '__sortMetaHeadersCmp' ));
$amz = " \n " . implode ( " \n " , $amz );
2018-01-26 15:50:15 +01:00
} else {
$amz = '' ;
}
2016-11-28 21:52:15 -08:00
2018-01-26 15:50:15 +01:00
if ( $this -> s3 -> hasAuth ()) {
2016-11-28 21:52:15 -08:00
// Authorization string (CloudFront stringToSign should only contain a date)
2018-01-26 15:50:15 +01:00
if ( 'cloudfront.amazonaws.com' == $this -> headers [ 'Host' ]) {
$headers [] = 'Authorization: ' . $this -> s3 -> __getSignature ( $this -> headers [ 'Date' ]);
} else {
if ( 'v2' === $this -> s3 -> signVer ) {
$headers [] = 'Authorization: ' . $this -> s3 -> __getSignature (
$this -> verb . " \n " .
$this -> headers [ 'Content-MD5' ] . " \n " .
$this -> headers [ 'Content-Type' ] . " \n " .
$this -> headers [ 'Date' ] . $amz . " \n " .
$this -> resource
);
} else {
$amzHeaders = $this -> s3 -> __getSignatureV4 (
$this -> amzHeaders ,
$this -> headers ,
$this -> verb ,
$this -> uri ,
$this -> data
);
foreach ( $amzHeaders as $k => $v ) {
$headers [] = $k . ': ' . $v ;
}
}
2016-11-28 21:52:15 -08:00
}
}
2018-01-26 15:50:15 +01:00
if ( false !== $this -> s3 -> port ) curl_setopt ( $curl , CURLOPT_PORT , $this -> s3 -> port );
2016-11-28 21:52:15 -08:00
curl_setopt ( $curl , CURLOPT_HTTPHEADER , $headers );
curl_setopt ( $curl , CURLOPT_HEADER , false );
curl_setopt ( $curl , CURLOPT_RETURNTRANSFER , false );
curl_setopt ( $curl , CURLOPT_WRITEFUNCTION , array ( & $this , '__responseWriteCallback' ));
curl_setopt ( $curl , CURLOPT_HEADERFUNCTION , array ( & $this , '__responseHeaderCallback' ));
@ curl_setopt ( $curl , CURLOPT_FOLLOWLOCATION , true );
// Request types
2018-01-26 15:50:15 +01:00
switch ( $this -> verb ) {
2016-11-28 21:52:15 -08:00
case 'GET' : break ;
case 'PUT' : case 'POST' :
2018-01-26 15:50:15 +01:00
if ( false !== $this -> fp ) {
2016-11-28 21:52:15 -08:00
curl_setopt ( $curl , CURLOPT_PUT , true );
curl_setopt ( $curl , CURLOPT_INFILE , $this -> fp );
if ( $this -> size >= 0 ) {
curl_setopt ( $curl , CURLOPT_INFILESIZE , $this -> size );
}
2018-01-26 15:50:15 +01:00
} elseif ( false !== $this -> data ) {
2016-11-28 21:52:15 -08:00
curl_setopt ( $curl , CURLOPT_CUSTOMREQUEST , $this -> verb );
curl_setopt ( $curl , CURLOPT_POSTFIELDS , $this -> data );
curl_setopt ( $curl , CURLOPT_INFILESIZE , strlen ( $this -> data ));
2018-01-26 15:50:15 +01:00
} else {
2016-11-28 21:52:15 -08:00
curl_setopt ( $curl , CURLOPT_CUSTOMREQUEST , $this -> verb );
}
break ;
case 'HEAD' :
curl_setopt ( $curl , CURLOPT_CUSTOMREQUEST , 'HEAD' );
curl_setopt ( $curl , CURLOPT_NOBODY , true );
break ;
case 'DELETE' :
curl_setopt ( $curl , CURLOPT_CUSTOMREQUEST , 'DELETE' );
break ;
default : break ;
}
// Execute, grab errors
if ( curl_exec ( $curl ))
$this -> response -> code = curl_getinfo ( $curl , CURLINFO_HTTP_CODE );
else
$this -> response -> error = array (
'code' => curl_errno ( $curl ),
'message' => curl_error ( $curl ),
'resource' => $this -> resource
);
@ curl_close ( $curl );
// Parse body into XML
2018-01-26 15:50:15 +01:00
if ( false === $this -> response -> error && isset ( $this -> response -> headers [ 'type' ]) &&
'application/xml' == $this -> response -> headers [ 'type' ] && isset ( $this -> response -> body )) {
2016-11-28 21:52:15 -08:00
$this -> response -> body = simplexml_load_string ( $this -> response -> body );
// Grab S3 errors
if ( ! in_array ( $this -> response -> code , array ( 200 , 204 , 206 )) &&
2018-01-26 15:50:15 +01:00
isset ( $this -> response -> body -> Code , $this -> response -> body -> Message )) {
2016-11-28 21:52:15 -08:00
$this -> response -> error = array (
'code' => ( string ) $this -> response -> body -> Code ,
'message' => ( string ) $this -> response -> body -> Message
);
if ( isset ( $this -> response -> body -> Resource ))
$this -> response -> error [ 'resource' ] = ( string ) $this -> response -> body -> Resource ;
unset ( $this -> response -> body );
}
}
2018-01-26 15:50:15 +01:00
2016-11-28 21:52:15 -08:00
// Clean up file resources
2018-01-26 15:50:15 +01:00
if ( false !== $this -> fp && is_resource ( $this -> fp )) fclose ( $this -> fp );
2016-11-28 21:52:15 -08:00
return $this -> response ;
}
/**
2018-01-26 15:50:15 +01:00
* Sort compare for meta headers
*
* @ internal Used to sort x - amz meta headers
*
* @ param string $a String A
* @ param string $b String B
*
* @ return integer
*/
private function __sortMetaHeadersCmp ( $a , $b ) {
2016-11-28 21:52:15 -08:00
$lenA = strpos ( $a , ':' );
$lenB = strpos ( $b , ':' );
$minLen = min ( $lenA , $lenB );
$ncmp = strncmp ( $a , $b , $minLen );
if ( $lenA == $lenB ) return $ncmp ;
if ( 0 == $ncmp ) return $lenA < $lenB ? - 1 : 1 ;
return $ncmp ;
}
/**
2018-01-26 15:50:15 +01:00
* CURL write callback
*
* @ param resource $curl CURL resource
* @ param string $data Data
*
* @ return integer
*/
private function __responseWriteCallback ( $curl , $data ) {
if ( in_array ( $this -> response -> code , array ( 200 , 206 )) && false !== $this -> fp )
2016-11-28 21:52:15 -08:00
return fwrite ( $this -> fp , $data );
else
$this -> response -> body = ( empty ( $this -> response -> body )) ? $data : $this -> response -> body . $data ;
return strlen ( $data );
}
/**
2018-01-26 15:50:15 +01:00
* Check DNS conformity
*
* @ param string $bucket Bucket name
*
* @ return boolean
*/
private function __dnsBucketName ( $bucket ) {
2016-11-28 21:52:15 -08:00
# A DNS bucket name cannot have len>63
# A DNS bucket name must have a character in other than a-z, 0-9, . -
# The purpose of this second check is not clear - is it that there's some limitation somewhere on bucket names that match that pattern that means that the bucket must be accessed by hostname?
if ( strlen ( $bucket ) > 63 || ! preg_match ( " /[^a-z0-9 \ .-]/ " , $bucket )) return false ;
# A DNS bucket name cannot contain -.
2018-01-26 15:50:15 +01:00
if ( false !== strstr ( $bucket , '-.' )) return false ;
2016-11-28 21:52:15 -08:00
# A DNS bucket name cannot contain ..
2018-01-26 15:50:15 +01:00
if ( false !== strstr ( $bucket , '..' )) return false ;
2016-11-28 21:52:15 -08:00
# A DNS bucket name must begin with 0-9a-z
if ( ! preg_match ( " /^[0-9a-z]/ " , $bucket )) return false ;
# A DNS bucket name must end with 0-9 a-z
if ( ! preg_match ( " /[0-9a-z] $ / " , $bucket )) return false ;
return true ;
}
/**
2018-01-26 15:50:15 +01:00
* CURL header callback
*
* @ param resource $curl CURL resource
* @ param string $data Data
* @ return integer
*/
private function __responseHeaderCallback ( $curl , $data ) {
2016-11-28 21:52:15 -08:00
if (( $strlen = strlen ( $data )) <= 2 ) return $strlen ;
2018-01-26 15:50:15 +01:00
if ( 'HTTP' == substr ( $data , 0 , 4 )) {
2016-11-28 21:52:15 -08:00
$this -> response -> code = ( int ) substr ( $data , 9 , 3 );
2018-01-26 15:50:15 +01:00
} else {
2016-11-28 21:52:15 -08:00
$data = trim ( $data );
2018-01-26 15:50:15 +01:00
if ( false === strpos ( $data , ': ' )) return $strlen ;
2016-11-28 21:52:15 -08:00
list ( $header , $value ) = explode ( ': ' , $data , 2 );
2018-01-26 15:50:15 +01:00
if ( 'last-modified' == strtolower ( $header ))
2016-11-28 21:52:15 -08:00
$this -> response -> headers [ 'time' ] = strtotime ( $value );
2018-01-26 15:50:15 +01:00
elseif ( 'content-length' == strtolower ( $header ))
2016-11-28 21:52:15 -08:00
$this -> response -> headers [ 'size' ] = ( int ) $value ;
2018-01-26 15:50:15 +01:00
elseif ( 'content-type' == strtolower ( $header ))
2016-11-28 21:52:15 -08:00
$this -> response -> headers [ 'type' ] = $value ;
2018-01-26 15:50:15 +01:00
elseif ( 'etag' == strtolower ( $header ))
$this -> response -> headers [ 'hash' ] = '"' == $value { 0 } ? substr ( $value , 1 , - 1 ) : $value ;
2016-11-28 21:52:15 -08:00
elseif ( preg_match ( '/^x-amz-meta-.*$/i' , $header ))
$this -> response -> headers [ strtolower ( $header )] = $value ;
}
return $strlen ;
}
}