Repository: smaranchand/bucky
Branch: master
Commit: bda01360514f
Files: 32
Total size: 368.1 KB
Directory structure:
gitextract_3t64hr3k/
├── .github/
│ └── ISSUE_TEMPLATE/
│ ├── bug_report.md
│ └── feature_request.md
├── README.md
├── addon/
│ ├── bucky.js
│ └── manifest.json
├── all_buckets.php
├── config.inc.php
├── dbreset.php
├── index.php
├── lib/
│ └── requestcore/
│ └── requestcore.class.php
├── manual_check.php
├── process.php
├── reset.sh
├── run.sh
├── sdk.class.php
├── services/
│ └── s3.class.php
└── utilities/
├── array.class.php
├── batchrequest.class.php
├── complextype.class.php
├── credential.class.php
├── credentials.class.php
├── gzipdecode.class.php
├── info.class.php
├── json.class.php
├── manifest.class.php
├── mimetypes.class.php
├── policy.class.php
├── request.class.php
├── response.class.php
├── simplexml.class.php
├── stepconfig.class.php
└── utilities.class.php
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
================================================
FILE: README.md
================================================

# Project is on temporary hold.
# Bucky
Bucky is an automatic tool designed to discover S3 bucket misconfiguration, Bucky consists up of two modules Bucky firefox addon and Bucky backend engine. Bucky addon reads the source code of the webpages and uses Regular Expression(Regex) to match the S3 bucket used as Content Delivery Network(CDN) and sends it to the Bucky Backend engine. The backend engine receives the data from addon and checks if the S3 bucket is publicly writeable or not. Bucky automatically uploads a text file as Proof Of Concept(PoC) if the bucket is vulnerable.
# Working
Bucky addon sends the details of s3 bucket name discovered from a user visited web pages to backend engine.
It uses [AWS PHP SDK](https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/getting-started_installation.html) to discover misconfiguration.
Users can also check for S3 bucket misconfiguration manually. All the results from automatic and manuall check are populated to dashboard.
Checkout video https://vimeo.com/444442588
# Installation
```
git clone https://github.com/smaranchand/bucky.git
cd bucky
```
Requirements: AWS Access Keys and PHP installation
Get AWS Access Keys: https://console.aws.amazon.com/iam/home?#/security_credentials
PHP installation: Install according to your OS, apt install php7.3 / brew install php7.3
Currently, Bucky addon is not published in the Firefox addon store; as soon as the addon will be published, the addon link will be provided.
For now, users can manually load the addon into the browser to do so
1. Open Firefox browser and visit about:debugging
2. Click on "This Firefox" > Load Temporary Add-on
3. Select the addon located at bucky/addon/bucky.js
Add AWS Access keys:
```
cd bucky/
nano config.inc.php
Add your AWS Access Key ID and Secret Access Key. (On-Line 57 and 61)
```
# Usage
To use Bucky, load the Bucky addon to the browser and start backend engine.
```
cd bucky/
chmod +x run.sh
./run.sh
The backend engine runs on http://127.0.0.1:13337
Browse websites, Bucky will discover S3 buckets automatically and will be reflected in the dashboard.
Visit the above address to access Bucky dashboard.
```
# Screenshots
Running Bucky

Loading Addon

User Interface

All Buckets

Manual Check

POC By Bucky

# Note
Bucky is not a perfect tool to discover S3 buckets, it is well known that Bucky lacks many feautres and it may fail to detect the misconfiguration sometimes. Certain changes and development are in pipeline. I really appreciate the feedbacks and contribution.
================================================
FILE: addon/bucky.js
================================================
//If you think we can improve Bucky, then suggestions are welcomed.
var sourcecode = document.documentElement.outerHTML;
var pageurl= document.URL;
var hostname= window.location.hostname;
var s3bucketurl= sourcecode.match(/https:\/\/[a-z0-9-.]{3,63}\.s3\.([a-z0-9-]{5,19}\.|)amazonaws\.com/g);
if (s3bucketurl==null)
{
var s3bucketname= sourcecode.match(/(?<=\/\/s3\.amazonaws.com\/)[a-z0-9-.]{3,63}(?=\/)/);
if (s3bucketname==null)
{
}
else
sendBucket();
}
else
{
var s3bucketname= String(s3bucketurl[0]).match(/(?<=\/\/).*(?=\.s3)/);
sendBucket();
}
function sendBucket() {
var xhr = new XMLHttpRequest();
xhr.open("POST", "http://127.0.0.1:13337/process.php", true);
xhr.setRequestHeader("Accept-Language", "en-US,en;q=0.5");
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
var body = ("bucketname="+s3bucketname+"&sourceurl="+pageurl+"&hostname="+hostname);
xhr.send(body);
}
================================================
FILE: addon/manifest.json
================================================
{
"manifest_version": 2,
"name": "Bucky",
"version": "1.0",
"description": "Bucky discovers AWS S3 buckets from the web pages and sends it to the backend engine to check for misconfiguration.",
"icons": {
"48": "bucky.png"
},
"content_scripts": [
{
"matches": ["*://*/*"],
"js": ["bucky.js"]
}
],
"permissions": [
"http://127.0.0.1/*",
"webRequest"
]
}
================================================
FILE: all_buckets.php
================================================
All Buckets
| SN |
Bucket Name |
Source URL |
IP |
POC |
VULNERABLE |
open('bucky.db');
}
}
$db = new MyDB();
if(!$db) {
echo $db->lastErrorMsg();
} else {
}
$sql =<<query($sql);
while($row = $ret->fetchArray(SQLITE3_ASSOC) ) {
echo "";
echo "| ". $row['ID'] . " | ";
echo "". $row['BUCKETNAME'] . " | ";
echo "". $row['URL'] ." | ";
echo "". $row['IP'] ." | ";
echo "". $row['POC'] ." | ";
echo "".$row['VULNERABLE'] ." | ";
}
$db->close();
?>
================================================
FILE: config.inc.php
================================================
array(
// Amazon Web Services Key. Found in the AWS Security Credentials. You can also pass
// this value as the first parameter to a service constructor.
'key' => 'YOUR_AWS_KEY',
// Amazon Web Services Secret Key. Found in the AWS Security Credentials. You can also
// pass this value as the second parameter to a service constructor.
'secret' => 'YOUR_SECRET',
// This option allows you to configure a preferred storage type to use for caching by
// default. This can be changed later using the set_cache_config() method.
//
// Valid values are: `apc`, `xcache`, or a file system path such as `./cache` or
// `/tmp/cache/`.
'default_cache_config' => '',
// Determines which Cerificate Authority file to use.
//
// A value of boolean `false` will use the Certificate Authority file available on the
// system. A value of boolean `true` will use the Certificate Authority provided by the
// SDK. Passing a file system path to a Certificate Authority file (chmodded to `0755`)
// will use that.
//
// Leave this set to `false` if you're not sure.
'certificate_authority' => false
),
// Specify a default credential set to use if there are more than one.
'@default' => 'development'
));
================================================
FILE: dbreset.php
================================================
open('bucky.db');
}
}
$db = new MyDB();
if(!$db) {
echo $db->lastErrorMsg();
} else {
$sql =<<exec($sql);
if(!$ret){
echo $db->lastErrorMsg();
} else {
echo "Database setup successful.";
}
$db->close();
}
?>
================================================
FILE: index.php
================================================
Bucky
================================================
FILE: lib/requestcore/requestcore.class.php
================================================
).
*/
public $request_class = 'RequestCore';
/**
* The default class to use for HTTP Responses (defaults to ).
*/
public $response_class = 'ResponseCore';
/**
* Default useragent string to use.
*/
public $useragent = 'RequestCore/1.4.4';
/**
* File to read from while streaming up.
*/
public $read_file = null;
/**
* The resource to read from while streaming up.
*/
public $read_stream = null;
/**
* The size of the stream to read from.
*/
public $read_stream_size = null;
/**
* The length already read from the stream.
*/
public $read_stream_read = 0;
/**
* File to write to while streaming down.
*/
public $write_file = null;
/**
* The resource to write to while streaming down.
*/
public $write_stream = null;
/**
* Stores the intended starting seek position.
*/
public $seek_position = null;
/**
* The location of the cacert.pem file to use.
*/
public $cacert_location = false;
/**
* The state of SSL certificate verification.
*/
public $ssl_verification = true;
/**
* The user-defined callback function to call when a stream is read from.
*/
public $registered_streaming_read_callback = null;
/**
* The user-defined callback function to call when a stream is written to.
*/
public $registered_streaming_write_callback = null;
/**
* Whether or not the set_time_limit function should be called.
*/
public $allow_set_time_limit = true;
/**
* Whether or not to use gzip encoding via CURLOPT_ENCODING
*/
public $use_gzip_enconding = true;
/*%******************************************************************************************%*/
// CONSTANTS
/**
* GET HTTP Method
*/
const HTTP_GET = 'GET';
/**
* POST HTTP Method
*/
const HTTP_POST = 'POST';
/**
* PUT HTTP Method
*/
const HTTP_PUT = 'PUT';
/**
* DELETE HTTP Method
*/
const HTTP_DELETE = 'DELETE';
/**
* HEAD HTTP Method
*/
const HTTP_HEAD = 'HEAD';
/*%******************************************************************************************%*/
// CONSTRUCTOR/DESTRUCTOR
/**
* Constructs a new instance of this class.
*
* @param string $url (Optional) The URL to request or service endpoint to query.
* @param string $proxy (Optional) The faux-url to use for proxy settings. Takes the following format: `proxy://user:pass@hostname:port`
* @param array $helpers (Optional) An associative array of classnames to use for request, and response functionality. Gets passed in automatically by the calling class.
* @return $this A reference to the current instance.
*/
public function __construct($url = null, $proxy = null, $helpers = null)
{
// Set some default values.
$this->request_url = $url;
$this->method = self::HTTP_GET;
$this->request_headers = array();
$this->request_body = '';
// Determine if set_time_limit can be called
if (strpos(ini_get('disable_functions'), 'set_time_limit') !== false)
{
$this->allow_set_time_limit = false;
}
// Set a new Request class if one was set.
if (isset($helpers['request']) && !empty($helpers['request']))
{
$this->request_class = $helpers['request'];
}
// Set a new Request class if one was set.
if (isset($helpers['response']) && !empty($helpers['response']))
{
$this->response_class = $helpers['response'];
}
if ($proxy)
{
$this->set_proxy($proxy);
}
return $this;
}
/**
* Destructs the instance. Closes opened file handles.
*
* @return $this A reference to the current instance.
*/
public function __destruct()
{
if (isset($this->read_file) && isset($this->read_stream))
{
fclose($this->read_stream);
}
if (isset($this->write_file) && isset($this->write_stream))
{
fclose($this->write_stream);
}
return $this;
}
/*%******************************************************************************************%*/
// REQUEST METHODS
/**
* Sets the credentials to use for authentication.
*
* @param string $user (Required) The username to authenticate with.
* @param string $pass (Required) The password to authenticate with.
* @return $this A reference to the current instance.
*/
public function set_credentials($user, $pass)
{
$this->username = $user;
$this->password = $pass;
return $this;
}
/**
* Adds a custom HTTP header to the cURL request.
*
* @param string $key (Required) The custom HTTP header to set.
* @param mixed $value (Required) The value to assign to the custom HTTP header.
* @return $this A reference to the current instance.
*/
public function add_header($key, $value)
{
$this->request_headers[$key] = $value;
return $this;
}
/**
* Removes an HTTP header from the cURL request.
*
* @param string $key (Required) The custom HTTP header to set.
* @return $this A reference to the current instance.
*/
public function remove_header($key)
{
if (isset($this->request_headers[$key]))
{
unset($this->request_headers[$key]);
}
return $this;
}
/**
* Set the method type for the request.
*
* @param string $method (Required) One of the following constants: , , , , .
* @return $this A reference to the current instance.
*/
public function set_method($method)
{
$this->method = strtoupper($method);
return $this;
}
/**
* Sets a custom useragent string for the class.
*
* @param string $ua (Required) The useragent string to use.
* @return $this A reference to the current instance.
*/
public function set_useragent($ua)
{
$this->useragent = $ua;
return $this;
}
/**
* Set the body to send in the request.
*
* @param string $body (Required) The textual content to send along in the body of the request.
* @return $this A reference to the current instance.
*/
public function set_body($body)
{
$this->request_body = $body;
return $this;
}
/**
* Set the URL to make the request to.
*
* @param string $url (Required) The URL to make the request to.
* @return $this A reference to the current instance.
*/
public function set_request_url($url)
{
$this->request_url = $url;
return $this;
}
/**
* Set additional CURLOPT settings. These will merge with the default settings, and override if
* there is a duplicate.
*
* @param array $curlopts (Optional) A set of key-value pairs that set `CURLOPT` options. These will merge with the existing CURLOPTs, and ones passed here will override the defaults. Keys should be the `CURLOPT_*` constants, not strings.
* @return $this A reference to the current instance.
*/
public function set_curlopts($curlopts)
{
$this->curlopts = $curlopts;
return $this;
}
/**
* Sets the length in bytes to read from the stream while streaming up.
*
* @param integer $size (Required) The length in bytes to read from the stream.
* @return $this A reference to the current instance.
*/
public function set_read_stream_size($size)
{
$this->read_stream_size = $size;
return $this;
}
/**
* Sets the resource to read from while streaming up. Reads the stream from its current position until
* EOF or `$size` bytes have been read. If `$size` is not given it will be determined by and
* .
*
* @param resource $resource (Required) The readable resource to read from.
* @param integer $size (Optional) The size of the stream to read.
* @return $this A reference to the current instance.
*/
public function set_read_stream($resource, $size = null)
{
if (!isset($size) || $size < 0)
{
$stats = fstat($resource);
if ($stats && $stats['size'] >= 0)
{
$position = ftell($resource);
if ($position !== false && $position >= 0)
{
$size = $stats['size'] - $position;
}
}
}
$this->read_stream = $resource;
return $this->set_read_stream_size($size);
}
/**
* Sets the file to read from while streaming up.
*
* @param string $location (Required) The readable location to read from.
* @return $this A reference to the current instance.
*/
public function set_read_file($location)
{
$this->read_file = $location;
$read_file_handle = fopen($location, 'r');
return $this->set_read_stream($read_file_handle);
}
/**
* Sets the resource to write to while streaming down.
*
* @param resource $resource (Required) The writeable resource to write to.
* @return $this A reference to the current instance.
*/
public function set_write_stream($resource)
{
$this->write_stream = $resource;
return $this;
}
/**
* Sets the file to write to while streaming down.
*
* @param string $location (Required) The writeable location to write to.
* @return $this A reference to the current instance.
*/
public function set_write_file($location)
{
$this->write_file = $location;
$write_file_handle = fopen($location, 'w');
return $this->set_write_stream($write_file_handle);
}
/**
* Set the proxy to use for making requests.
*
* @param string $proxy (Required) The faux-url to use for proxy settings. Takes the following format: `proxy://user:pass@hostname:port`
* @return $this A reference to the current instance.
*/
public function set_proxy($proxy)
{
$proxy = parse_url($proxy);
$proxy['user'] = isset($proxy['user']) ? $proxy['user'] : null;
$proxy['pass'] = isset($proxy['pass']) ? $proxy['pass'] : null;
$proxy['port'] = isset($proxy['port']) ? $proxy['port'] : null;
$this->proxy = $proxy;
return $this;
}
/**
* Set the intended starting seek position.
*
* @param integer $position (Required) The byte-position of the stream to begin reading from.
* @return $this A reference to the current instance.
*/
public function set_seek_position($position)
{
$this->seek_position = isset($position) ? (integer) $position : null;
return $this;
}
/**
* Register a callback function to execute whenever a data stream is read from using
* .
*
* The user-defined callback function should accept three arguments:
*
*
* $curl_handle - resource - Required - The cURL handle resource that represents the in-progress transfer.
* $file_handle - resource - Required - The file handle resource that represents the file on the local file system.
* $length - integer - Required - The length in kilobytes of the data chunk that was transferred.
*
*
* @param string|array|function $callback (Required) The callback function is called by , so you can pass the following values:
* - The name of a global function to execute, passed as a string.
* - A method to execute, passed as
array('ClassName', 'MethodName').
* - An anonymous function (PHP 5.3+).
* @return $this A reference to the current instance.
*/
public function register_streaming_read_callback($callback)
{
$this->registered_streaming_read_callback = $callback;
return $this;
}
/**
* Register a callback function to execute whenever a data stream is written to using
* .
*
* The user-defined callback function should accept two arguments:
*
*
* $curl_handle - resource - Required - The cURL handle resource that represents the in-progress transfer.
* $length - integer - Required - The length in kilobytes of the data chunk that was transferred.
*
*
* @param string|array|function $callback (Required) The callback function is called by , so you can pass the following values:
* - The name of a global function to execute, passed as a string.
* - A method to execute, passed as
array('ClassName', 'MethodName').
* - An anonymous function (PHP 5.3+).
* @return $this A reference to the current instance.
*/
public function register_streaming_write_callback($callback)
{
$this->registered_streaming_write_callback = $callback;
return $this;
}
/*%******************************************************************************************%*/
// PREPARE, SEND, AND PROCESS REQUEST
/**
* A callback function that is invoked by cURL for streaming up.
*
* @param resource $curl_handle (Required) The cURL handle for the request.
* @param resource $file_handle (Required) The open file handle resource.
* @param integer $length (Required) The maximum number of bytes to read.
* @return binary Binary data from a stream.
*/
public function streaming_read_callback($curl_handle, $file_handle, $length)
{
// Once we've sent as much as we're supposed to send...
if ($this->read_stream_read >= $this->read_stream_size)
{
// Send EOF
return '';
}
// If we're at the beginning of an upload and need to seek...
if ($this->read_stream_read == 0 && isset($this->seek_position) && $this->seek_position !== ftell($this->read_stream))
{
if (fseek($this->read_stream, $this->seek_position) !== 0)
{
throw new RequestCore_Exception('The stream does not support seeking and is either not at the requested position or the position is unknown.');
}
}
$read = fread($this->read_stream, min($this->read_stream_size - $this->read_stream_read, $length)); // Remaining upload data or cURL's requested chunk size
$this->read_stream_read += strlen($read);
$out = $read === false ? '' : $read;
// Execute callback function
if ($this->registered_streaming_read_callback)
{
call_user_func($this->registered_streaming_read_callback, $curl_handle, $file_handle, $out);
}
return $out;
}
/**
* A callback function that is invoked by cURL for streaming down.
*
* @param resource $curl_handle (Required) The cURL handle for the request.
* @param binary $data (Required) The data to write.
* @return integer The number of bytes written.
*/
public function streaming_write_callback($curl_handle, $data)
{
$length = strlen($data);
$written_total = 0;
$written_last = 0;
while ($written_total < $length)
{
$written_last = fwrite($this->write_stream, substr($data, $written_total));
if ($written_last === false)
{
return $written_total;
}
$written_total += $written_last;
}
// Execute callback function
if ($this->registered_streaming_write_callback)
{
call_user_func($this->registered_streaming_write_callback, $curl_handle, $written_total);
}
return $written_total;
}
/**
* Prepares and adds the details of the cURL request. This can be passed along to a
* function.
*
* @return resource The handle for the cURL object.
*/
public function prep_request()
{
$curl_handle = curl_init();
// Set default options.
curl_setopt($curl_handle, CURLOPT_URL, $this->request_url);
curl_setopt($curl_handle, CURLOPT_FILETIME, true);
curl_setopt($curl_handle, CURLOPT_FRESH_CONNECT, false);
curl_setopt($curl_handle, CURLOPT_CLOSEPOLICY, CURLCLOSEPOLICY_LEAST_RECENTLY_USED);
curl_setopt($curl_handle, CURLOPT_MAXREDIRS, 5);
curl_setopt($curl_handle, CURLOPT_HEADER, true);
curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl_handle, CURLOPT_TIMEOUT, 5184000);
curl_setopt($curl_handle, CURLOPT_CONNECTTIMEOUT, 120);
curl_setopt($curl_handle, CURLOPT_NOSIGNAL, true);
curl_setopt($curl_handle, CURLOPT_REFERER, $this->request_url);
curl_setopt($curl_handle, CURLOPT_USERAGENT, $this->useragent);
curl_setopt($curl_handle, CURLOPT_READFUNCTION, array($this, 'streaming_read_callback'));
// Verification of the SSL cert
if ($this->ssl_verification)
{
curl_setopt($curl_handle, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($curl_handle, CURLOPT_SSL_VERIFYHOST, 2);
}
else
{
curl_setopt($curl_handle, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl_handle, CURLOPT_SSL_VERIFYHOST, false);
}
// chmod the file as 0755
if ($this->cacert_location === true)
{
curl_setopt($curl_handle, CURLOPT_CAINFO, dirname(__FILE__) . '/cacert.pem');
}
elseif (is_string($this->cacert_location))
{
curl_setopt($curl_handle, CURLOPT_CAINFO, $this->cacert_location);
}
// Debug mode
if ($this->debug_mode)
{
curl_setopt($curl_handle, CURLOPT_VERBOSE, true);
}
// Handle open_basedir & safe mode
if (!ini_get('safe_mode') && !ini_get('open_basedir'))
{
curl_setopt($curl_handle, CURLOPT_FOLLOWLOCATION, true);
}
// Enable a proxy connection if requested.
if ($this->proxy)
{
curl_setopt($curl_handle, CURLOPT_HTTPPROXYTUNNEL, true);
$host = $this->proxy['host'];
$host .= ($this->proxy['port']) ? ':' . $this->proxy['port'] : '';
curl_setopt($curl_handle, CURLOPT_PROXY, $host);
if (isset($this->proxy['user']) && isset($this->proxy['pass']))
{
curl_setopt($curl_handle, CURLOPT_PROXYUSERPWD, $this->proxy['user'] . ':' . $this->proxy['pass']);
}
}
// Set credentials for HTTP Basic/Digest Authentication.
if ($this->username && $this->password)
{
curl_setopt($curl_handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
curl_setopt($curl_handle, CURLOPT_USERPWD, $this->username . ':' . $this->password);
}
// Handle the encoding if we can.
if ($this->use_gzip_enconding && extension_loaded('zlib'))
{
curl_setopt($curl_handle, CURLOPT_ENCODING, 'gzip, deflate');
}
// Process custom headers
if (isset($this->request_headers) && count($this->request_headers))
{
$temp_headers = array();
if (!array_key_exists('Expect', $this->request_headers))
{
$this->request_headers['Expect'] = '';
}
foreach ($this->request_headers as $k => $v)
{
$temp_headers[] = $k . ': ' . $v;
}
curl_setopt($curl_handle, CURLOPT_HTTPHEADER, $temp_headers);
}
switch ($this->method)
{
case self::HTTP_PUT:
curl_setopt($curl_handle, CURLOPT_CUSTOMREQUEST, 'PUT');
if (isset($this->read_stream))
{
if (!isset($this->read_stream_size) || $this->read_stream_size < 0)
{
throw new RequestCore_Exception('The stream size for the streaming upload cannot be determined.');
}
curl_setopt($curl_handle, CURLOPT_INFILESIZE, $this->read_stream_size);
curl_setopt($curl_handle, CURLOPT_UPLOAD, true);
}
else
{
curl_setopt($curl_handle, CURLOPT_POSTFIELDS, $this->request_body);
}
break;
case self::HTTP_POST:
curl_setopt($curl_handle, CURLOPT_POST, true);
curl_setopt($curl_handle, CURLOPT_POSTFIELDS, $this->request_body);
break;
case self::HTTP_HEAD:
curl_setopt($curl_handle, CURLOPT_CUSTOMREQUEST, self::HTTP_HEAD);
curl_setopt($curl_handle, CURLOPT_NOBODY, 1);
break;
default: // Assumed GET
curl_setopt($curl_handle, CURLOPT_CUSTOMREQUEST, $this->method);
if (isset($this->write_stream))
{
curl_setopt($curl_handle, CURLOPT_WRITEFUNCTION, array($this, 'streaming_write_callback'));
curl_setopt($curl_handle, CURLOPT_HEADER, false);
}
else
{
curl_setopt($curl_handle, CURLOPT_POSTFIELDS, $this->request_body);
}
break;
}
// Merge in the CURLOPTs
if (isset($this->curlopts) && sizeof($this->curlopts) > 0)
{
foreach ($this->curlopts as $k => $v)
{
curl_setopt($curl_handle, $k, $v);
}
}
return $curl_handle;
}
/**
* Take the post-processed cURL data and break it down into useful header/body/info chunks. Uses the
* data stored in the `curl_handle` and `response` properties unless replacement data is passed in via
* parameters.
*
* @param resource $curl_handle (Optional) The reference to the already executed cURL request.
* @param string $response (Optional) The actual response content itself that needs to be parsed.
* @return ResponseCore A object containing a parsed HTTP response.
*/
public function process_response($curl_handle = null, $response = null)
{
// Accept a custom one if it's passed.
if ($curl_handle && $response)
{
$this->curl_handle = $curl_handle;
$this->response = $response;
}
// As long as this came back as a valid resource...
if (is_resource($this->curl_handle))
{
// Determine what's what.
$header_size = curl_getinfo($this->curl_handle, CURLINFO_HEADER_SIZE);
$this->response_headers = substr($this->response, 0, $header_size);
$this->response_body = substr($this->response, $header_size);
$this->response_code = curl_getinfo($this->curl_handle, CURLINFO_HTTP_CODE);
$this->response_info = curl_getinfo($this->curl_handle);
// Parse out the headers
$this->response_headers = explode("\r\n\r\n", trim($this->response_headers));
$this->response_headers = array_pop($this->response_headers);
$this->response_headers = explode("\r\n", $this->response_headers);
array_shift($this->response_headers);
// Loop through and split up the headers.
$header_assoc = array();
foreach ($this->response_headers as $header)
{
$kv = explode(': ', $header);
$header_assoc[strtolower($kv[0])] = $kv[1];
}
// Reset the headers to the appropriate property.
$this->response_headers = $header_assoc;
$this->response_headers['_info'] = $this->response_info;
$this->response_headers['_info']['method'] = $this->method;
if ($curl_handle && $response)
{
return new $this->response_class($this->response_headers, $this->response_body, $this->response_code, $this->curl_handle);
}
}
// Return false
return false;
}
/**
* Sends the request, calling necessary utility functions to update built-in properties.
*
* @param boolean $parse (Optional) Whether to parse the response with ResponseCore or not.
* @return string The resulting unparsed data from the request.
*/
public function send_request($parse = false)
{
if ($this->allow_set_time_limit)
{
set_time_limit(0);
}
$curl_handle = $this->prep_request();
$this->response = curl_exec($curl_handle);
if ($this->response === false)
{
throw new cURL_Exception('cURL resource: ' . (string) $curl_handle . '; cURL error: ' . curl_error($curl_handle) . ' (cURL error code ' . curl_errno($curl_handle) . '). See http://curl.haxx.se/libcurl/c/libcurl-errors.html for an explanation of error codes.');
}
$parsed_response = $this->process_response($curl_handle, $this->response);
curl_close($curl_handle);
if ($parse)
{
return $parsed_response;
}
return $this->response;
}
/**
* Sends the request using , enabling parallel requests. Uses the "rolling" method.
*
* @param array $handles (Required) An indexed array of cURL handles to process simultaneously.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* callback - string|array - Optional - The string name of a function to pass the response data to. If this is a method, pass an array where the [0] index is the class and the [1] index is the method name.
* limit - integer - Optional - The number of simultaneous requests to make. This can be useful for scaling around slow server responses. Defaults to trusting cURLs judgement as to how many to use.
* @return array Post-processed cURL responses.
*/
public function send_multi_request($handles, $opt = null)
{
if ($this->allow_set_time_limit)
{
set_time_limit(0);
}
// Skip everything if there are no handles to process.
if (count($handles) === 0) return array();
if (!$opt) $opt = array();
// Initialize any missing options
$limit = isset($opt['limit']) ? $opt['limit'] : -1;
// Initialize
$handle_list = $handles;
$http = new $this->request_class();
$multi_handle = curl_multi_init();
$handles_post = array();
$added = count($handles);
$last_handle = null;
$count = 0;
$i = 0;
// Loop through the cURL handles and add as many as it set by the limit parameter.
while ($i < $added)
{
if ($limit > 0 && $i >= $limit) break;
curl_multi_add_handle($multi_handle, array_shift($handles));
$i++;
}
do
{
$active = false;
// Start executing and wait for a response.
while (($status = curl_multi_exec($multi_handle, $active)) === CURLM_CALL_MULTI_PERFORM)
{
// Start looking for possible responses immediately when we have to add more handles
if (count($handles) > 0) break;
}
// Figure out which requests finished.
$to_process = array();
while ($done = curl_multi_info_read($multi_handle))
{
// Since curl_errno() isn't reliable for handles that were in multirequests, we check the 'result' of the info read, which contains the curl error number, (listed here http://curl.haxx.se/libcurl/c/libcurl-errors.html )
if ($done['result'] > 0)
{
throw new cURL_Multi_Exception('cURL resource: ' . (string) $done['handle'] . '; cURL error: ' . curl_error($done['handle']) . ' (cURL error code ' . $done['result'] . '). See http://curl.haxx.se/libcurl/c/libcurl-errors.html for an explanation of error codes.');
}
// Because curl_multi_info_read() might return more than one message about a request, we check to see if this request is already in our array of completed requests
elseif (!isset($to_process[(int) $done['handle']]))
{
$to_process[(int) $done['handle']] = $done;
}
}
// Actually deal with the request
foreach ($to_process as $pkey => $done)
{
$response = $http->process_response($done['handle'], curl_multi_getcontent($done['handle']));
$key = array_search($done['handle'], $handle_list, true);
$handles_post[$key] = $response;
if (count($handles) > 0)
{
curl_multi_add_handle($multi_handle, array_shift($handles));
}
curl_multi_remove_handle($multi_handle, $done['handle']);
curl_close($done['handle']);
}
}
while ($active || count($handles_post) < $added);
curl_multi_close($multi_handle);
ksort($handles_post, SORT_NUMERIC);
return $handles_post;
}
/*%******************************************************************************************%*/
// RESPONSE METHODS
/**
* Get the HTTP response headers from the request.
*
* @param string $header (Optional) A specific header value to return. Defaults to all headers.
* @return string|array All or selected header values.
*/
public function get_response_header($header = null)
{
if ($header)
{
return $this->response_headers[strtolower($header)];
}
return $this->response_headers;
}
/**
* Get the HTTP response body from the request.
*
* @return string The response body.
*/
public function get_response_body()
{
return $this->response_body;
}
/**
* Get the HTTP response code from the request.
*
* @return string The HTTP response code.
*/
public function get_response_code()
{
return $this->response_code;
}
}
/**
* Container for all response-related methods.
*/
class ResponseCore
{
/**
* Stores the HTTP header information.
*/
public $header;
/**
* Stores the SimpleXML response.
*/
public $body;
/**
* Stores the HTTP response code.
*/
public $status;
/**
* Constructs a new instance of this class.
*
* @param array $header (Required) Associative array of HTTP headers (typically returned by ).
* @param string $body (Required) XML-formatted response from AWS.
* @param integer $status (Optional) HTTP response status code from the request.
* @return object Contains an `header` property (HTTP headers as an associative array), a or `body` property, and an `status` code.
*/
public function __construct($header, $body, $status = null)
{
$this->header = $header;
$this->body = $body;
$this->status = $status;
return $this;
}
/**
* Did we receive the status code we expected?
*
* @param integer|array $codes (Optional) The status code(s) to expect. Pass an for a single acceptable value, or an of integers for multiple acceptable values.
* @return boolean Whether we received the expected status code or not.
*/
public function isOK($codes = array(200, 201, 204, 206))
{
if (is_array($codes))
{
return in_array($this->status, $codes);
}
return $this->status === $codes;
}
}
class cURL_Exception extends Exception {}
class cURL_Multi_Exception extends cURL_Exception {}
class RequestCore_Exception extends Exception {}
================================================
FILE: manual_check.php
================================================
Manual Check
================================================
FILE: process.php
================================================
open('bucky.db');
}
}
$db = new MyDB();
if(!$db){
echo $db->lastErrorMsg();
} else {
}
$sql =<<YES' );
EOF;
$ret = $db->exec($sql);
if(!$ret) {
echo $db->lastErrorMsg();
} else {
header("refresh:3; url=all_buckets.php");
echo "S3 Bucket $newbucket seems vulnerable, Check dashboard for more information.";
}
$db->close();
}
function bucketfailed(){
$newbucket=$_POST['bucketname'];
$sourceurl=$_POST['sourceurl'];
$hostname=$_POST['hostname'];
$serverip= gethostbyname("$hostname");
class MyDB extends SQLite3 {
function __construct() {
$this->open('bucky.db');
}
}
$db = new MyDB();
if(!$db){
echo $db->lastErrorMsg();
} else {
}
$sql =<<exec($sql);
if(!$ret) {
echo $db->lastErrorMsg();
} else {
header("refresh:3; url=manual_check.php");
echo "S3 Bucket $newbucket doesnot seems vulnerable, Check dashboard for more information.";
}
$db->close();
}
$newbucket=$_POST['bucketname'];
error_reporting(0);
require_once 'sdk.class.php';
$s3 = new AmazonS3();
$response = $s3->create_object($newbucket, 'bucky.txt', array(
'contentType' => 'text/plain',
'body' => 'S3 bucket misconfiguration is discovered.
Discovered by Bucky.
Developed by: https://twitter.com/smaranchand',
'acl'=>AmazonS3::ACL_PUBLIC,
));
if ((int) $response->isOK())
bucketsuccess();
else
bucketfailed();
?>
================================================
FILE: reset.sh
================================================
#!/bin/bash
rm bucky.db
php dbreset.php
================================================
FILE: run.sh
================================================
#!/bin/bash
#Bucky is AWS S3 bucket discovery tool developed by https://twitter.com/smaranchand
#Bucky Backend Engine automatically takes input from Bucky addon and check for misconfiguration.
#Download Bucky aaddon from https://addons.mozilla.org/en-US/firefox/addon/bucky/
#No changes done.
echo "
____ _ __ ___
| _ \ | | /_ | / _ \
| |_) |_ _ ___| | ___ _ __ _| || | | |
| _ <| | | |/ __| |/ / | | | \ \ / / || | | |
| |_) | |_| | (__| <| |_| | \ V /| || |_| |
|____/ \__,_|\___|_|\_\\__, | \_/ |_(_)___/
__/ |
|___/
An automatic tool to find misconfigured AWS S3 buckets.
Developed by : https://twitter.com/smaranchand
";
php -S 127.0.0.1:13337
================================================
FILE: sdk.class.php
================================================
).
*/
public $utilities_class = 'CFUtilities';
/**
* The default class to use for HTTP requests (defaults to ).
*/
public $request_class = 'CFRequest';
/**
* The default class to use for HTTP responses (defaults to ).
*/
public $response_class = 'CFResponse';
/**
* The default class to use for parsing XML (defaults to ).
*/
public $parser_class = 'CFSimpleXML';
/**
* The default class to use for handling batch requests (defaults to ).
*/
public $batch_class = 'CFBatchRequest';
/**
* The state of SSL/HTTPS use.
*/
public $use_ssl = true;
/**
* The state of SSL certificate verification.
*/
public $ssl_verification = true;
/**
* The proxy to use for connecting.
*/
public $proxy = null;
/**
* The alternate hostname to use, if any.
*/
public $hostname = null;
/**
* The state of the capability to override the hostname with .
*/
public $override_hostname = true;
/**
* The alternate port number to use, if any.
*/
public $port_number = null;
/**
* The alternate resource prefix to use, if any.
*/
public $resource_prefix = null;
/**
* The state of cache flow usage.
*/
public $use_cache_flow = false;
/**
* The caching class to use.
*/
public $cache_class = null;
/**
* The caching location to use.
*/
public $cache_location = null;
/**
* When the cache should be considered stale.
*/
public $cache_expires = null;
/**
* The state of cache compression.
*/
public $cache_compress = null;
/**
* The current instantiated cache object.
*/
public $cache_object = null;
/**
* The current instantiated batch request object.
*/
public $batch_object = null;
/**
* The internally instantiated batch request object.
*/
public $internal_batch_object = null;
/**
* The state of batch flow usage.
*/
public $use_batch_flow = false;
/**
* The state of the cache deletion setting.
*/
public $delete_cache = false;
/**
* The state of the debug mode setting.
*/
public $debug_mode = false;
/**
* The number of times to retry failed requests.
*/
public $max_retries = 3;
/**
* The user-defined callback function to call when a stream is read from.
*/
public $registered_streaming_read_callback = null;
/**
* The user-defined callback function to call when a stream is written to.
*/
public $registered_streaming_write_callback = null;
/**
* The credentials to use for authentication.
*/
public $credentials = array();
/**
* The authentication class to use.
*/
public $auth_class = null;
/**
* The operation to execute.
*/
public $operation = null;
/**
* The payload to send.
*/
public $payload = array();
/**
* The string prefix to prepend to the operation name.
*/
public $operation_prefix = '';
/**
* The number of times a request has been retried.
*/
public $redirects = 0;
/**
* The state of whether the response should be parsed or not.
*/
public $parse_the_response = true;
/*%******************************************************************************************%*/
// CONSTRUCTOR
/**
* The constructor. This class should not be instantiated directly. Rather, a service-specific class
* should be instantiated.
*
* @param array $options (Optional) An associative array of parameters that can have the following keys:
* certificate_authority - boolean - Optional - Determines which Cerificate Authority file to use. A value of boolean false will use the Certificate Authority file available on the system. A value of boolean true will use the Certificate Authority provided by the SDK. Passing a file system path to a Certificate Authority file (chmodded to 0755) will use that. Leave this set to false if you're not sure.
* credentials - string - Optional - The name of the credential set to use for authentication.
* default_cache_config - string - Optional - This option allows a preferred storage type to be configured for long-term caching. This can be changed later using the method. Valid values are: apc, xcache, or a file system path such as ./cache or /tmp/cache/.
* key - string - Optional - Your AWS key, or a session key. If blank, the default credential set will be used.
* instance_profile_timeout - integer - Optional - When retrieving IAM instance profile credentials, there is a hard connection timeout that defaults to 2 seconds to prevent unnecessary on non-EC2 systems. This setting allows you to change that timeout if needed.
* secret - string - Optional - Your AWS secret key, or a session secret key. If blank, the default credential set will be used.
* token - string - Optional - An AWS session token.
* use_instance_profile_credentials - boolean - Optional - Forces the use of IAM Instance Profile credentials, even when regular credentials are provided.
* @return void
*/
public function __construct(array $options = array())
{
// Instantiate the utilities class.
$this->util = new $this->utilities_class();
// Determine the current service.
$this->service = get_class($this);
// Create credentials based on the options
$runtime_credentials = new CFCredential($options);
$credentials_provided = false;
// Retrieve a credential set from config.inc.php if it exists
if (isset($options['credentials']))
{
// Use a specific credential set and merge with the runtime credentials
$this->credentials = CFCredentials::get($options['credentials'])
->merge($runtime_credentials);
}
else
{
try
{
// Use the default credential set and merge with the runtime credentials
$this->credentials = CFCredentials::get(CFCredentials::DEFAULT_KEY)
->merge($runtime_credentials);
}
catch (CFCredentials_Exception $e)
{
// Only the runtime credentials were provided
$this->credentials = $runtime_credentials;
}
}
// Check if keys were actually provided
if (isset($this->credentials['key']) && isset($this->credentials['secret']))
{
$credentials_provided = true;
}
// Check for an instance profile credentials override
if (isset($this->credentials['use_instance_profile_credentials']) && $this->credentials['use_instance_profile_credentials'])
{
$credentials_provided = false;
}
// Automatically enable whichever caching mechanism is set to default.
$this->set_cache_config($this->credentials->default_cache_config);
// If no credentials were provided, try to get them from the EC2 instance profile
if (!$credentials_provided)
{
// Default caching mechanism is required
if (!$this->credentials->default_cache_config)
{
// @codeCoverageIgnoreStart
throw new CFCredentials_Exception('No credentials were provided. The SDK attempts to retrieve Instance '
. 'Profile credentials from the EC2 Instance Metadata Service, but doing this requires the '
. '"default_cache_config" option to be set in the config.inc.php file or constructor. In order to '
. 'cache the retrieved credentials.');
// @codeCoverageIgnoreEnd
}
// Instantiate and invoke the cache for instance profile credentials
$cache = new $this->cache_class('instance_profile_credentials', $this->cache_location, 0, $this->cache_compress);
if ($data = $cache->read())
{
$cache->expire_in((strtotime($data['expires']) - time()) * 0.85);
}
$instance_profile_credentials = $cache->response_manager(array($this, 'cache_instance_profile_credentials'), array($cache, $options));
$this->credentials->key = $instance_profile_credentials['key'];
$this->credentials->secret = $instance_profile_credentials['secret'];
$this->credentials->token = $instance_profile_credentials['token'];
}
// Set internal credentials after they are resolved
$this->key = $this->credentials->key;
$this->secret_key = $this->credentials->secret;
$this->auth_token = $this->credentials->token;
}
/**
* Alternate approach to constructing a new instance. Supports chaining.
*
* @param array $options (Optional) An associative array of parameters that can have the following keys:
* certificate_authority - boolean - Optional - Determines which Cerificate Authority file to use. A value of boolean false will use the Certificate Authority file available on the system. A value of boolean true will use the Certificate Authority provided by the SDK. Passing a file system path to a Certificate Authority file (chmodded to 0755) will use that. Leave this set to false if you're not sure.
* credentials - string - Optional - The name of the credential set to use for authentication.
* default_cache_config - string - Optional - This option allows a preferred storage type to be configured for long-term caching. This can be changed later using the method. Valid values are: apc, xcache, or a file system path such as ./cache or /tmp/cache/.
* key - string - Optional - Your AWS key, or a session key. If blank, the default credential set will be used.
* secret - string - Optional - Your AWS secret key, or a session secret key. If blank, the default credential set will be used.
* token - string - Optional - An AWS session token.
* @return void
*/
public static function factory(array $options = array())
{
if (version_compare(PHP_VERSION, '5.3.0', '<'))
{
throw new Exception('PHP 5.3 or newer is required to instantiate a new class with CLASS::factory().');
}
$self = get_called_class();
return new $self($options);
}
/*%******************************************************************************************%*/
// MAGIC METHODS
/**
* A magic method that allows `camelCase` method names to be translated into `snake_case` names.
*
* @param string $name (Required) The name of the method.
* @param array $arguments (Required) The arguments passed to the method.
* @return mixed The results of the intended method.
*/
public function __call($name, $arguments)
{
// Convert camelCase method calls to snake_case.
$method_name = strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', $name));
if (method_exists($this, $method_name))
{
return call_user_func_array(array($this, $method_name), $arguments);
}
throw new CFRuntime_Exception('The method ' . $name . '() is undefined. Attempted to map to ' . $method_name . '() which is also undefined. Error occurred');
}
/*%******************************************************************************************%*/
// SET CUSTOM SETTINGS
/**
* Set the proxy settings to use.
*
* @param string $proxy (Required) Accepts proxy credentials in the following format: `proxy://user:pass@hostname:port`
* @return $this A reference to the current instance.
*/
public function set_proxy($proxy)
{
$this->proxy = $proxy;
return $this;
}
/**
* Set the hostname to connect to. This is useful for alternate services that are API-compatible with
* AWS, but run from a different hostname.
*
* @param string $hostname (Required) The alternate hostname to use in place of the default one. Useful for mock or test applications living on different hostnames.
* @param integer $port_number (Optional) The alternate port number to use in place of the default one. Useful for mock or test applications living on different port numbers.
* @return $this A reference to the current instance.
*/
public function set_hostname($hostname, $port_number = null)
{
if ($this->override_hostname)
{
$this->hostname = $hostname;
if ($port_number)
{
$this->port_number = $port_number;
$this->hostname .= ':' . (string) $this->port_number;
}
}
return $this;
}
/**
* Set the resource prefix to use. This method is useful for alternate services that are API-compatible
* with AWS.
*
* @param string $prefix (Required) An alternate prefix to prepend to the resource path. Useful for mock or test applications.
* @return $this A reference to the current instance.
*/
public function set_resource_prefix($prefix)
{
$this->resource_prefix = $prefix;
return $this;
}
/**
* Disables any subsequent use of the method.
*
* @param boolean $override (Optional) Whether or not subsequent calls to should be obeyed. A `false` value disables the further effectiveness of . Defaults to `true`.
* @return $this A reference to the current instance.
*/
public function allow_hostname_override($override = true)
{
$this->override_hostname = $override;
return $this;
}
/**
* Disables SSL/HTTPS connections for hosts that don't support them. Some services, however, still
* require SSL support.
*
* This method will throw a user warning when invoked, which can be hidden by changing your
* settings.
*
* @return $this A reference to the current instance.
*/
public function disable_ssl()
{
trigger_error('Disabling SSL connections is potentially unsafe and highly discouraged.', E_USER_WARNING);
$this->use_ssl = false;
return $this;
}
/**
* Disables the verification of the SSL Certificate Authority. Doing so can enable an attacker to carry
* out a man-in-the-middle attack.
*
* https://secure.wikimedia.org/wikipedia/en/wiki/Man-in-the-middle_attack
*
* This method will throw a user warning when invoked, which can be hidden by changing your
* settings.
*
* @return $this A reference to the current instance.
*/
public function disable_ssl_verification($ssl_verification = false)
{
trigger_error('Disabling the verification of SSL certificates can lead to man-in-the-middle attacks. It is potentially unsafe and highly discouraged.', E_USER_WARNING);
$this->ssl_verification = $ssl_verification;
return $this;
}
/**
* Enables HTTP request/response header logging to `STDERR`.
*
* @param boolean $enabled (Optional) Whether or not to enable debug mode. Defaults to `true`.
* @return $this A reference to the current instance.
*/
public function enable_debug_mode($enabled = true)
{
$this->debug_mode = $enabled;
return $this;
}
/**
* Sets the maximum number of times to retry failed requests.
*
* @param integer $retries (Optional) The maximum number of times to retry failed requests. Defaults to `3`.
* @return $this A reference to the current instance.
*/
public function set_max_retries($retries = 3)
{
$this->max_retries = $retries;
return $this;
}
/**
* Set the caching configuration to use for response caching.
*
* @param string $location (Required) The location to store the cache object in. This may vary by cache method.
- File - The local file system paths such as
./cache (relative) or /tmp/cache/ (absolute). The location must be server-writable. - APC - Pass in
apc to use this lightweight cache. You must have the APC extension installed. - XCache - Pass in
xcache to use this lightweight cache. You must have the XCache extension installed. - Memcached - Pass in an indexed array of associative arrays. Each associative array should have a
host and a port value representing a Memcached server to connect to. - PDO - A URL-style string (e.g.
pdo.mysql://user:pass@localhost/cache) or a standard DSN-style string (e.g. pdo.sqlite:/sqlite/cache.db). MUST be prefixed with pdo.. See CachePDO and PDO for more details.
* @param boolean $gzip (Optional) Whether or not data should be gzipped before being stored. A value of `true` will compress the contents before caching them. A value of `false` will leave the contents uncompressed. Defaults to `true`.
* @return $this A reference to the current instance.
*/
public function set_cache_config($location, $gzip = true)
{
// If location is empty, don't do anything.
if (empty($location))
{
return $this;
}
// If we have an array, we're probably passing in Memcached servers and ports.
if (is_array($location))
{
$this->cache_class = 'CacheMC';
}
else
{
// I would expect locations like `/tmp/cache`, `pdo.mysql://user:pass@hostname:port`, `pdo.sqlite:memory:`, and `apc`.
$type = strtolower(substr($location, 0, 3));
switch ($type)
{
case 'apc':
$this->cache_class = 'CacheAPC';
break;
case 'xca': // First three letters of `xcache`
$this->cache_class = 'CacheXCache';
break;
case 'pdo':
$this->cache_class = 'CachePDO';
$location = substr($location, 4);
break;
default:
$this->cache_class = 'CacheFile';
break;
}
}
// Set the remaining cache information.
$this->cache_location = $location;
$this->cache_compress = $gzip;
return $this;
}
/**
* Register a callback function to execute whenever a data stream is read from using
* .
*
* The user-defined callback function should accept three arguments:
*
*
* $curl_handle - resource - Required - The cURL handle resource that represents the in-progress transfer.
* $file_handle - resource - Required - The file handle resource that represents the file on the local file system.
* $length - integer - Required - The length in kilobytes of the data chunk that was transferred.
*
*
* @param string|array|function $callback (Required) The callback function is called by , so you can pass the following values:
* - The name of a global function to execute, passed as a string.
* - A method to execute, passed as
array('ClassName', 'MethodName').
* - An anonymous function (PHP 5.3+).
* @return $this A reference to the current instance.
*/
public function register_streaming_read_callback($callback)
{
$this->registered_streaming_read_callback = $callback;
return $this;
}
/**
* Register a callback function to execute whenever a data stream is written to using
* .
*
* The user-defined callback function should accept two arguments:
*
*
* $curl_handle - resource - Required - The cURL handle resource that represents the in-progress transfer.
* $length - integer - Required - The length in kilobytes of the data chunk that was transferred.
*
*
* @param string|array|function $callback (Required) The callback function is called by , so you can pass the following values:
* - The name of a global function to execute, passed as a string.
* - A method to execute, passed as
array('ClassName', 'MethodName').
* - An anonymous function (PHP 5.3+).
* @return $this A reference to the current instance.
*/
public function register_streaming_write_callback($callback)
{
$this->registered_streaming_write_callback = $callback;
return $this;
}
/**
* Fetches and caches STS credentials. This is meant to be used by the constructor, and is not to be
* manually invoked.
*
* @param CacheCore $cache (Required) The a reference to the cache object that is being used to handle the caching.
* @param array $options (Required) The options that were passed into the constructor.
* @return mixed The data to be cached, or NULL.
*/
public function cache_sts_credentials($cache, $options)
{
$token = new AmazonSTS($options);
$response = $token->get_session_token();
if ($response->isOK())
{
// Update the expiration
$expiration_time = strtotime((string) $response->body->GetSessionTokenResult->Credentials->Expiration);
$expiration_duration = round(($expiration_time - time()) * 0.85);
$cache->expire_in($expiration_duration);
// Return the important data
$credentials = $response->body->GetSessionTokenResult->Credentials;
return array(
'key' => (string) $credentials->AccessKeyId,
'secret' => (string) $credentials->SecretAccessKey,
'token' => (string) $credentials->SessionToken,
'expires' => (string) $credentials->Expiration,
);
}
// @codeCoverageIgnoreStart
throw new STS_Exception('Temporary credentials from the AWS Security '
. 'Token Service could not be retrieved using the provided long '
. 'term credentials. It\'s possible that the provided long term '
. 'credentials were invalid.');
// @codeCoverageIgnoreEnd
}
/**
* Fetches and caches EC2 instance profile credentials. This is meant to be used by the constructor, and is not to
* be manually invoked.
*
* @param CacheCore $cache (Required) The a reference to the cache object that is being used to handle the caching.
* @param array $options (Required) The options that were passed into the constructor.
* @return mixed The data to be cached, or NULL.
*/
public function cache_instance_profile_credentials($cache, $options)
{
$instance_profile_url = 'http://169.254.169.254/latest/meta-data/iam/security-credentials/';
$connect_timeout = isset($options['instance_profile_timeout']) ? $options['instance_profile_timeout'] : 2;
try
{
// Make a call to the EC2 Metadata Service to find the available instance profile
$request = new RequestCore($instance_profile_url);
$request->set_curlopts(array(CURLOPT_CONNECTTIMEOUT => $connect_timeout));
$response = $request->send_request(true);
if ($response->isOK())
{
// Get the instance profile name
$profile = (string) $response->body;
// Make a call to the EC2 Metadata Service to get the instance profile credentials
$request = new RequestCore($instance_profile_url . $profile);
$request->set_curlopts(array(CURLOPT_CONNECTTIMEOUT => $connect_timeout));
$response = $request->send_request(true);
if ($response->isOK())
{
// Get the credentials
$credentials = json_decode($response->body, true);
if ($credentials['Code'] === 'Success')
{
// Determine the expiration time
$expiration_time = strtotime((string) $credentials['Expiration']);
$expiration_duration = round(($expiration_time - time()) * 0.85);
$cache->expire_in($expiration_duration);
// Return the credential information
return array(
'key' => $credentials['AccessKeyId'],
'secret' => $credentials['SecretAccessKey'],
'token' => $credentials['Token'],
'expires' => $credentials['Expiration'],
);
}
}
}
}
catch (cURL_Exception $e)
{
// The EC2 Metadata Service does not exist or had timed out.
// An exception will be thrown on the next line.
}
// @codeCoverageIgnoreStart
throw new CFCredentials_Exception('No credentials were provided. The SDK attempted to retrieve Instance '
. 'Profile credentials from the EC2 Instance Metadata Service, but failed to do so. Instance profile '
. 'credentials are only accessible on EC2 instances configured with a specific IAM role.');
// @codeCoverageIgnoreEnd
}
/*%******************************************************************************************%*/
// SET CUSTOM CLASSES
/**
* Set a custom class for this functionality. Use this method when extending/overriding existing classes
* with new functionality.
*
* The replacement class must extend from .
*
* @param string $class (Optional) The name of the new class to use for this functionality.
* @return $this A reference to the current instance.
*/
public function set_utilities_class($class = 'CFUtilities')
{
$this->utilities_class = $class;
$this->util = new $this->utilities_class();
return $this;
}
/**
* Set a custom class for this functionality. Use this method when extending/overriding existing classes
* with new functionality.
*
* The replacement class must extend from .
*
* @param string $class (Optional) The name of the new class to use for this functionality.
* @param $this A reference to the current instance.
*/
public function set_request_class($class = 'CFRequest')
{
$this->request_class = $class;
return $this;
}
/**
* Set a custom class for this functionality. Use this method when extending/overriding existing classes
* with new functionality.
*
* The replacement class must extend from .
*
* @param string $class (Optional) The name of the new class to use for this functionality.
* @return $this A reference to the current instance.
*/
public function set_response_class($class = 'CFResponse')
{
$this->response_class = $class;
return $this;
}
/**
* Set a custom class for this functionality. Use this method when extending/overriding existing classes
* with new functionality.
*
* The replacement class must extend from .
*
* @param string $class (Optional) The name of the new class to use for this functionality.
* @return $this A reference to the current instance.
*/
public function set_parser_class($class = 'CFSimpleXML')
{
$this->parser_class = $class;
return $this;
}
/**
* Set a custom class for this functionality. Use this method when extending/overriding existing classes
* with new functionality.
*
* The replacement class must extend from .
*
* @param string $class (Optional) The name of the new class to use for this functionality.
* @return $this A reference to the current instance.
*/
public function set_batch_class($class = 'CFBatchRequest')
{
$this->batch_class = $class;
return $this;
}
/*%******************************************************************************************%*/
// AUTHENTICATION
/**
* Default, shared method for authenticating a connection to AWS.
*
* @param string $operation (Required) Indicates the operation to perform.
* @param array $payload (Required) An associative array of parameters for authenticating. See the individual methods for allowed keys.
* @return CFResponse Object containing a parsed HTTP response.
*/
public function authenticate($operation, $payload)
{
$original_payload = $payload;
$method_arguments = func_get_args();
$curlopts = array();
$return_curl_handle = false;
if (substr($operation, 0, strlen($this->operation_prefix)) !== $this->operation_prefix)
{
$operation = $this->operation_prefix . $operation;
}
// Extract the custom CURLOPT settings from the payload
if (is_array($payload) && isset($payload['curlopts']))
{
$curlopts = $payload['curlopts'];
unset($payload['curlopts']);
}
// Determine whether the response or curl handle should be returned
if (is_array($payload) && isset($payload['returnCurlHandle']))
{
$return_curl_handle = isset($payload['returnCurlHandle']) ? $payload['returnCurlHandle'] : false;
unset($payload['returnCurlHandle']);
}
// Use the caching flow to determine if we need to do a round-trip to the server.
if ($this->use_cache_flow)
{
// Generate an identifier specific to this particular set of arguments.
$cache_id = $this->key . '_' . get_class($this) . '_' . $operation . '_' . sha1(serialize($method_arguments));
// Instantiate the appropriate caching object.
$this->cache_object = new $this->cache_class($cache_id, $this->cache_location, $this->cache_expires, $this->cache_compress);
if ($this->delete_cache)
{
$this->use_cache_flow = false;
$this->delete_cache = false;
return $this->cache_object->delete();
}
// Invoke the cache callback function to determine whether to pull data from the cache or make a fresh request.
$data = $this->cache_object->response_manager(array($this, 'cache_callback'), $method_arguments);
// Parse the XML body
$data = $this->parse_callback($data);
// End!
return $data;
}
/*%******************************************************************************************%*/
// Signer
$signer = new $this->auth_class($this->hostname, $operation, $payload, $this->credentials);
$signer->key = $this->key;
$signer->secret_key = $this->secret_key;
$signer->auth_token = $this->auth_token;
$signer->api_version = $this->api_version;
$signer->utilities_class = $this->utilities_class;
$signer->request_class = $this->request_class;
$signer->response_class = $this->response_class;
$signer->use_ssl = $this->use_ssl;
$signer->proxy = $this->proxy;
$signer->util = $this->util;
$signer->registered_streaming_read_callback = $this->registered_streaming_read_callback;
$signer->registered_streaming_write_callback = $this->registered_streaming_write_callback;
$request = $signer->authenticate();
// Update RequestCore settings
$request->request_class = $this->request_class;
$request->response_class = $this->response_class;
$request->ssl_verification = $this->ssl_verification;
/*%******************************************************************************************%*/
// Debug mode
if ($this->debug_mode)
{
$request->debug_mode = $this->debug_mode;
}
// Set custom CURLOPT settings
if (count($curlopts))
{
$request->set_curlopts($curlopts);
}
// Manage the (newer) batch request API or the (older) returnCurlHandle setting.
if ($this->use_batch_flow)
{
$handle = $request->prep_request();
$this->batch_object->add($handle);
$this->use_batch_flow = false;
return $handle;
}
elseif ($return_curl_handle)
{
return $request->prep_request();
}
// Send!
$request->send_request();
// Prepare the response.
$headers = $request->get_response_header();
$headers['x-aws-stringtosign'] = $signer->string_to_sign;
if (isset($signer->canonical_request))
{
$headers['x-aws-canonicalrequest'] = $signer->canonical_request;
}
$headers['x-aws-request-headers'] = $request->request_headers;
$headers['x-aws-body'] = $signer->querystring;
$data = new $this->response_class($headers, ($this->parse_the_response === true) ? $this->parse_callback($request->get_response_body()) : $request->get_response_body(), $request->get_response_code());
$response_body = (string) $request->get_response_body();
// Was it Amazon's fault the request failed? Retry the request until we reach $max_retries.
if (
(integer) $request->get_response_code() === 500 || // Internal Error (presumably transient)
(integer) $request->get_response_code() === 503) // Service Unavailable (presumably transient)
{
if ($this->redirects <= $this->max_retries)
{
// Exponential backoff
$delay = (integer) (pow(4, $this->redirects) * 100000);
usleep($delay);
$this->redirects++;
$data = $this->authenticate($operation, $original_payload);
}
}
// DynamoDB has additional, custom logic for retrying requests
else
{
// If the request to DynamoDB was throttled, we need to retry
$need_to_retry_dynamodb_request = (
(integer) $request->get_response_code() === 400 &&
stripos($response_body, 'com.amazonaws.dynamodb.') !== false &&
stripos($response_body, 'ProvisionedThroughputExceededException') !== false
);
// If the CRC32 of the response does not match the expected value, we need to retry
$response_headers = $request->get_response_header();
if (!$need_to_retry_dynamodb_request && isset($response_headers['x-amz-crc32']))
{
$crc32_expected = $response_headers['x-amz-crc32'];
$crc32_actual = hexdec(hash('crc32b', $response_body));
$need_to_retry_dynamodb_request = ($crc32_expected != $crc32_actual);
}
// Perform retry if necessary using a more aggressive exponential backoff
if ($need_to_retry_dynamodb_request)
{
if ($this->redirects === 0)
{
$this->redirects++;
$data = $this->authenticate($operation, $original_payload);
}
elseif ($this->redirects <= max($this->max_retries, 10))
{
// Exponential backoff
$delay = (integer) (pow(2, ($this->redirects - 1)) * 50000);
usleep($delay);
$this->redirects++;
$data = $this->authenticate($operation, $original_payload);
}
}
}
$this->redirects = 0;
return $data;
}
/*%******************************************************************************************%*/
// BATCH REQUEST LAYER
/**
* Specifies that the intended request should be queued for a later batch request.
*
* @param CFBatchRequest $queue (Optional) The instance to use for managing batch requests. If not available, it generates a new instance of .
* @return $this A reference to the current instance.
*/
public function batch(CFBatchRequest &$queue = null)
{
if ($queue)
{
$this->batch_object = $queue;
}
elseif ($this->internal_batch_object)
{
$this->batch_object = &$this->internal_batch_object;
}
else
{
$this->internal_batch_object = new $this->batch_class();
$this->batch_object = &$this->internal_batch_object;
}
$this->use_batch_flow = true;
return $this;
}
/**
* Executes the batch request queue by sending all queued requests.
*
* @param boolean $clear_after_send (Optional) Whether or not to clear the batch queue after sending a request. Defaults to `true`. Set this to `false` if you are caching batch responses and want to retrieve results later.
* @return array An array of objects.
*/
public function send($clear_after_send = true)
{
if ($this->use_batch_flow)
{
// When we send the request, disable batch flow.
$this->use_batch_flow = false;
// If we're not caching, simply send the request.
if (!$this->use_cache_flow)
{
$response = $this->batch_object->send();
$parsed_data = array_map(array($this, 'parse_callback'), $response);
$parsed_data = new CFArray($parsed_data);
// Clear the queue
if ($clear_after_send)
{
$this->batch_object->queue = array();
}
return $parsed_data;
}
// Generate an identifier specific to this particular set of arguments.
$cache_id = $this->key . '_' . get_class($this) . '_' . sha1(serialize($this->batch_object));
// Instantiate the appropriate caching object.
$this->cache_object = new $this->cache_class($cache_id, $this->cache_location, $this->cache_expires, $this->cache_compress);
if ($this->delete_cache)
{
$this->use_cache_flow = false;
$this->delete_cache = false;
return $this->cache_object->delete();
}
// Invoke the cache callback function to determine whether to pull data from the cache or make a fresh request.
$data_set = $this->cache_object->response_manager(array($this, 'cache_callback_batch'), array($this->batch_object));
$parsed_data = array_map(array($this, 'parse_callback'), $data_set);
$parsed_data = new CFArray($parsed_data);
// Clear the queue
if ($clear_after_send)
{
$this->batch_object->queue = array();
}
// End!
return $parsed_data;
}
// Load the class
$null = new CFBatchRequest();
unset($null);
throw new CFBatchRequest_Exception('You must use $object->batch()->send()');
}
/**
* Parses a response body into a PHP object if appropriate.
*
* @param CFResponse|string $response (Required) The object to parse, or an XML string that would otherwise be a response body.
* @param string $content_type (Optional) The content-type to use when determining how to parse the content.
* @return CFResponse|string A parsed object, or parsed XML.
*/
public function parse_callback($response, $headers = null)
{
// Bail out
if (!$this->parse_the_response) return $response;
// Shorten this so we have a (mostly) single code path
if (isset($response->body))
{
if (is_string($response->body))
{
$body = $response->body;
}
else
{
return $response;
}
}
elseif (is_string($response))
{
$body = $response;
}
else
{
return $response;
}
// Decompress gzipped content
if (isset($headers['content-encoding']))
{
switch (strtolower(trim($headers['content-encoding'], "\x09\x0A\x0D\x20")))
{
case 'gzip':
case 'x-gzip':
$decoder = new CFGzipDecode($body);
if ($decoder->parse())
{
$body = $decoder->data;
}
break;
case 'deflate':
if (($uncompressed = gzuncompress($body)) !== false)
{
$body = $uncompressed;
}
elseif (($uncompressed = gzinflate($body)) !== false)
{
$body = $uncompressed;
}
break;
}
}
// Look for XML cues
if (
(isset($headers['content-type']) && ($headers['content-type'] === 'text/xml' || $headers['content-type'] === 'application/xml')) || // We know it's XML
(!isset($headers['content-type']) && (stripos($body, '') === 0) || preg_match('/^<(\w*) xmlns="http(s?):\/\/(\w*).amazon(aws)?.com/im', $body)) // Sniff for XML
)
{
// Strip the default XML namespace to simplify XPath expressions
$body = str_replace("xmlns=", "ns=", $body);
try {
// Parse the XML body
$body = new $this->parser_class($body);
}
catch (Exception $e)
{
throw new Parser_Exception($e->getMessage());
}
}
// Look for JSON cues
elseif (
(isset($headers['content-type']) && ($headers['content-type'] === 'application/json') || $headers['content-type'] === 'application/x-amz-json-1.0') || // We know it's JSON
(!isset($headers['content-type']) && $this->util->is_json($body)) // Sniff for JSON
)
{
// Normalize JSON to a CFSimpleXML object
$body = CFJSON::to_xml($body, $this->parser_class);
}
// Put the parsed data back where it goes
if (isset($response->body))
{
$response->body = $body;
}
else
{
$response = $body;
}
return $response;
}
/*%******************************************************************************************%*/
// CACHING LAYER
/**
* Specifies that the resulting object should be cached according to the settings from
* .
*
* @param string|integer $expires (Required) The time the cache is to expire. Accepts a number of seconds as an integer, or an amount of time, as a string, that is understood by (e.g. "1 hour").
* @return $this A reference to the current instance.
*/
public function cache($expires)
{
// Die if they haven't used set_cache_config().
if (!$this->cache_class)
{
throw new CFRuntime_Exception('Must call set_cache_config() before using cache()');
}
if (is_string($expires))
{
$expires = strtotime($expires);
$this->cache_expires = $expires - time();
}
elseif (is_int($expires))
{
$this->cache_expires = $expires;
}
$this->use_cache_flow = true;
return $this;
}
/**
* The callback function that is executed when the cache doesn't exist or has expired. The response of
* this method is cached. Accepts identical parameters as the method. Never call this
* method directly -- it is used internally by the caching system.
*
* @param string $operation (Required) Indicates the operation to perform.
* @param array $payload (Required) An associative array of parameters for authenticating. See the individual methods for allowed keys.
* @return CFResponse A parsed HTTP response.
*/
public function cache_callback($operation, $payload)
{
// Disable the cache flow since it's already been handled.
$this->use_cache_flow = false;
// Make the request
$response = $this->authenticate($operation, $payload);
// If this is an XML document, convert it back to a string.
if (isset($response->body) && ($response->body instanceof SimpleXMLElement))
{
$response->body = $response->body->asXML();
}
return $response;
}
/**
* Used for caching the results of a batch request. Never call this method directly; it is used
* internally by the caching system.
*
* @param CFBatchRequest $batch (Required) The batch request object to send.
* @return CFResponse A parsed HTTP response.
*/
public function cache_callback_batch(CFBatchRequest $batch)
{
return $batch->send();
}
/**
* Deletes a cached object using the specified cache storage type.
*
* @return boolean A value of `true` if cached object exists and is successfully deleted, otherwise `false`.
*/
public function delete_cache()
{
$this->use_cache_flow = true;
$this->delete_cache = true;
return $this;
}
}
/**
* Contains the functionality for auto-loading service classes.
*/
class CFLoader
{
/*%******************************************************************************************%*/
// AUTO-LOADER
/**
* Automatically load classes that aren't included.
*
* @param string $class (Required) The classname to load.
* @return boolean Whether or not the file was successfully loaded.
*/
public static function autoloader($class)
{
$path = dirname(__FILE__) . DIRECTORY_SEPARATOR;
// Amazon SDK classes
if (strstr($class, 'Amazon'))
{
if (file_exists($require_this = $path . 'services' . DIRECTORY_SEPARATOR . str_ireplace('Amazon', '', strtolower($class)) . '.class.php'))
{
require_once $require_this;
return true;
}
return false;
}
// Utility classes
elseif (strstr($class, 'CF'))
{
if (file_exists($require_this = $path . 'utilities' . DIRECTORY_SEPARATOR . str_ireplace('CF', '', strtolower($class)) . '.class.php'))
{
require_once $require_this;
return true;
}
return false;
}
// Load CacheCore
elseif (strstr($class, 'Cache'))
{
if (file_exists($require_this = $path . 'lib' . DIRECTORY_SEPARATOR . 'cachecore' . DIRECTORY_SEPARATOR . strtolower($class) . '.class.php'))
{
require_once $require_this;
return true;
}
return false;
}
// Load RequestCore
elseif (strstr($class, 'RequestCore') || strstr($class, 'ResponseCore'))
{
if (file_exists($require_this = $path . 'lib' . DIRECTORY_SEPARATOR . 'requestcore' . DIRECTORY_SEPARATOR . 'requestcore.class.php'))
{
require_once $require_this;
return true;
}
return false;
}
// Load Transmogrifier
elseif (strstr($class, 'Transmogrifier'))
{
if (file_exists($require_this = $path . 'lib' . DIRECTORY_SEPARATOR . 'dom' . DIRECTORY_SEPARATOR . 'Transmogrifier.php'))
{
require_once $require_this;
return true;
}
return false;
}
// Load Authentication Signers
elseif (strstr($class, 'Auth'))
{
if (file_exists($require_this = $path . 'authentication' . DIRECTORY_SEPARATOR . str_replace('auth', 'signature_', strtolower($class)) . '.class.php'))
{
require_once $require_this;
return true;
}
return false;
}
// Load Signer interface
elseif ($class === 'Signer')
{
if (!interface_exists('Signable', false) &&
file_exists($require_this = $path . 'authentication' . DIRECTORY_SEPARATOR . 'signable.interface.php'))
{
require_once $require_this;
}
if (file_exists($require_this = $path . 'authentication' . DIRECTORY_SEPARATOR . 'signer.abstract.php'))
{
require_once $require_this;
return true;
}
return false;
}
// Load Symfony YAML classes
elseif (strstr($class, 'sfYaml'))
{
if (file_exists($require_this = $path . 'lib' . DIRECTORY_SEPARATOR . 'yaml' . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'sfYaml.php'))
{
require_once $require_this;
return true;
}
return false;
}
return false;
}
}
// Register the autoloader.
spl_autoload_register(array('CFLoader', 'autoloader'));
/*%******************************************************************************************%*/
// CONFIGURATION
// If config auto-discovery is explicitly disabled, stop here
if (defined('AWS_DISABLE_CONFIG_AUTO_DISCOVERY')) return;
// Look for include file in the same directory (e.g. `./config.inc.php`).
if (file_exists(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'config.inc.php'))
{
include_once dirname(__FILE__) . DIRECTORY_SEPARATOR . 'config.inc.php';
}
// Fallback to `~/.aws/sdk/config.inc.php`
else
{
if (!isset($_ENV['HOME']) && isset($_SERVER['HOME']))
{
$_ENV['HOME'] = $_SERVER['HOME'];
}
elseif (!isset($_ENV['HOME']) && !isset($_SERVER['HOME']))
{
$os = strtolower(PHP_OS);
if (in_array($os, array('windows', 'winnt', 'win32')))
{
$_ENV['HOME'] = false;
}
else
{
$dir = exec('(cd ~ && pwd) 2>&1', $out, $exit);
if ($exit === 0)
{
$_ENV['HOME'] = trim($dir);
}
else
{
error_log('Failed to determine HOME directory after trying "' . $dir . '" (exit code ' . $exit . ')');
$_ENV['HOME'] = false;
}
}
if (!$_ENV['HOME'])
{
switch ($os)
{
case 'darwin':
$_ENV['HOME'] = '/Users/' . get_current_user();
break;
case 'windows':
case 'winnt':
case 'win32':
$_ENV['HOME'] = 'c:' . DIRECTORY_SEPARATOR . 'Documents and Settings' . DIRECTORY_SEPARATOR . get_current_user();
break;
default:
$_ENV['HOME'] = '/home/' . get_current_user();
break;
}
}
}
$path = DIRECTORY_SEPARATOR . '.aws' . DIRECTORY_SEPARATOR . 'sdk' . DIRECTORY_SEPARATOR . 'config.inc.php';
if (isset($_ENV['HOME']) && file_exists($_ENV['HOME'] . $path))
{
include_once $_ENV['HOME'] . $path;
}
unset($os, $dir, $out, $exit, $path);
}
================================================
FILE: services/s3.class.php
================================================
for more information.
*
* @version 2012.10.02
* @license See the included NOTICE.md file for more information.
* @copyright See the included NOTICE.md file for more information.
* @link http://aws.amazon.com/s3/ Amazon Simple Storage Service
* @link http://aws.amazon.com/documentation/s3/ Amazon Simple Storage Service documentation
*/
class AmazonS3 extends CFRuntime
{
/*%******************************************************************************************%*/
// REGIONAL ENDPOINTS
/**
* Specify the queue URL for the US-Standard (Northern Virginia & Washington State) Region.
*/
const REGION_US_E1 = 's3.amazonaws.com';
/**
* Specify the queue URL for the US-Standard (Northern Virginia & Washington State) Region.
*/
const REGION_VIRGINIA = self::REGION_US_E1;
/**
* Specify the queue URL for the US-Standard (Northern Virginia & Washington State) Region.
*/
const REGION_US_STANDARD = self::REGION_US_E1;
/**
* Specify the queue URL for the US-West 1 (Northern California) Region.
*/
const REGION_US_W1 = 's3-us-west-1.amazonaws.com';
/**
* Specify the queue URL for the US-West 1 (Northern California) Region.
*/
const REGION_CALIFORNIA = self::REGION_US_W1;
/**
* Specify the queue URL for the US-West 2 (Oregon) Region.
*/
const REGION_US_W2 = 's3-us-west-2.amazonaws.com';
/**
* Specify the queue URL for the US-West 2 (Oregon) Region.
*/
const REGION_OREGON = self::REGION_US_W2;
/**
* Specify the queue URL for the EU (Ireland) Region.
*/
const REGION_EU_W1 = 's3-eu-west-1.amazonaws.com';
/**
* Specify the queue URL for the EU (Ireland) Region.
*/
const REGION_IRELAND = self::REGION_EU_W1;
/**
* Specify the queue URL for the Asia Pacific (Singapore) Region.
*/
const REGION_APAC_SE1 = 's3-ap-southeast-1.amazonaws.com';
/**
* Specify the queue URL for the Asia Pacific (Singapore) Region.
*/
const REGION_SINGAPORE = self::REGION_APAC_SE1;
/**
* Specify the queue URL for the Asia Pacific (Sydney) Region.
*/
const REGION_APAC_SE2 = 's3-ap-southeast-2.amazonaws.com';
/**
* Specify the queue URL for the Asia Pacific (Sydney) Region.
*/
const REGION_SYDNEY = self::REGION_APAC_SE2;
/**
* Specify the queue URL for the Asia Pacific (Japan) Region.
*/
const REGION_APAC_NE1 = 's3-ap-northeast-1.amazonaws.com';
/**
* Specify the queue URL for the Asia Pacific (Japan) Region.
*/
const REGION_TOKYO = self::REGION_APAC_NE1;
/**
* Specify the queue URL for the South America (Sao Paulo) Region.
*/
const REGION_SA_E1 = 's3-sa-east-1.amazonaws.com';
/**
* Specify the queue URL for the South America (Sao Paulo) Region.
*/
const REGION_SAO_PAULO = self::REGION_SA_E1;
/**
* Specify the queue URL for the United States GovCloud Region.
*/
const REGION_US_GOV1 = 's3-us-gov-west-1.amazonaws.com';
/**
* Specify the queue URL for the United States GovCloud FIPS 140-2 Region.
*/
const REGION_US_GOV1_FIPS = 's3-fips-us-gov-west-1.amazonaws.com';
/**
* The default endpoint.
*/
const DEFAULT_URL = self::REGION_US_E1;
/*%******************************************************************************************%*/
// REGIONAL WEBSITE ENDPOINTS
/**
* Specify the queue URL for the US-Standard (Northern Virginia & Washington State) Website Region.
*/
const REGION_US_E1_WEBSITE = 's3-website-us-east-1.amazonaws.com';
/**
* Specify the queue URL for the US-Standard (Northern Virginia & Washington State) Website Region.
*/
const REGION_VIRGINIA_WEBSITE = self::REGION_US_E1_WEBSITE;
/**
* Specify the queue URL for the US-Standard (Northern Virginia & Washington State) Website Region.
*/
const REGION_US_STANDARD_WEBSITE = self::REGION_US_E1_WEBSITE;
/**
* Specify the queue URL for the US-West 1 (Northern California) Website Region.
*/
const REGION_US_W1_WEBSITE = 's3-website-us-west-1.amazonaws.com';
/**
* Specify the queue URL for the US-West 1 (Northern California) Website Region.
*/
const REGION_CALIFORNIA_WEBSITE = self::REGION_US_W1_WEBSITE;
/**
* Specify the queue URL for the US-West 2 (Oregon) Website Region.
*/
const REGION_US_W2_WEBSITE = 's3-website-us-west-2.amazonaws.com';
/**
* Specify the queue URL for the US-West 2 (Oregon) Website Region.
*/
const REGION_OREGON_WEBSITE = self::REGION_US_W2_WEBSITE;
/**
* Specify the queue URL for the EU (Ireland) Website Region.
*/
const REGION_EU_W1_WEBSITE = 's3-website-eu-west-1.amazonaws.com';
/**
* Specify the queue URL for the EU (Ireland) Website Region.
*/
const REGION_IRELAND_WEBSITE = self::REGION_EU_W1_WEBSITE;
/**
* Specify the queue URL for the Asia Pacific (Singapore) Website Region.
*/
const REGION_APAC_SE1_WEBSITE = 's3-website-ap-southeast-1.amazonaws.com';
/**
* Specify the queue URL for the Asia Pacific (Singapore) Website Region.
*/
const REGION_SINGAPORE_WEBSITE = self::REGION_APAC_SE1_WEBSITE;
/**
* Specify the queue URL for the Asia Pacific (Sydney) Website Region.
*/
const REGION_APAC_SE2_WEBSITE = 's3-website-ap-southeast-2.amazonaws.com';
/**
* Specify the queue URL for the Asia Pacific (Sydney) Website Region.
*/
const REGION_SYDNEY_WEBSITE = self::REGION_APAC_SE2_WEBSITE;
/**
* Specify the queue URL for the Asia Pacific (Japan) Website Region.
*/
const REGION_APAC_NE1_WEBSITE = 's3-website-ap-northeast-1.amazonaws.com';
/**
* Specify the queue URL for the Asia Pacific (Japan) Website Region.
*/
const REGION_TOKYO_WEBSITE = self::REGION_APAC_NE1_WEBSITE;
/**
* Specify the queue URL for the South America (Sao Paulo) Website Region.
*/
const REGION_SA_E1_WEBSITE = 's3-website-sa-east-1.amazonaws.com';
/**
* Specify the queue URL for the South America (Sao Paulo) Website Region.
*/
const REGION_SAO_PAULO_WEBSITE = self::REGION_SA_E1_WEBSITE;
/**
* Specify the queue URL for the United States GovCloud Website Region.
*/
const REGION_US_GOV1_WEBSITE = 's3-website-us-gov-west-1.amazonaws.com';
/*%******************************************************************************************%*/
// ACL
/**
* ACL: Owner-only read/write.
*/
const ACL_PRIVATE = 'private';
/**
* ACL: Owner read/write, public read.
*/
const ACL_PUBLIC = 'public-read';
/**
* ACL: Public read/write.
*/
const ACL_OPEN = 'public-read-write';
/**
* ACL: Owner read/write, authenticated read.
*/
const ACL_AUTH_READ = 'authenticated-read';
/**
* ACL: Bucket owner read.
*/
const ACL_OWNER_READ = 'bucket-owner-read';
/**
* ACL: Bucket owner full control.
*/
const ACL_OWNER_FULL_CONTROL = 'bucket-owner-full-control';
/*%******************************************************************************************%*/
// GRANTS
/**
* When applied to a bucket, grants permission to list the bucket. When applied to an object, this
* grants permission to read the object data and/or metadata.
*/
const GRANT_READ = 'READ';
/**
* When applied to a bucket, grants permission to create, overwrite, and delete any object in the
* bucket. This permission is not supported for objects.
*/
const GRANT_WRITE = 'WRITE';
/**
* Grants permission to read the ACL for the applicable bucket or object. The owner of a bucket or
* object always has this permission implicitly.
*/
const GRANT_READ_ACP = 'READ_ACP';
/**
* Gives permission to overwrite the ACP for the applicable bucket or object. The owner of a bucket
* or object always has this permission implicitly. Granting this permission is equivalent to granting
* FULL_CONTROL because the grant recipient can make any changes to the ACP.
*/
const GRANT_WRITE_ACP = 'WRITE_ACP';
/**
* Provides READ, WRITE, READ_ACP, and WRITE_ACP permissions. It does not convey additional rights and
* is provided only for convenience.
*/
const GRANT_FULL_CONTROL = 'FULL_CONTROL';
/*%******************************************************************************************%*/
// USERS
/**
* The "AuthenticatedUsers" group for access control policies.
*/
const USERS_AUTH = 'http://acs.amazonaws.com/groups/global/AuthenticatedUsers';
/**
* The "AllUsers" group for access control policies.
*/
const USERS_ALL = 'http://acs.amazonaws.com/groups/global/AllUsers';
/**
* The "LogDelivery" group for access control policies.
*/
const USERS_LOGGING = 'http://acs.amazonaws.com/groups/s3/LogDelivery';
/*%******************************************************************************************%*/
// PATTERNS
/**
* PCRE: Match all items
*/
const PCRE_ALL = '/.*/i';
/*%******************************************************************************************%*/
// STORAGE
/**
* Standard storage redundancy.
*/
const STORAGE_STANDARD = 'STANDARD';
/**
* Reduced storage redundancy.
*/
const STORAGE_REDUCED = 'REDUCED_REDUNDANCY';
/**
* Storage in Glacier.
*/
const STORAGE_GLACIER = 'GLACIER';
/*%******************************************************************************************%*/
// PROPERTIES
/**
* The request URL.
*/
public $request_url;
/**
* The virtual host setting.
*/
public $vhost;
/**
* The base XML elements to use for access control policy methods.
*/
public $base_acp_xml;
/**
* The base XML elements to use for creating buckets in regions.
*/
public $base_location_constraint;
/**
* The base XML elements to use for logging methods.
*/
public $base_logging_xml;
/**
* The base XML elements to use for notifications.
*/
public $base_notification_xml;
/**
* The base XML elements to use for versioning.
*/
public $base_versioning_xml;
/**
* The base XML elements to use for completing a multipart upload.
*/
public $complete_mpu_xml;
/**
* The base XML elements to use for website support.
*/
public $website_config_xml;
/**
* The base XML elements to use for multi-object delete support.
*/
public $multi_object_delete_xml;
/**
* The base XML elements to use for object expiration support.
*/
public $object_expiration_xml;
/**
* The base XML elements to use for bucket tagging.
*/
public $bucket_tagging_xml;
/**
* The base XML elements to use for CORS support.
*/
public $cors_config_xml;
/**
* The base XML elements to use for restoration requests.
*/
public $restore_request_xml;
/**
* The DNS vs. Path-style setting.
*/
public $path_style = false;
/**
* The state of whether the prefix change is temporary or permanent.
*/
public $temporary_prefix = false;
/*%******************************************************************************************%*/
// CONSTRUCTOR
/**
* Constructs a new instance of .
*
* @param array $options (Optional) An associative array of parameters that can have the following keys:
* certificate_authority - boolean - Optional - Determines which Cerificate Authority file to use. A value of boolean false will use the Certificate Authority file available on the system. A value of boolean true will use the Certificate Authority provided by the SDK. Passing a file system path to a Certificate Authority file (chmodded to 0755) will use that. Leave this set to false if you're not sure.
* credentials - string - Optional - The name of the credential set to use for authentication.
* default_cache_config - string - Optional - This option allows a preferred storage type to be configured for long-term caching. This can be changed later using the method. Valid values are: apc, xcache, or a file system path such as ./cache or /tmp/cache/.
* key - string - Optional - Your AWS key, or a session key. If blank, the default credential set will be used.
* secret - string - Optional - Your AWS secret key, or a session secret key. If blank, the default credential set will be used.
* token - string - Optional - An AWS session token.
* @return void
*/
public function __construct(array $options = array())
{
$this->vhost = null;
$this->api_version = '2006-03-01';
$this->hostname = self::DEFAULT_URL;
$this->base_acp_xml = '';
$this->base_location_constraint = '';
$this->base_logging_xml = '';
$this->base_notification_xml = '';
$this->base_versioning_xml = '';
$this->complete_mpu_xml = '';
$this->website_config_xml = 'index.htmlerror.html';
$this->multi_object_delete_xml = '';
$this->object_expiration_xml = '';
$this->bucket_tagging_xml = '';
$this->cors_config_xml = '';
$this->restore_request_xml = '';
parent::__construct($options);
}
/*%******************************************************************************************%*/
// AUTHENTICATION
/**
* Authenticates a connection to Amazon S3. Do not use directly unless implementing custom methods for
* this class.
*
* @param string $operation (Required) The name of the bucket to operate on (S3 Only).
* @param array $payload (Required) An associative array of parameters for authenticating. See inline comments for allowed keys.
* @return CFResponse A object containing a parsed HTTP response.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/S3_Authentication.html REST authentication
*/
public function authenticate($operation, $payload)
{
/*
* Overriding or extending this class? You can pass the following "magic" keys into $opt.
*
* ## verb, resource, sub_resource and query_string ##
* /?&
* GET /filename.txt?versions&prefix=abc&max-items=1
*
* ## versionId, uploadId, partNumber, response-* ##
* These don't follow the same rules as above, in that the they needs to be signed, while
* other query_string values do not.
*
* ## curlopts ##
* These values get passed directly to the cURL methods in RequestCore.
*
* ## fileUpload, fileDownload, seekTo ##
* These are slightly modified and then passed to the cURL methods in RequestCore.
*
* ## headers ##
* $opt['headers'] is an array, whose keys are HTTP headers to be sent.
*
* ## body ##
* This is the request body that is sent to the server via PUT/POST.
*
* ## preauth ##
* This is a hook that tells authenticate() to generate a pre-authenticated URL.
*
* ## returnCurlHandle ##
* Tells authenticate() to return the cURL handle for the request instead of executing it.
*/
// Rename variables (to overcome inheritence issues)
$bucket = $operation;
$opt = $payload;
// Validate the S3 bucket name
if (!$this->validate_bucketname_support($bucket))
{
// @codeCoverageIgnoreStart
throw new S3_Exception('S3 does not support "' . $bucket . '" as a valid bucket name. Review "Bucket Restrictions and Limitations" in the S3 Developer Guide for more information.');
// @codeCoverageIgnoreEnd
}
// Die if $opt isn't set.
if (!$opt) return false;
$method_arguments = func_get_args();
// Use the caching flow to determine if we need to do a round-trip to the server.
if ($this->use_cache_flow)
{
// Generate an identifier specific to this particular set of arguments.
$cache_id = $this->key . '_' . get_class($this) . '_' . $bucket . '_' . sha1(serialize($method_arguments));
// Instantiate the appropriate caching object.
$this->cache_object = new $this->cache_class($cache_id, $this->cache_location, $this->cache_expires, $this->cache_compress);
if ($this->delete_cache)
{
$this->use_cache_flow = false;
$this->delete_cache = false;
return $this->cache_object->delete();
}
// Invoke the cache callback function to determine whether to pull data from the cache or make a fresh request.
$data = $this->cache_object->response_manager(array($this, 'cache_callback'), $method_arguments);
if ($this->parse_the_response)
{
// Parse the XML body
$data = $this->parse_callback($data);
}
// End!
return $data;
}
// If we haven't already set a resource prefix and the bucket name isn't DNS-valid...
if ((!$this->resource_prefix && !$this->validate_bucketname_create($bucket)) || $this->path_style)
{
// Fall back to the older path-style URI
$this->set_resource_prefix('/' . $bucket);
$this->temporary_prefix = true;
}
// If the bucket name has periods and we are using SSL, we need to switch to path style URLs
$bucket_name_may_cause_ssl_wildcard_failures = false;
if ($this->use_ssl && strpos($bucket, '.') !== false)
{
$bucket_name_may_cause_ssl_wildcard_failures = true;
}
// Determine hostname
$scheme = $this->use_ssl ? 'https://' : 'http://';
if ($bucket_name_may_cause_ssl_wildcard_failures || $this->resource_prefix || $this->path_style)
{
// Use bucket-in-path method
$hostname = $this->hostname . $this->resource_prefix . (($bucket === '' || $this->resource_prefix === '/' . $bucket) ? '' : ('/' . $bucket));
}
else
{
$hostname = $this->vhost ? $this->vhost : (($bucket === '') ? $this->hostname : ($bucket . '.') . $this->hostname);
}
// Get the UTC timestamp in RFC 2616 format
$date = gmdate(CFUtilities::DATE_FORMAT_RFC2616, time());
// Storage for request parameters.
$resource = '';
$sub_resource = '';
$querystringparams = array();
$signable_querystringparams = array();
$string_to_sign = '';
$headers = array(
'Content-MD5' => '',
'Content-Type' => 'application/x-www-form-urlencoded',
'Date' => $date
);
/*%******************************************************************************************%*/
// Do we have an authentication token?
if ($this->auth_token)
{
$headers['X-Amz-Security-Token'] = $this->auth_token;
}
// Handle specific resources
if (isset($opt['resource']))
{
$resource .= $opt['resource'];
}
// Merge query string values
if (isset($opt['query_string']))
{
$querystringparams = array_merge($querystringparams, $opt['query_string']);
}
$query_string = $this->util->to_query_string($querystringparams);
// Merge the signable query string values. Must be alphabetical.
$signable_list = array(
'partNumber',
'response-cache-control',
'response-content-disposition',
'response-content-encoding',
'response-content-language',
'response-content-type',
'response-expires',
'uploadId',
'versionId'
);
foreach ($signable_list as $item)
{
if (isset($opt[$item]))
{
$signable_querystringparams[$item] = $opt[$item];
}
}
$signable_query_string = $this->util->to_query_string($signable_querystringparams);
// Merge the HTTP headers
if (isset($opt['headers']))
{
$headers = array_merge($headers, $opt['headers']);
}
// Compile the URI to request
$conjunction = '?';
$signable_resource = '/' . str_replace('%2F', '/', rawurlencode($resource));
$non_signable_resource = '';
if (isset($opt['sub_resource']))
{
$signable_resource .= $conjunction . rawurlencode($opt['sub_resource']);
$conjunction = '&';
}
if ($signable_query_string !== '')
{
$signable_query_string = $conjunction . $signable_query_string;
$conjunction = '&';
}
if ($query_string !== '')
{
$non_signable_resource .= $conjunction . $query_string;
$conjunction = '&';
}
if (substr($hostname, -1) === substr($signable_resource, 0, 1))
{
$signable_resource = ltrim($signable_resource, '/');
}
$this->request_url = $scheme . $hostname . $signable_resource . $signable_query_string . $non_signable_resource;
if (isset($opt['location']))
{
$this->request_url = $opt['location'];
}
// Gather information to pass along to other classes.
$helpers = array(
'utilities' => $this->utilities_class,
'request' => $this->request_class,
'response' => $this->response_class,
);
// Instantiate the request class
$request = new $this->request_class($this->request_url, $this->proxy, $helpers, $this->credentials);
// Update RequestCore settings
$request->request_class = $this->request_class;
$request->response_class = $this->response_class;
$request->ssl_verification = $this->ssl_verification;
// Pass along registered stream callbacks
if ($this->registered_streaming_read_callback)
{
$request->register_streaming_read_callback($this->registered_streaming_read_callback);
}
if ($this->registered_streaming_write_callback)
{
$request->register_streaming_write_callback($this->registered_streaming_write_callback);
}
// Streaming uploads
if (isset($opt['fileUpload']))
{
if (is_resource($opt['fileUpload']))
{
// Determine the length to read from the stream
$length = null; // From current position until EOF by default, size determined by set_read_stream()
if (isset($headers['Content-Length']))
{
$length = $headers['Content-Length'];
}
elseif (isset($opt['seekTo']))
{
// Read from seekTo until EOF by default
$stats = fstat($opt['fileUpload']);
if ($stats && $stats['size'] >= 0)
{
$length = $stats['size'] - (integer) $opt['seekTo'];
}
}
$request->set_read_stream($opt['fileUpload'], $length);
if ($headers['Content-Type'] === 'application/x-www-form-urlencoded')
{
$headers['Content-Type'] = 'application/octet-stream';
}
}
else
{
$request->set_read_file($opt['fileUpload']);
// Determine the length to read from the file
$length = $request->read_stream_size; // The file size by default
if (isset($headers['Content-Length']))
{
$length = $headers['Content-Length'];
}
elseif (isset($opt['seekTo']) && isset($length))
{
// Read from seekTo until EOF by default
$length -= (integer) $opt['seekTo'];
}
$request->set_read_stream_size($length);
// Attempt to guess the correct mime-type
if ($headers['Content-Type'] === 'application/x-www-form-urlencoded')
{
$extension = explode('.', $opt['fileUpload']);
$extension = array_pop($extension);
$mime_type = CFMimeTypes::get_mimetype($extension);
$headers['Content-Type'] = $mime_type;
}
}
$headers['Content-Length'] = $request->read_stream_size;
$headers['Content-MD5'] = '';
}
// Handle streaming file offsets
if (isset($opt['seekTo']))
{
// Pass the seek position to RequestCore
$request->set_seek_position((integer) $opt['seekTo']);
}
// Streaming downloads
if (isset($opt['fileDownload']))
{
if (is_resource($opt['fileDownload']))
{
$request->set_write_stream($opt['fileDownload']);
}
else
{
$request->set_write_file($opt['fileDownload']);
}
}
$curlopts = array();
// Set custom CURLOPT settings
if (isset($opt['curlopts']))
{
$curlopts = $opt['curlopts'];
}
// Debug mode
if ($this->debug_mode)
{
$curlopts[CURLOPT_VERBOSE] = true;
}
// Set the curl options.
if (count($curlopts))
{
$request->set_curlopts($curlopts);
}
// Do we have a verb?
if (isset($opt['verb']))
{
$request->set_method($opt['verb']);
$string_to_sign .= $opt['verb'] . "\n";
}
// Add headers and content when we have a body
if (isset($opt['body']))
{
$request->set_body($opt['body']);
$headers['Content-Length'] = strlen($opt['body']);
if ($headers['Content-Type'] === 'application/x-www-form-urlencoded')
{
$headers['Content-Type'] = 'application/octet-stream';
}
if (!isset($opt['NoContentMD5']) || $opt['NoContentMD5'] !== true)
{
$headers['Content-MD5'] = $this->util->hex_to_base64(md5($opt['body']));
}
}
// Handle query-string authentication
if (isset($opt['preauth']) && (integer) $opt['preauth'] > 0)
{
unset($headers['Date']);
$headers['Content-Type'] = '';
$headers['Expires'] = is_int($opt['preauth']) ? $opt['preauth'] : strtotime($opt['preauth']);
}
// Sort headers
uksort($headers, 'strnatcasecmp');
// Add headers to request and compute the string to sign
foreach ($headers as $header_key => $header_value)
{
// Strip linebreaks from header values as they're illegal and can allow for security issues
$header_value = str_replace(array("\r", "\n"), '', $header_value);
// Add the header if it has a value
if ($header_value !== '')
{
$request->add_header($header_key, $header_value);
}
// Generate the string to sign
if (
strtolower($header_key) === 'content-md5' ||
strtolower($header_key) === 'content-type' ||
strtolower($header_key) === 'date' ||
(strtolower($header_key) === 'expires' && isset($opt['preauth']) && (integer) $opt['preauth'] > 0)
)
{
$string_to_sign .= $header_value . "\n";
}
elseif (substr(strtolower($header_key), 0, 6) === 'x-amz-')
{
$string_to_sign .= strtolower($header_key) . ':' . $header_value . "\n";
}
}
// Add the signable resource location
$string_to_sign .= ($this->resource_prefix ? $this->resource_prefix : '');
$string_to_sign .= (($bucket === '' || $this->resource_prefix === '/' . $bucket) ? '' : ('/' . $bucket)) . $signable_resource . urldecode($signable_query_string);
// Hash the AWS secret key and generate a signature for the request.
$signature = base64_encode(hash_hmac('sha1', $string_to_sign, $this->secret_key, true));
$request->add_header('Authorization', 'AWS ' . $this->key . ':' . $signature);
// If we're generating a URL, return the URL to the calling method.
if (isset($opt['preauth']) && (integer) $opt['preauth'] > 0)
{
$query_params = array(
'AWSAccessKeyId' => $this->key,
'Expires' => $headers['Expires'],
'Signature' => $signature,
);
// If using short-term credentials, add the token to the query string
if ($this->auth_token)
{
$query_params['x-amz-security-token'] = $this->auth_token;
}
return $this->request_url . $conjunction . http_build_query($query_params, '', '&');
}
elseif (isset($opt['preauth']))
{
return $this->request_url;
}
/*%******************************************************************************************%*/
// If our changes were temporary, reset them.
if ($this->temporary_prefix)
{
$this->temporary_prefix = false;
$this->resource_prefix = null;
}
// Manage the (newer) batch request API or the (older) returnCurlHandle setting.
if ($this->use_batch_flow)
{
$handle = $request->prep_request();
$this->batch_object->add($handle);
$this->use_batch_flow = false;
return $handle;
}
elseif (isset($opt['returnCurlHandle']) && $opt['returnCurlHandle'] === true)
{
return $request->prep_request();
}
// Send!
$request->send_request();
// Prepare the response
$headers = $request->get_response_header();
$headers['x-aws-request-url'] = $this->request_url;
$headers['x-aws-redirects'] = $this->redirects;
$headers['x-aws-stringtosign'] = $string_to_sign;
$headers['x-aws-requestheaders'] = $request->request_headers;
// Did we have a request body?
if (isset($opt['body']))
{
$headers['x-aws-requestbody'] = $opt['body'];
}
$data = new $this->response_class($headers, $this->parse_callback($request->get_response_body()), $request->get_response_code());
// Did Amazon tell us to redirect? Typically happens for multiple rapid requests EU datacenters.
// @see: http://docs.amazonwebservices.com/AmazonS3/latest/dev/Redirects.html
// @codeCoverageIgnoreStart
if ((integer) $request->get_response_code() === 307) // Temporary redirect to new endpoint.
{
$this->redirects++;
$opt['location'] = $headers['location'];
$data = $this->authenticate($bucket, $opt);
}
// Was it Amazon's fault the request failed? Retry the request until we reach $max_retries.
elseif ((integer) $request->get_response_code() === 500 || (integer) $request->get_response_code() === 503)
{
if ($this->redirects <= $this->max_retries)
{
// Exponential backoff
$delay = (integer) (pow(4, $this->redirects) * 100000);
usleep($delay);
$this->redirects++;
$data = $this->authenticate($bucket, $opt);
}
}
// @codeCoverageIgnoreEnd
// Return!
$this->redirects = 0;
return $data;
}
/**
* Validates whether or not the specified Amazon S3 bucket name is valid for DNS-style access. This
* method is leveraged by any method that creates buckets.
*
* @param string $bucket (Required) The name of the bucket to validate.
* @return boolean Whether or not the specified Amazon S3 bucket name is valid for DNS-style access. A value of true means that the bucket name is valid. A value of false means that the bucket name is invalid.
*/
public function validate_bucketname_create($bucket)
{
// list_buckets() uses this. Let it pass.
if ($bucket === '') return true;
if (
($bucket === null || $bucket === false) || // Must not be null or false
preg_match('/[^(a-z0-9\-\.)]/', $bucket) || // Must be in the lowercase Roman alphabet, period or hyphen
!preg_match('/^([a-z]|\d)/', $bucket) || // Must start with a number or letter
!(strlen($bucket) >= 3 && strlen($bucket) <= 63) || // Must be between 3 and 63 characters long
(strpos($bucket, '..') !== false) || // Bucket names cannot contain two, adjacent periods
(strpos($bucket, '-.') !== false) || // Bucket names cannot contain dashes next to periods
(strpos($bucket, '.-') !== false) || // Bucket names cannot contain dashes next to periods
preg_match('/(-|\.)$/', $bucket) || // Bucket names should not end with a dash or period
preg_match('/^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/', $bucket) // Must not be formatted as an IP address
) return false;
return true;
}
/**
* Validates whether or not the specified Amazon S3 bucket name is valid for path-style access. This
* method is leveraged by any method that reads from buckets.
*
* @param string $bucket (Required) The name of the bucket to validate.
* @return boolean Whether or not the bucket name is valid. A value of true means that the bucket name is valid. A value of false means that the bucket name is invalid.
*/
public function validate_bucketname_support($bucket)
{
// list_buckets() uses this. Let it pass.
if ($bucket === '') return true;
// Validate
if (
($bucket === null || $bucket === false) || // Must not be null or false
preg_match('/[^(a-z0-9_\-\.)]/i', $bucket) || // Must be in the Roman alphabet, period, hyphen or underscore
!preg_match('/^([a-z]|\d)/i', $bucket) || // Must start with a number or letter
!(strlen($bucket) >= 3 && strlen($bucket) <= 255) || // Must be between 3 and 255 characters long
preg_match('/^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/', $bucket) // Must not be formatted as an IP address
) return false;
return true;
}
/*%******************************************************************************************%*/
// SETTERS
/**
* Sets the region to use for subsequent Amazon S3 operations. This will also reset any prior use of
* .
*
* @param string $region (Required) The region to use for subsequent Amazon S3 operations. For a complete list of REGION constants, see the AmazonS3 Constants page in the API reference.
* @return $this A reference to the current instance.
*/
public function set_region($region)
{
// @codeCoverageIgnoreStart
$this->set_hostname($region);
switch ($region)
{
case self::REGION_US_E1: // Northern Virginia
$this->enable_path_style(false);
break;
case self::REGION_EU_W1: // Ireland
$this->enable_path_style(); // Always use path-style access for EU endpoint.
break;
default:
$this->enable_path_style(false);
break;
}
// @codeCoverageIgnoreEnd
return $this;
}
/**
* Sets the virtual host to use in place of the default `bucket.s3.amazonaws.com` domain.
*
* @param string $vhost (Required) The virtual host to use in place of the default `bucket.s3.amazonaws.com` domain.
* @return $this A reference to the current instance.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/VirtualHosting.html Virtual Hosting of Buckets
*/
public function set_vhost($vhost)
{
$this->vhost = $vhost;
return $this;
}
/**
* Enables the use of the older path-style URI access for all requests.
*
* @param string $style (Optional) Whether or not to enable path-style URI access for all requests. The default value is true.
* @return $this A reference to the current instance.
*/
public function enable_path_style($style = true)
{
$this->path_style = $style;
return $this;
}
/*%******************************************************************************************%*/
// BUCKET METHODS
/**
* Creates an Amazon S3 bucket.
*
* Every object stored in Amazon S3 is contained in a bucket. Buckets partition the namespace of
* objects stored in Amazon S3 at the top level. in a bucket, any name can be used for objects.
* However, bucket names must be unique across all of Amazon S3.
*
* @param string $bucket (Required) The name of the bucket to create.
* @param string $region (Required) The preferred geographical location for the bucket. [Allowed values: `AmazonS3::REGION_US_E1 `, `AmazonS3::REGION_US_W1`, `AmazonS3::REGION_EU_W1`, `AmazonS3::REGION_APAC_SE1`, `AmazonS3::REGION_APAC_NE1`]
* @param string $acl (Optional) The ACL settings for the specified object. Accepts any of the following constants: [Allowed values: AmazonS3::ACL_PRIVATE, AmazonS3::ACL_PUBLIC, AmazonS3::ACL_OPEN, AmazonS3::ACL_AUTH_READ, AmazonS3::ACL_OWNER_READ, AmazonS3::ACL_OWNER_FULL_CONTROL]. Alternatively, an array of associative arrays. Each associative array contains an id and a permission key. The default value is ACL_PRIVATE.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request.
* @return CFResponse A object containing a parsed HTTP response.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/UsingBucket.html Working with Amazon S3 Buckets
*/
public function create_bucket($bucket, $region, $acl = self::ACL_PRIVATE, $opt = null)
{
// If the bucket contains uppercase letters...
if (preg_match('/[A-Z]/', $bucket))
{
// Throw a warning
trigger_error('Since DNS-valid bucket names cannot contain uppercase characters, "' . $bucket . '" has been automatically converted to "' . strtolower($bucket) . '"', E_USER_WARNING);
// Force the bucketname to lowercase
$bucket = strtolower($bucket);
}
// Validate the S3 bucket name for creation
if (!$this->validate_bucketname_create($bucket))
{
// @codeCoverageIgnoreStart
throw new S3_Exception('"' . $bucket . '" is not DNS-valid (i.e., .s3.amazonaws.com), and cannot be used as an S3 bucket name. Review "Bucket Restrictions and Limitations" in the S3 Developer Guide for more information.');
// @codeCoverageIgnoreEnd
}
if (!$opt) $opt = array();
$opt['verb'] = 'PUT';
$opt['headers'] = array(
'Content-Type' => 'application/xml'
);
// Handle Access Control Lists. Can also be passed as an HTTP header.
if (isset($acl))
{
if (is_array($acl))
{
$opt['headers'] = array_merge($opt['headers'], $this->generate_access_policy_headers($acl));
}
else
{
$opt['headers']['x-amz-acl'] = $acl;
}
}
// Defaults
$this->set_region($region); // Also sets path-style
$xml = simplexml_load_string($this->base_location_constraint);
switch ($region)
{
case self::REGION_US_E1: // Northern Virginia
$opt['body'] = '';
break;
case self::REGION_EU_W1: // Ireland
$xml->LocationConstraint = 'EU';
$opt['body'] = $xml->asXML();
break;
default:
$xml->LocationConstraint = str_replace(array('s3-', '.amazonaws.com'), '', $region);
$opt['body'] = $xml->asXML();
break;
}
$response = $this->authenticate($bucket, $opt);
// Make sure we're set back to DNS-style URLs
$this->enable_path_style(false);
return $response;
}
/**
* Gets the region in which the specified Amazon S3 bucket is located.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* preauth - integer|string - Optional - Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with .
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
*/
public function get_bucket_region($bucket, $opt = null)
{
// Add this to our request
if (!$opt) $opt = array();
$opt['verb'] = 'GET';
$opt['sub_resource'] = 'location';
// Authenticate to S3
$response = $this->authenticate($bucket, $opt);
if ($response->isOK())
{
// Handle body
$response->body = (string) $response->body;
}
return $response;
}
/**
* Gets the HTTP headers for the specified Amazon S3 bucket.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* preauth - integer|string - Optional - Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with .
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
*/
public function get_bucket_headers($bucket, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'HEAD';
return $this->authenticate($bucket, $opt);
}
/**
* Deletes a bucket from an Amazon S3 account. A bucket must be empty before the bucket itself can be deleted.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param boolean $force (Optional) Whether to force-delete the bucket and all of its contents. The default value is false.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return mixed A object if the bucket was deleted successfully. Returns boolean false if otherwise.
*/
public function delete_bucket($bucket, $force = false, $opt = null)
{
// Set default value
$success = true;
if ($force)
{
// Delete all of the items from the bucket.
$success = $this->delete_all_object_versions($bucket);
}
// As long as we were successful...
if ($success)
{
if (!$opt) $opt = array();
$opt['verb'] = 'DELETE';
return $this->authenticate($bucket, $opt);
}
// @codeCoverageIgnoreStart
return false;
// @codeCoverageIgnoreEnd
}
/**
* Gets a list of all buckets contained in the caller's Amazon S3 account.
*
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* preauth - integer|string - Optional - Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with .
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
*/
public function list_buckets($opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'GET';
return $this->authenticate('', $opt);
}
/**
* Gets the access control list (ACL) settings for the specified Amazon S3 bucket.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* preauth - integer|string - Optional - Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with .
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAccessPolicy.html REST Access Control Policy
*/
public function get_bucket_acl($bucket, $opt = null)
{
// Add this to our request
if (!$opt) $opt = array();
$opt['verb'] = 'GET';
$opt['sub_resource'] = 'acl';
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Sets the access control list (ACL) settings for the specified Amazon S3 bucket.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $acl (Optional) The ACL settings for the specified bucket. [Allowed values: AmazonS3::ACL_PRIVATE, AmazonS3::ACL_PUBLIC, AmazonS3::ACL_OPEN, AmazonS3::ACL_AUTH_READ, AmazonS3::ACL_OWNER_READ, AmazonS3::ACL_OWNER_FULL_CONTROL]. Alternatively, an array of associative arrays. Each associative array contains an `id` and a `permission` key. The default value is .
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAccessPolicy.html REST Access Control Policy
*/
public function set_bucket_acl($bucket, $acl = self::ACL_PRIVATE, $opt = null)
{
// Add this to our request
if (!$opt) $opt = array();
$opt['verb'] = 'PUT';
$opt['sub_resource'] = 'acl';
$opt['headers'] = array(
'Content-Type' => 'application/xml'
);
// Make sure these are defined.
// @codeCoverageIgnoreStart
if (!$this->credentials->canonical_id || !$this->credentials->canonical_name)
{
// Fetch the data live.
$canonical = $this->get_canonical_user_id();
$this->credentials->canonical_id = $canonical['id'];
$this->credentials->canonical_name = $canonical['display_name'];
}
// @codeCoverageIgnoreEnd
if (is_array($acl))
{
$opt['headers'] = array_merge($opt['headers'], $this->generate_access_policy_headers($acl));
}
else
{
$opt['body'] = '';
$opt['headers']['x-amz-acl'] = $acl;
}
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/*%******************************************************************************************%*/
// OBJECT METHODS
/**
* Creates an Amazon S3 object. After an Amazon S3 bucket is created, objects can be stored in it.
*
* Each standard object can hold up to 5 GB of data. When an object is stored in Amazon S3, the data is streamed
* to multiple storage servers in multiple data centers. This ensures the data remains available in the
* event of internal network or hardware failure.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $filename (Required) The file name for the object.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* acl - string - Optional - The ACL settings for the specified object. Accepts any of the following constants: [Allowed values: AmazonS3::ACL_PRIVATE, AmazonS3::ACL_PUBLIC, AmazonS3::ACL_OPEN, AmazonS3::ACL_AUTH_READ, AmazonS3::ACL_OWNER_READ, AmazonS3::ACL_OWNER_FULL_CONTROL]. Alternatively, an array of associative arrays. Each associative array contains an id and a permission key. The default value is ACL_PRIVATE.
* body - string - Required; Conditional - The data to be stored in the object. Either this parameter or fileUpload must be specified.
* contentType - string - Optional - The type of content that is being sent in the body. If a file is being uploaded via fileUpload as a file system path, it will attempt to determine the correct mime-type based on the file extension. The default value is application/octet-stream.
* encryption - string - Optional - The algorithm to use for encrypting the object. [Allowed values: AES256]
* fileUpload - string|resource - Required; Conditional - The URL/path for the file to upload, or an open resource. Either this parameter or body is required.
* headers - array - Optional - Standard HTTP headers to send along in the request. Accepts an associative array of key-value pairs.
* length - integer - Optional - The size of the object in bytes. For more information, see RFC 2616, section 14.13. The value can also be passed to the header option as Content-Length.
* meta - array - Optional - An associative array of key-value pairs. Represented by x-amz-meta-:. Any header starting with this prefix is considered user metadata. It will be stored with the object and returned when you retrieve the object. The total size of the HTTP request, not including the body, must be less than 4 KB.
* redirectTo - string - Optional - The URI to send an HTTP 301 redirect to when accessing this object. Value must be prefixed either /, http:// or https://.
* seekTo - integer - Optional - The starting position in bytes within the file/stream to upload from.
* storage - string - Optional - Whether to use Standard or Reduced Redundancy storage. [Allowed values: AmazonS3::STORAGE_STANDARD, AmazonS3::STORAGE_REDUCED]. The default value is STORAGE_STANDARD.
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAccessPolicy.html REST Access Control Policy
*/
public function create_object($bucket, $filename, $opt = null)
{
if (!$opt) $opt = array();
// Add this to our request
$opt['verb'] = 'PUT';
$opt['resource'] = $filename;
// Handle content length. Can also be passed as an HTTP header.
if (isset($opt['length']))
{
$opt['headers']['Content-Length'] = $opt['length'];
unset($opt['length']);
}
// Handle content type. Can also be passed as an HTTP header.
if (isset($opt['contentType']))
{
$opt['headers']['Content-Type'] = $opt['contentType'];
unset($opt['contentType']);
}
// Handle Access Control Lists. Can also be passed as an HTTP header.
if (isset($opt['acl']))
{
if (is_array($opt['acl']))
{
$opt['headers'] = array_merge($opt['headers'], $this->generate_access_policy_headers($opt['acl']));
}
else
{
$opt['headers']['x-amz-acl'] = $opt['acl'];
}
}
// Handle storage settings. Can also be passed as an HTTP header.
if (isset($opt['storage']))
{
$opt['headers']['x-amz-storage-class'] = $opt['storage'];
unset($opt['storage']);
}
// Handle encryption settings. Can also be passed as an HTTP header.
if (isset($opt['encryption']))
{
$opt['headers']['x-amz-server-side-encryption'] = $opt['encryption'];
unset($opt['encryption']);
}
// URI to redirect to. Can also be passed as an HTTP header.
if (isset($opt['redirectTo']))
{
$opt['headers']['x-amz-website-redirect-location'] = $opt['redirectTo'];
unset($opt['redirectTo']);
}
// Handle meta tags. Can also be passed as an HTTP header.
if (isset($opt['meta']))
{
foreach ($opt['meta'] as $meta_key => $meta_value)
{
// e.g., `My Meta Header` is converted to `x-amz-meta-my-meta-header`.
$opt['headers']['x-amz-meta-' . strtolower(str_replace(' ', '-', $meta_key))] = $meta_value;
}
unset($opt['meta']);
}
$opt['headers']['Expect'] = '100-continue';
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Gets the contents of an Amazon S3 object in the specified bucket.
*
* The MD5 value for an object can be retrieved from the ETag HTTP header for any object that was uploaded
* with a normal PUT/POST. This value is incorrect for multipart uploads.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $filename (Required) The file name for the object.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* etag - string - Optional - The ETag header passed in from a previous request. If specified, request LastModified option must be specified as well. Will trigger a 304 Not Modified status code if the file hasn't changed.
* fileDownload - string|resource - Optional - The file system location to download the file to, or an open file resource. Must be a server-writable location.
* headers - array - Optional - Standard HTTP headers to send along in the request. Accepts an associative array of key-value pairs.
* lastmodified - string - Optional - The LastModified header passed in from a previous request. If specified, request ETag option must be specified as well. Will trigger a 304 Not Modified status code if the file hasn't changed.
* preauth - integer|string - Optional - Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with .
* range - string - Optional - The range of bytes to fetch from the object. Specify this parameter when downloading partial bits or completing incomplete object downloads. The specified range must be notated with a hyphen (e.g., 0-10485759). Defaults to the byte range of the complete Amazon S3 object.
* response - array - Optional - Allows adjustments to specific response headers. Pass an associative array where each key is one of the following: cache-control, content-disposition, content-encoding, content-language, content-type, expires. The expires value should use and be formatted with the DATE_RFC2822 constant.
* versionId - string - Optional - The version of the object to retrieve. Version IDs are returned in the x-amz-version-id header of any previous object-related request.
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
*/
public function get_object($bucket, $filename, $opt = null)
{
if (!$opt) $opt = array();
// Add this to our request
$opt['verb'] = 'GET';
$opt['resource'] = $filename;
if (!isset($opt['headers']) || !is_array($opt['headers']))
{
$opt['headers'] = array();
}
if (isset($opt['lastmodified']))
{
$opt['headers']['If-Modified-Since'] = $opt['lastmodified'];
}
if (isset($opt['etag']))
{
$opt['headers']['If-None-Match'] = $opt['etag'];
}
// Partial content range
if (isset($opt['range']))
{
$opt['headers']['Range'] = 'bytes=' . $opt['range'];
}
// GET responses
if (isset($opt['response']))
{
foreach ($opt['response'] as $key => $value)
{
$opt['response-' . $key] = $value;
unset($opt['response'][$key]);
}
}
// Authenticate to S3
$this->parse_the_response = false;
$response = $this->authenticate($bucket, $opt);
$this->parse_the_response = true;
return $response;
}
/**
* Gets the HTTP headers for the specified Amazon S3 object.
*
* The MD5 value for an object can be retrieved from the ETag HTTP header for any object that was uploaded
* with a normal PUT/POST. This value is incorrect for multipart uploads.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $filename (Required) The file name for the object.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* versionId - string - Optional - The version of the object to retrieve. Version IDs are returned in the x-amz-version-id header of any previous object-related request.
* preauth - integer|string - Optional - Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with .
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
*/
public function get_object_headers($bucket, $filename, $opt = null)
{
// Add this to our request
if (!$opt) $opt = array();
$opt['verb'] = 'HEAD';
$opt['resource'] = $filename;
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Deletes an Amazon S3 object from the specified bucket.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $filename (Required) The file name for the object.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* versionId - string - Optional - The version of the object to delete. Version IDs are returned in the x-amz-version-id header of any previous object-related request.
* MFASerial - string - Optional - The serial number on the back of the Gemalto device. MFASerial and MFAToken must both be set for MFA to work.
* MFAToken - string - Optional - The current token displayed on the Gemalto device. MFASerial and MFAToken must both be set for MFA to work.
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
* @link http://aws.amazon.com/mfa/ Multi-Factor Authentication
*/
public function delete_object($bucket, $filename, $opt = null)
{
// Add this to our request
if (!$opt) $opt = array();
$opt['verb'] = 'DELETE';
$opt['resource'] = $filename;
// Enable MFA delete?
// @codeCoverageIgnoreStart
if (isset($opt['MFASerial']) && isset($opt['MFAToken']))
{
$opt['headers'] = array(
'x-amz-mfa' => ($opt['MFASerial'] . ' ' . $opt['MFAToken'])
);
}
// @codeCoverageIgnoreEnd
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Deletes one or more specified Amazon S3 objects from the specified bucket.
*
* Since `delete_object()` is designed for deleting a single object, this method is intended to be used
* when there are two or more objects to delete.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* objects - array - Required - The object references to delete from the bucket.
* key - string - Required - The name of the object (e.g., the "key") to delete. This should include the entire file path including all "subdirectories".
* version_id - string - Optional - If the object is versioned, include the version ID to delete.
*
* quiet - boolean - Optional - Whether or not Amazon S3 should use "Quiet" mode for this operation. A value of true will enable Quiet mode. A value of false will use Verbose mode. The default value is false.
* MFASerial - string - Optional - The serial number on the back of the Gemalto device. MFASerial and MFAToken must both be set for MFA to work.
* MFAToken - string - Optional - The current token displayed on the Gemalto device. MFASerial and MFAToken must both be set for MFA to work.
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
* @link http://aws.amazon.com/mfa/ Multi-Factor Authentication
*/
public function delete_objects($bucket, $opt = null)
{
// Add this to our request
if (!$opt) $opt = array();
$opt['verb'] = 'POST';
$opt['sub_resource'] = 'delete';
$opt['body'] = '';
// Bail out
if (!isset($opt['objects']) || !is_array($opt['objects']))
{
throw new S3_Exception('The ' . __FUNCTION__ . ' method requires the "objects" option to be set as an array.');
}
$xml = new SimpleXMLElement($this->multi_object_delete_xml);
// Add the objects
foreach ($opt['objects'] as $object)
{
$xobject = $xml->addChild('Object');
$node = $xobject->addChild('Key');
$node[0] = $object['key'];
if (isset($object['version_id']))
{
$xobject->addChild('VersionId', $object['version_id']);
}
}
// Quiet mode?
if (isset($opt['quiet']))
{
$quiet = 'false';
if (is_bool($opt['quiet'])) // Boolean
{
$quiet = $opt['quiet'] ? 'true' : 'false';
}
elseif (is_string($opt['quiet'])) // String
{
$quiet = ($opt['quiet'] === 'true') ? 'true' : 'false';
}
$xml->addChild('Quiet', $quiet);
}
// Enable MFA delete?
// @codeCoverageIgnoreStart
if (isset($opt['MFASerial']) && isset($opt['MFAToken']))
{
$opt['headers'] = array(
'x-amz-mfa' => ($opt['MFASerial'] . ' ' . $opt['MFAToken'])
);
}
// @codeCoverageIgnoreEnd
$opt['body'] = $xml->asXML();
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Gets a list of all Amazon S3 objects in the specified bucket.
*
* NOTE: This method is paginated, and will not return more than max-keys keys. If you want to retrieve a list of all keys, you will need to make multiple calls to this function using the marker option to specify the pagination offset (the key of the last processed key--lexically ordered) and the IsTruncated response key to detect when all results have been processed. See: the S3 REST documentation for get_bucket for more information.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* delimiter - string - Optional - Keys that contain the same string between the prefix and the first occurrence of the delimiter will be rolled up into a single result element in the CommonPrefixes collection.
* marker - string - Optional - Restricts the response to contain results that only occur alphabetically after the value of the marker.
* max-keys - string - Optional - The maximum number of results returned by the method call. The returned list will contain no more results than the specified value, but may return fewer. The default value is 1000.
* preauth - integer|string - Optional - Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with .
* prefix - string - Optional - Restricts the response to contain results that begin only with the specified prefix.
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
*/
public function list_objects($bucket, $opt = null)
{
if (!$opt) $opt = array();
// Add this to our request
$opt['verb'] = 'GET';
foreach (array('delimiter', 'marker', 'max-keys', 'prefix') as $param)
{
if (isset($opt[$param]))
{
$opt['query_string'][$param] = $opt[$param];
unset($opt[$param]);
}
}
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Copies an Amazon S3 object to a new location, whether in the same Amazon S3 region, bucket, or otherwise.
*
* NOTE: Object redirect locations are not carried over when an object is copied.
*
* @param array $source (Required) The bucket and file name to copy from. The following keys must be set:
* bucket - string - Required - Specifies the name of the bucket containing the source object.
* filename - string - Required - Specifies the file name of the source object to copy.
* @param array $dest (Required) The bucket and file name to copy to. The following keys must be set:
* bucket - string - Required - Specifies the name of the bucket to copy the object to.
* filename - string - Required - Specifies the file name to copy the object to.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* acl - string - Optional - The ACL settings for the specified object. [Allowed values: AmazonS3::ACL_PRIVATE, AmazonS3::ACL_PUBLIC, AmazonS3::ACL_OPEN, AmazonS3::ACL_AUTH_READ, AmazonS3::ACL_OWNER_READ, AmazonS3::ACL_OWNER_FULL_CONTROL]. Alternatively, an array of associative arrays. Each associative array contains an id and a permission key. The default value is ACL_PRIVATE.
* encryption - string - Optional - The algorithm to use for encrypting the object. [Allowed values: AES256]
* storage - string - Optional - Whether to use Standard or Reduced Redundancy storage. [Allowed values: AmazonS3::STORAGE_STANDARD, AmazonS3::STORAGE_REDUCED]. The default value is STORAGE_STANDARD.
* versionId - string - Optional - The version of the object to copy. Version IDs are returned in the x-amz-version-id header of any previous object-related request.
* ifMatch - string - Optional - The ETag header from a previous request. Copies the object if its entity tag (ETag) matches the specified tag; otherwise, the request returns a 412 HTTP status code error (precondition failed). Used in conjunction with ifUnmodifiedSince.
* ifUnmodifiedSince - string - Optional - The LastModified header from a previous request. Copies the object if it hasn't been modified since the specified time; otherwise, the request returns a 412 HTTP status code error (precondition failed). Used in conjunction with ifMatch.
* ifNoneMatch - string - Optional - The ETag header from a previous request. Copies the object if its entity tag (ETag) is different than the specified ETag; otherwise, the request returns a 412 HTTP status code error (failed condition). Used in conjunction with ifModifiedSince.
* ifModifiedSince - string - Optional - The LastModified header from a previous request. Copies the object if it has been modified since the specified time; otherwise, the request returns a 412 HTTP status code error (failed condition). Used in conjunction with ifNoneMatch.
* headers - array - Optional - Standard HTTP headers to send along in the request. Accepts an associative array of key-value pairs.
* meta - array - Optional - Associative array of key-value pairs. Represented by x-amz-meta-: Any header starting with this prefix is considered user metadata. It will be stored with the object and returned when you retrieve the object. The total size of the HTTP request, not including the body, must be less than 4 KB.
* metadataDirective - string - Optional - Accepts either COPY or REPLACE. You will likely never need to use this, as it manages itself with no issues.
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/API/RESTObjectCOPY.html Copying Amazon S3 Objects
*/
public function copy_object($source, $dest, $opt = null)
{
if (!$opt) $opt = array();
$batch = array();
// Add this to our request
$opt['verb'] = 'PUT';
$opt['resource'] = $dest['filename'];
$opt['body'] = '';
// Handle copy source
if (isset($source['bucket']) && isset($source['filename']))
{
$opt['headers']['x-amz-copy-source'] = '/' . $source['bucket'] . '/' . rawurlencode($source['filename'])
. (isset($opt['versionId']) ? ('?' . 'versionId=' . rawurlencode($opt['versionId'])) : ''); // Append the versionId to copy, if available
unset($opt['versionId']);
}
// Handle metadata directive
$opt['headers']['x-amz-metadata-directive'] = 'COPY';
if ($source['bucket'] === $dest['bucket'] && $source['filename'] === $dest['filename'])
{
$opt['headers']['x-amz-metadata-directive'] = 'REPLACE';
}
if (isset($opt['metadataDirective']))
{
$opt['headers']['x-amz-metadata-directive'] = $opt['metadataDirective'];
unset($opt['metadataDirective']);
}
// Handle Access Control Lists. Can also pass canned ACLs as an HTTP header.
if (isset($opt['acl']) && is_array($opt['acl']))
{
$batch[] = $this->set_object_acl($dest['bucket'], $dest['filename'], $opt['acl'], array(
'returnCurlHandle' => true
));
unset($opt['acl']);
}
elseif (isset($opt['acl']))
{
$opt['headers']['x-amz-acl'] = $opt['acl'];
unset($opt['acl']);
}
// Handle storage settings. Can also be passed as an HTTP header.
if (isset($opt['storage']))
{
$opt['headers']['x-amz-storage-class'] = $opt['storage'];
unset($opt['storage']);
}
// Handle encryption settings. Can also be passed as an HTTP header.
if (isset($opt['encryption']))
{
$opt['headers']['x-amz-server-side-encryption'] = $opt['encryption'];
unset($opt['encryption']);
}
// Handle conditional-copy parameters
if (isset($opt['ifMatch']))
{
$opt['headers']['x-amz-copy-source-if-match'] = $opt['ifMatch'];
unset($opt['ifMatch']);
}
if (isset($opt['ifNoneMatch']))
{
$opt['headers']['x-amz-copy-source-if-none-match'] = $opt['ifNoneMatch'];
unset($opt['ifNoneMatch']);
}
if (isset($opt['ifUnmodifiedSince']))
{
$opt['headers']['x-amz-copy-source-if-unmodified-since'] = $opt['ifUnmodifiedSince'];
unset($opt['ifUnmodifiedSince']);
}
if (isset($opt['ifModifiedSince']))
{
$opt['headers']['x-amz-copy-source-if-modified-since'] = $opt['ifModifiedSince'];
unset($opt['ifModifiedSince']);
}
// Handle meta tags. Can also be passed as an HTTP header.
if (isset($opt['meta']))
{
foreach ($opt['meta'] as $meta_key => $meta_value)
{
// e.g., `My Meta Header` is converted to `x-amz-meta-my-meta-header`.
$opt['headers']['x-amz-meta-' . strtolower(str_replace(' ', '-', $meta_key))] = $meta_value;
}
unset($opt['meta']);
}
// Authenticate to S3
$response = $this->authenticate($dest['bucket'], $opt);
// Attempt to reset ACLs
$http = new CFRequest();
$http->send_multi_request($batch);
return $response;
}
/**
* Updates an Amazon S3 object with new headers or other metadata. To replace the content of the
* specified Amazon S3 object, call with the same bucket and file name parameters.
*
* @param string $bucket (Required) The name of the bucket that contains the source file.
* @param string $filename (Required) The source file name that you want to update.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* acl - string - Optional - The ACL settings for the specified object. [Allowed values: AmazonS3::ACL_PRIVATE, AmazonS3::ACL_PUBLIC, AmazonS3::ACL_OPEN, AmazonS3::ACL_AUTH_READ, AmazonS3::ACL_OWNER_READ, AmazonS3::ACL_OWNER_FULL_CONTROL]. The default value is .
* headers - array - Optional - Standard HTTP headers to send along in the request. Accepts an associative array of key-value pairs.
* meta - array - Optional - An associative array of key-value pairs. Any header with the x-amz-meta- prefix is considered user metadata and is stored with the Amazon S3 object. It will be stored with the object and returned when you retrieve the object. The total size of the HTTP request, not including the body, must be less than 4 KB.
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/API/RESTObjectCOPY.html Copying Amazon S3 Objects
*/
public function update_object($bucket, $filename, $opt = null)
{
if (!$opt) $opt = array();
$opt['metadataDirective'] = 'REPLACE';
// Retrieve the original metadata
if ($metadata = $this->get_object_metadata($bucket, $filename))
{
if (isset($metadata['ACL']))
{
$opt['acl'] = isset($opt['acl']) ? $opt['acl'] : $metadata['ACL'];
}
if (isset($metadata['StorageClass']))
{
$opt['headers']['x-amz-storage-class'] = $metadata['StorageClass'];
}
if (isset($metadata['ContentType']))
{
$opt['headers']['Content-Type'] = $metadata['ContentType'];
}
}
// Remove a header
unset($metadata['Headers']['date']);
// Merge headers
$opt['headers'] = array_merge($opt['headers'], $metadata['Headers']);
// Authenticate to S3
return $this->copy_object(
array('bucket' => $bucket, 'filename' => $filename),
array('bucket' => $bucket, 'filename' => $filename),
$opt
);
}
/*%******************************************************************************************%*/
// ACCESS CONTROL LISTS
/**
* Gets the access control list (ACL) settings for the specified Amazon S3 object.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $filename (Required) The file name for the object.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* versionId - string - Optional - The version of the object to retrieve. Version IDs are returned in the x-amz-version-id header of any previous object-related request.
* preauth - integer|string - Optional - Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with .
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAccessPolicy.html REST Access Control Policy
*/
public function get_object_acl($bucket, $filename, $opt = null)
{
// Add this to our request
if (!$opt) $opt = array();
$opt['verb'] = 'GET';
$opt['resource'] = $filename;
$opt['sub_resource'] = 'acl';
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Sets the access control list (ACL) settings for the specified Amazon S3 object.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $filename (Required) The file name for the object.
* @param string $acl (Optional) The ACL settings for the specified object. Accepts any of the following constants: [Allowed values: AmazonS3::ACL_PRIVATE, AmazonS3::ACL_PUBLIC, AmazonS3::ACL_OPEN, AmazonS3::ACL_AUTH_READ, AmazonS3::ACL_OWNER_READ, AmazonS3::ACL_OWNER_FULL_CONTROL]. Alternatively, an array of associative arrays. Each associative array contains an id and a permission key. The default value is ACL_PRIVATE.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAccessPolicy.html REST Access Control Policy
*/
public function set_object_acl($bucket, $filename, $acl = self::ACL_PRIVATE, $opt = null)
{
// Add this to our request
if (!$opt) $opt = array();
$opt['verb'] = 'PUT';
$opt['resource'] = $filename;
$opt['sub_resource'] = 'acl';
// Retrieve the original metadata
$metadata = $this->get_object_metadata($bucket, $filename);
if ($metadata && $metadata['ContentType'])
{
$opt['headers']['Content-Type'] = $metadata['ContentType'];
}
if ($metadata && $metadata['StorageClass'])
{
$opt['headers']['x-amz-storage-class'] = $metadata['StorageClass'];
}
// Make sure these are defined.
// @codeCoverageIgnoreStart
if (!$this->credentials->canonical_id || !$this->credentials->canonical_name)
{
// Fetch the data live.
$canonical = $this->get_canonical_user_id();
$this->credentials->canonical_id = $canonical['id'];
$this->credentials->canonical_name = $canonical['display_name'];
}
// @codeCoverageIgnoreEnd
if (is_array($acl))
{
$opt['headers'] = array_merge($opt['headers'], $this->generate_access_policy_headers($acl));
}
else
{
$opt['body'] = '';
$opt['headers']['x-amz-acl'] = $acl;
}
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Generates the XML to be used for the Access Control Policy.
*
* @param string $canonical_id (Required) The canonical ID for the bucket owner. This is provided as the `id` return value from .
* @param string $canonical_name (Required) The canonical display name for the bucket owner. This is provided as the `display_name` value from .
* @param array $users (Optional) An array of associative arrays. Each associative array contains an `id` value and a `permission` value.
* @return string Access Control Policy XML.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/ACLOverview.html Access Control Lists
*/
public function generate_access_policy($canonical_id, $canonical_name, $users)
{
$xml = simplexml_load_string($this->base_acp_xml);
$owner = $xml->addChild('Owner');
$owner->addChild('ID', $canonical_id);
$owner->addChild('DisplayName', $canonical_name);
$acl = $xml->addChild('AccessControlList');
foreach ($users as $user)
{
$grant = $acl->addChild('Grant');
$grantee = $grant->addChild('Grantee');
switch ($user['id'])
{
// Authorized Users
case self::USERS_AUTH:
$grantee->addAttribute('xsi:type', 'Group', 'http://www.w3.org/2001/XMLSchema-instance');
$grantee->addChild('URI', self::USERS_AUTH);
break;
// All Users
case self::USERS_ALL:
$grantee->addAttribute('xsi:type', 'Group', 'http://www.w3.org/2001/XMLSchema-instance');
$grantee->addChild('URI', self::USERS_ALL);
break;
// The Logging User
case self::USERS_LOGGING:
$grantee->addAttribute('xsi:type', 'Group', 'http://www.w3.org/2001/XMLSchema-instance');
$grantee->addChild('URI', self::USERS_LOGGING);
break;
// Email Address or Canonical Id
default:
if (strpos($user['id'], '@'))
{
$grantee->addAttribute('xsi:type', 'AmazonCustomerByEmail', 'http://www.w3.org/2001/XMLSchema-instance');
$grantee->addChild('EmailAddress', $user['id']);
}
else
{
// Assume Canonical Id
$grantee->addAttribute('xsi:type', 'CanonicalUser', 'http://www.w3.org/2001/XMLSchema-instance');
$grantee->addChild('ID', $user['id']);
}
break;
}
$grant->addChild('Permission', $user['permission']);
}
return $xml->asXML();
}
/**
* Generates the HTTP headers to be used for the Access Control Policy Grants.
*
* @param array $users (Optional) An array of associative arrays. Each associative array contains an `id` value and a `permission` value.
* @return array HTTP headers to be applied to the request.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/ACLOverview.html Access Control Lists
*/
public function generate_access_policy_headers($users)
{
$headers = array();
foreach ($users as $user)
{
// Determine permission. If doesn't exist, create it.
$permission = 'x-amz-grant-' . str_replace('_', '-', strtolower($user['permission']));
if (!isset($headers[$permission]))
{
$headers[$permission] = array();
}
// Handle the IDs
switch ($user['id'])
{
case self::USERS_AUTH: // Authorized Users
case self::USERS_ALL: // All Users
case self::USERS_LOGGING: // The Logging User
$headers[$permission][] = 'uri="' . $user['id'] . '"';
break;
// Email Address or Canonical Id
default:
if (strpos($user['id'], '@'))
{
// Treat as email address
$headers[$permission][] = 'emailAddress="' . $user['id'] . '"';
}
else
{
// Assume Canonical Id
$headers[$permission][] = 'id="' . $user['id'] . '"';
}
break;
}
}
foreach ($headers as &$permission)
{
$permission = implode(', ', $permission);
}
return $headers;
}
/*%******************************************************************************************%*/
// LOGGING METHODS
/**
* Gets the access logs associated with the specified Amazon S3 bucket.
*
* @param string $bucket (Required) The name of the bucket to use. Pass a `null` value when using the method.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* preauth - integer|string - Optional - Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with .
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/ServerLogs.html Server Access Logging
*/
public function get_logs($bucket, $opt = null)
{
// Add this to our request
if (!$opt) $opt = array();
$opt['verb'] = 'GET';
$opt['sub_resource'] = 'logging';
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Enables access logging for the specified Amazon S3 bucket.
*
* @param string $bucket (Required) The name of the bucket to enable logging for. Pass a `null` value when using the method.
* @param string $target_bucket (Required) The name of the bucket to store the logs in.
* @param string $target_prefix (Required) The prefix to give to the log file names.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* users - array - Optional - An array of associative arrays specifying any user to give access to. Each associative array contains an id and permission value.
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/LoggingAPI.html Server Access Logging Configuration API
*/
public function enable_logging($bucket, $target_bucket, $target_prefix, $opt = null)
{
// Add this to our request
if (!$opt) $opt = array();
$opt['verb'] = 'PUT';
$opt['sub_resource'] = 'logging';
$opt['headers'] = array(
'Content-Type' => 'application/xml'
);
$xml = simplexml_load_string($this->base_logging_xml);
$LoggingEnabled = $xml->addChild('LoggingEnabled');
$LoggingEnabled->addChild('TargetBucket', $target_bucket);
$LoggingEnabled->addChild('TargetPrefix', $target_prefix);
$TargetGrants = $LoggingEnabled->addChild('TargetGrants');
if (isset($opt['users']) && is_array($opt['users']))
{
foreach ($opt['users'] as $user)
{
$grant = $TargetGrants->addChild('Grant');
$grantee = $grant->addChild('Grantee');
switch ($user['id'])
{
// Authorized Users
case self::USERS_AUTH:
$grantee->addAttribute('xsi:type', 'Group', 'http://www.w3.org/2001/XMLSchema-instance');
$grantee->addChild('URI', self::USERS_AUTH);
break;
// All Users
case self::USERS_ALL:
$grantee->addAttribute('xsi:type', 'Group', 'http://www.w3.org/2001/XMLSchema-instance');
$grantee->addChild('URI', self::USERS_ALL);
break;
// The Logging User
case self::USERS_LOGGING:
$grantee->addAttribute('xsi:type', 'Group', 'http://www.w3.org/2001/XMLSchema-instance');
$grantee->addChild('URI', self::USERS_LOGGING);
break;
// Email Address or Canonical Id
default:
if (strpos($user['id'], '@'))
{
$grantee->addAttribute('xsi:type', 'AmazonCustomerByEmail', 'http://www.w3.org/2001/XMLSchema-instance');
$grantee->addChild('EmailAddress', $user['id']);
}
else
{
// Assume Canonical Id
$grantee->addAttribute('xsi:type', 'CanonicalUser', 'http://www.w3.org/2001/XMLSchema-instance');
$grantee->addChild('ID', $user['id']);
}
break;
}
$grant->addChild('Permission', $user['permission']);
}
}
$opt['body'] = $xml->asXML();
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Disables access logging for the specified Amazon S3 bucket.
*
* @param string $bucket (Required) The name of the bucket to use. Pass `null` if using .
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/LoggingAPI.html Server Access Logging Configuration API
*/
public function disable_logging($bucket, $opt = null)
{
// Add this to our request
if (!$opt) $opt = array();
$opt['verb'] = 'PUT';
$opt['sub_resource'] = 'logging';
$opt['headers'] = array(
'Content-Type' => 'application/xml'
);
$opt['body'] = $this->base_logging_xml;
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/*%******************************************************************************************%*/
// CONVENIENCE METHODS
/**
* Gets whether or not the specified Amazon S3 bucket exists in Amazon S3. This includes buckets
* that do not belong to the caller.
*
* @param string $bucket (Required) The name of the bucket to use.
* @return boolean A value of true if the bucket exists, or a value of false if it does not.
*/
public function if_bucket_exists($bucket)
{
if ($this->use_batch_flow)
{
throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested');
}
$header = $this->get_bucket_headers($bucket);
return (integer) $header->status !== 404;
}
/**
* Gets whether or not the specified Amazon S3 object exists in the specified bucket.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $filename (Required) The file name for the object.
* @return boolean A value of true if the object exists, or a value of false if it does not.
*/
public function if_object_exists($bucket, $filename)
{
if ($this->use_batch_flow)
{
throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested');
}
$header = $this->get_object_headers($bucket, $filename);
if ($header->isOK()) { return true; }
elseif ($header->status === 404) { return false; }
// @codeCoverageIgnoreStart
return null;
// @codeCoverageIgnoreEnd
}
/**
* Gets whether or not the specified Amazon S3 bucket has a bucket policy associated with it.
*
* @param string $bucket (Required) The name of the bucket to use.
* @return boolean A value of true if a bucket policy exists, or a value of false if one does not.
*/
public function if_bucket_policy_exists($bucket)
{
if ($this->use_batch_flow)
{
// @codeCoverageIgnoreStart
throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested');
// @codeCoverageIgnoreEnd
}
$response = $this->get_bucket_policy($bucket);
if ($response->isOK()) { return true; }
elseif ($response->status === 404) { return false; }
// @codeCoverageIgnoreStart
return null;
// @codeCoverageIgnoreEnd
}
/**
* Gets the number of Amazon S3 objects in the specified bucket.
*
* @param string $bucket (Required) The name of the bucket to use.
* @return integer The number of Amazon S3 objects in the bucket.
*/
public function get_bucket_object_count($bucket)
{
if ($this->use_batch_flow)
{
// @codeCoverageIgnoreStart
throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested');
// @codeCoverageIgnoreEnd
}
return count($this->get_object_list($bucket));
}
/**
* Gets the cumulative file size of the contents of the Amazon S3 bucket.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param boolean $friendly_format (Optional) A value of true will format the return value to 2 decimal points using the largest possible unit (i.e., 3.42 GB). A value of false will format the return value as the raw number of bytes.
* @return integer|string The number of bytes as an integer, or the friendly format as a string.
*/
public function get_bucket_filesize($bucket, $friendly_format = false)
{
if ($this->use_batch_flow)
{
throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested');
}
$filesize = 0;
$list = $this->list_objects($bucket);
foreach ($list->body->Contents as $filename)
{
$filesize += (integer) $filename->Size;
}
while ((string) $list->body->IsTruncated === 'true')
{
$body = (array) $list->body;
$list = $this->list_objects($bucket, array(
'marker' => (string) end($body['Contents'])->Key
));
foreach ($list->body->Contents as $object)
{
$filesize += (integer) $object->Size;
}
}
if ($friendly_format)
{
$filesize = $this->util->size_readable($filesize);
}
return $filesize;
}
/**
* Gets the file size of the specified Amazon S3 object.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $filename (Required) The file name for the object.
* @param boolean $friendly_format (Optional) A value of true will format the return value to 2 decimal points using the largest possible unit (i.e., 3.42 GB). A value of false will format the return value as the raw number of bytes.
* @return integer|string|boolean The number of bytes as an integer, or the friendly format as a string. Returns false if the request failed.
*/
public function get_object_filesize($bucket, $filename, $friendly_format = false)
{
if ($this->use_batch_flow)
{
throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested');
}
$response = $this->get_object_headers($bucket, $filename);
if (!$response->isOK()) {
return false;
}
$filesize = (integer) $response->header['content-length'];
if ($friendly_format)
{
$filesize = $this->util->size_readable($filesize);
}
return $filesize;
}
/**
* Changes the content type for an existing Amazon S3 object.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $filename (Required) The file name for the object.
* @param string $contentType (Required) The content-type to apply to the object.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
*/
public function change_content_type($bucket, $filename, $contentType, $opt = null)
{
if (!$opt) $opt = array();
// Retrieve the original metadata
$metadata = $this->get_object_metadata($bucket, $filename);
if ($metadata && isset($metadata['ACL']))
{
$opt['acl'] = $metadata['ACL'];
}
if ($metadata && isset($metadata['StorageClass']))
{
$opt['headers']['x-amz-storage-class'] = $metadata['StorageClass'];
}
// Merge optional parameters
$opt = array_merge_recursive(array(
'headers' => array(
'Content-Type' => $contentType
),
'metadataDirective' => 'REPLACE'
), $opt);
return $this->copy_object(
array('bucket' => $bucket, 'filename' => $filename),
array('bucket' => $bucket, 'filename' => $filename),
$opt
);
}
/**
* Changes the storage redundancy for an existing object.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $filename (Required) The file name for the object.
* @param string $storage (Required) The storage setting to apply to the object. [Allowed values: AmazonS3::STORAGE_STANDARD, AmazonS3::STORAGE_REDUCED]
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
*/
public function change_storage_redundancy($bucket, $filename, $storage, $opt = null)
{
if (!$opt) $opt = array();
// Retrieve the original metadata
$metadata = $this->get_object_metadata($bucket, $filename);
if ($metadata && isset($metadata['ACL']))
{
$opt['acl'] = $metadata['ACL'];
}
if ($metadata && isset($metadata['StorageClass']))
{
$opt['headers']['x-amz-storage-class'] = $metadata['StorageClass'];
}
// Merge optional parameters
$opt = array_merge(array(
'storage' => $storage,
'metadataDirective' => 'COPY',
), $opt);
return $this->copy_object(
array('bucket' => $bucket, 'filename' => $filename),
array('bucket' => $bucket, 'filename' => $filename),
$opt
);
}
/**
* Gets a simplified list of bucket names on an Amazon S3 account.
*
* @param string $pcre (Optional) A Perl-Compatible Regular Expression (PCRE) to filter the bucket names against.
* @return array The list of matching bucket names. If there are no results, the method will return an empty array.
* @link http://php.net/pcre Regular Expressions (Perl-Compatible)
*/
public function get_bucket_list($pcre = null)
{
if ($this->use_batch_flow)
{
throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested');
}
// Get a list of buckets.
$list = $this->list_buckets();
if ($list = $list->body->query('descendant-or-self::Name'))
{
$list = $list->map_string($pcre);
return $list;
}
// @codeCoverageIgnoreStart
return array();
// @codeCoverageIgnoreEnd
}
/**
* Gets a simplified list of Amazon S3 object file names contained in a bucket.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* delimiter - string - Optional - Keys that contain the same string between the prefix and the first occurrence of the delimiter will be rolled up into a single result element in the CommonPrefixes collection.
* marker - string - Optional - Restricts the response to contain results that only occur alphabetically after the value of the marker.
* max-keys - integer - Optional - The maximum number of results returned by the method call. The returned list will contain no more results than the specified value, but may return less. A value of zero is treated as if you did not specify max-keys.
* pcre - string - Optional - A Perl-Compatible Regular Expression (PCRE) to filter the names against. This is applied only AFTER any native Amazon S3 filtering from specified prefix, marker, max-keys, or delimiter values are applied.
* prefix - string - Optional - Restricts the response to contain results that begin only with the specified prefix.
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* @return array The list of matching object names. If there are no results, the method will return an empty array.
* @link http://php.net/pcre Regular Expressions (Perl-Compatible)
*/
public function get_object_list($bucket, $opt = null)
{
if ($this->use_batch_flow)
{
throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested');
}
if (!$opt) $opt = array();
unset($opt['returnCurlHandle']); // This would cause problems
// Set some default values
$pcre = isset($opt['pcre']) ? $opt['pcre'] : null;
$max_keys = (isset($opt['max-keys']) && is_int($opt['max-keys'])) ? $opt['max-keys'] : null;
$objects = array();
if (!$max_keys)
{
// No max-keys specified. Get everything.
do
{
$list = $this->list_objects($bucket, $opt);
if (is_string($list->body))
{
$list->body = new CFSimpleXML($list->body);
}
if ($keys = $list->body->query('descendant-or-self::Key')->map_string($pcre))
{
$objects = array_merge($objects, $keys);
}
$body = (array) $list->body;
$opt = array_merge($opt, array(
'marker' => (isset($body['Contents']) && is_array($body['Contents'])) ?
((string) end($body['Contents'])->Key) :
((string) $list->body->Contents->Key)
));
}
while ((string) $list->body->IsTruncated === 'true');
}
else
{
// Max-keys specified. Approximate number of loops and make the requests.
$max_keys = $opt['max-keys'];
$loops = ceil($max_keys / 1000);
do
{
$list = $this->list_objects($bucket, $opt);
if (is_string($list->body))
{
$list->body = new CFSimpleXML($list->body);
}
$keys = $list->body->query('descendant-or-self::Key')->map_string($pcre);
if ($count = count($keys))
{
$objects = array_merge($objects, $keys);
if ($count < 1000)
{
break;
}
}
if ($max_keys > 1000)
{
$max_keys -= 1000;
}
$body = (array) $list->body;
$opt = array_merge($opt, array(
'max-keys' => $max_keys,
'marker' => (isset($body['Contents']) && is_array($body['Contents'])) ?
((string) end($body['Contents'])->Key) :
((string) $list->body->Contents->Key)
));
}
while (--$loops);
}
return $objects;
}
/**
* Deletes all Amazon S3 objects inside the specified bucket.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $pcre (Optional) A Perl-Compatible Regular Expression (PCRE) to filter the names against. The default value is .
* @return boolean A value of true means that all objects were successfully deleted. A value of false means that at least one object failed to delete.
* @link http://php.net/pcre Regular Expressions (Perl-Compatible)
*/
public function delete_all_objects($bucket, $pcre = self::PCRE_ALL)
{
// Collect all matches
$list = $this->get_object_list($bucket, array('pcre' => $pcre));
// As long as we have at least one match...
if (count($list) > 0)
{
$objects = array();
foreach ($list as $object)
{
$objects[] = array('key' => $object);
}
$batch = new CFBatchRequest();
$batch->use_credentials($this->credentials);
foreach (array_chunk($objects, 1000) as $object_set)
{
$this->batch($batch)->delete_objects($bucket, array(
'objects' => $object_set
));
}
$responses = $this->batch($batch)->send();
$is_ok = true;
foreach ($responses as $response)
{
if (!$response->isOK() || isset($response->body->Error))
{
$is_ok = false;
}
}
return $is_ok;
}
// If there are no matches, return true
return true;
}
/**
* Deletes all of the versions of all Amazon S3 objects inside the specified bucket.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $pcre (Optional) A Perl-Compatible Regular Expression (PCRE) to filter the names against. The default value is .
* @return boolean A value of true means that all object versions were successfully deleted. A value of false means that at least one object/version failed to delete.
* @link http://php.net/pcre Regular Expressions (Perl-Compatible)
*/
public function delete_all_object_versions($bucket, $pcre = null)
{
// Instantiate
$versions = $this->list_bucket_object_versions($bucket);
// Gather all nodes together into a single array
if ($versions->body->DeleteMarker() && $versions->body->Version())
{
$markers = array_merge($versions->body->DeleteMarker()->getArrayCopy(), $versions->body->Version()->getArrayCopy());
}
elseif ($versions->body->DeleteMarker())
{
$markers = $versions->body->DeleteMarker()->getArrayCopy();
}
elseif ($versions->body->Version())
{
$markers = $versions->body->Version()->getArrayCopy();
}
else
{
$markers = array();
}
while ((string) $versions->body->IsTruncated === 'true')
{
$versions = $this->list_bucket_object_versions($bucket, array(
'key-marker' => (string) $versions->body->NextKeyMarker
));
// Gather all nodes together into a single array
if ($versions->body->DeleteMarker() && $versions->body->Version())
{
$markers = array_merge($markers, $versions->body->DeleteMarker()->getArrayCopy(), $versions->body->Version()->getArrayCopy());
}
elseif ($versions->body->DeleteMarker())
{
$markers = array_merge($markers, $versions->body->DeleteMarker()->getArrayCopy());
}
elseif ($versions->body->Version())
{
$markers = array_merge($markers, $versions->body->Version()->getArrayCopy());
}
}
$objects = array();
// Loop through markers
foreach ($markers as $marker)
{
if ($pcre)
{
if (preg_match($pcre, (string) $marker->Key))
{
$xx = array('key' => (string) $marker->Key);
if ((string) $marker->VersionId !== 'null')
{
$xx['version_id'] = (string) $marker->VersionId;
}
$objects[] = $xx;
unset($xx);
}
}
else
{
$xx = array('key' => (string) $marker->Key);
if ((string) $marker->VersionId !== 'null')
{
$xx['version_id'] = (string) $marker->VersionId;
}
$objects[] = $xx;
unset($xx);
}
}
$batch = new CFBatchRequest();
$batch->use_credentials($this->credentials);
foreach (array_chunk($objects, 1000) as $object_set)
{
$this->batch($batch)->delete_objects($bucket, array(
'objects' => $object_set
));
}
$responses = $this->batch($batch)->send();
$is_ok = true;
foreach ($responses as $response)
{
if (!$response->isOK() || isset($response->body->Error))
{
$is_ok = false;
}
}
return $is_ok;
}
/**
* Gets the collective metadata for the given Amazon S3 object.
*
* The MD5 value for an object can be retrieved from the ETag HTTP header for any object that was uploaded
* with a normal PUT/POST. This value is incorrect for multipart uploads.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $filename (Required) The file name for the Amazon S3 object.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* versionId - string - Optional - The version of the object to retrieve. Version IDs are returned in the x-amz-version-id header of any previous object-related request.
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return mixed If the object exists, the method returns the collective metadata for the Amazon S3 object. If the object does not exist, the method returns boolean false.
*/
public function get_object_metadata($bucket, $filename, $opt = null)
{
$batch = new CFBatchRequest();
$this->batch($batch)->get_object_acl($bucket, $filename); // Get ACL info
$this->batch($batch)->get_object_headers($bucket, $filename); // Get content-type
$this->batch($batch)->list_objects($bucket, array( // Get other metadata
'max-keys' => 1,
'prefix' => $filename
));
$response = $this->batch($batch)->send();
// Fail if any requests were unsuccessful
if (!$response->areOK())
{
return false;
}
$data = array(
'ACL' => array(),
'ContentType' => null,
'ETag' => null,
'Headers' => null,
'Key' => null,
'LastModified' => null,
'Owner' => array(),
'Size' => null,
'StorageClass' => null,
);
// Add the content type
$data['ContentType'] = (string) $response[1]->header['content-type'];
// Add the other metadata (including storage type)
$contents = json_decode(json_encode($response[2]->body->query('descendant-or-self::Contents')->first()), true);
$data = array_merge($data, (is_array($contents) ? $contents : array()));
// Add ACL info
$grants = $response[0]->body->query('descendant-or-self::Grant');
$max = count($grants);
// Add raw header info
$data['Headers'] = $response[1]->header;
foreach (array('_info', 'x-amz-id-2', 'x-amz-request-id', 'cneonction', 'server', 'content-length', 'content-type', 'etag') as $header)
{
unset($data['Headers'][$header]);
}
ksort($data['Headers']);
if (count($grants) > 0)
{
foreach ($grants as $grant)
{
$dgrant = array(
'id' => (string) $this->util->try_these(array('ID', 'URI'), $grant->Grantee),
'permission' => (string) $grant->Permission
);
$data['ACL'][] = $dgrant;
}
}
return $data;
}
/*%******************************************************************************************%*/
// URLS
/**
* Gets the web-accessible URL for the Amazon S3 object or generates a time-limited signed request for
* a private file.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $filename (Required) The file name for the Amazon S3 object.
* @param integer|string $preauth (Optional) Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with .
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* https - boolean - Optional - Set to true if you would like the URL be in https mode. Otherwise, the default behavior is always to use http regardless of your SSL settings.
* method - string - Optional - The HTTP method to use for the request. Defaults to a value of GET.
* response - array - Optional - Allows adjustments to specific response headers. Pass an associative array where each key is one of the following: cache-control, content-disposition, content-encoding, content-language, content-type, expires. The expires value should use and be formatted with the DATE_RFC2822 constant.
* torrent - boolean - Optional - A value of true will return a URL to a torrent of the Amazon S3 object. A value of false will return a non-torrent URL. Defaults to false.
* versionId - string - Optional - The version of the object. Version IDs are returned in the x-amz-version-id header of any previous object-related request.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return string The file URL, with authentication and/or torrent parameters if requested.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/S3_QSAuth.html Using Query String Authentication
*/
public function get_object_url($bucket, $filename, $preauth = 0, $opt = null)
{
// Add this to our request
if (!$opt) $opt = array();
$opt['verb'] = isset($opt['method']) ? $opt['method'] : 'GET';
$opt['resource'] = $filename;
$opt['preauth'] = $preauth;
if (isset($opt['torrent']) && $opt['torrent'])
{
$opt['sub_resource'] = 'torrent';
unset($opt['torrent']);
}
// GET responses
if (isset($opt['response']))
{
foreach ($opt['response'] as $key => $value)
{
$opt['response-' . $key] = $value;
unset($opt['response'][$key]);
}
}
// Determine whether or not to use SSL
$use_ssl = isset($opt['https']) ? (bool) $opt['https'] : false;
unset($opt['https']);
$current_use_ssl_setting = $this->use_ssl;
// Authenticate to S3
$this->use_ssl = $use_ssl;
$response = $this->authenticate($bucket, $opt);
$this->use_ssl = $current_use_ssl_setting;
return $response;
}
/**
* Gets the web-accessible URL to a torrent of the Amazon S3 object. The Amazon S3 object's access
* control list settings (ACL) MUST be set to for a valid URL to be returned.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $filename (Required) The file name for the object.
* @param integer|string $preauth (Optional) Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with .
* @return string The torrent URL, with authentication parameters if requested.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/index.html?S3TorrentRetrieve.html Using BitTorrent to Retrieve Objects Stored in Amazon S3
*/
public function get_torrent_url($bucket, $filename, $preauth = 0)
{
return $this->get_object_url($bucket, $filename, $preauth, array(
'torrent' => true
));
}
/*%******************************************************************************************%*/
// VERSIONING
/**
* Enables versioning support for the specified Amazon S3 bucket.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* MFASerial - string (Optional) The serial number on the back of the Gemalto device. MFASerial, MFAToken and MFAStatus must all be set for MFA to work.
* MFAToken - string (Optional) The current token displayed on the Gemalto device. MFASerial, MFAToken and MFAStatus must all be set for MFA to work.
* MFAStatus - string (Optional) The MFA Delete status. Can be Enabled or Disabled. MFASerial, MFAToken and MFAStatus must all be set for MFA to work.
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
* @link http://aws.amazon.com/mfa/ Multi-Factor Authentication
*/
public function enable_versioning($bucket, $opt = null)
{
if (!$opt) $opt = array();
// Add this to our request
$opt['verb'] = 'PUT';
$opt['sub_resource'] = 'versioning';
$opt['headers'] = array(
'Content-Type' => 'application/xml'
);
$xml = simplexml_load_string($this->base_versioning_xml);
$xml->addChild('Status', 'Enabled');
// Enable MFA delete?
// @codeCoverageIgnoreStart
if (isset($opt['MFASerial']) && isset($opt['MFAToken']) && isset($opt['MFAStatus']))
{
$xml->addChild('MfaDelete', $opt['MFAStatus']);
$opt['headers']['x-amz-mfa'] = ($opt['MFASerial'] . ' ' . $opt['MFAToken']);
}
// @codeCoverageIgnoreEnd
$opt['body'] = $xml->asXML();
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Disables versioning support for the specified Amazon S3 bucket.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* MFASerial - string - Optional - The serial number on the back of the Gemalto device. MFASerial, MFAToken and MFAStatus must all be set for MFA to work.
* MFAToken - string - Optional - The current token displayed on the Gemalto device. MFASerial, MFAToken and MFAStatus must all be set for MFA to work.
* MFAStatus - string - Optional - The MFA Delete status. Can be Enabled or Disabled. MFASerial, MFAToken and MFAStatus must all be set for MFA to work.
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
* @link http://aws.amazon.com/mfa/ Multi-Factor Authentication
*/
public function disable_versioning($bucket, $opt = null)
{
if (!$opt) $opt = array();
// Add this to our request
$opt['verb'] = 'PUT';
$opt['sub_resource'] = 'versioning';
$opt['headers'] = array(
'Content-Type' => 'application/xml'
);
$xml = simplexml_load_string($this->base_versioning_xml);
$xml->addChild('Status', 'Suspended');
// Enable MFA delete?
// @codeCoverageIgnoreStart
if (isset($opt['MFASerial']) && isset($opt['MFAToken']) && isset($opt['MFAStatus']))
{
$xml->addChild('MfaDelete', $opt['MFAStatus']);
$opt['headers']['x-amz-mfa'] = ($opt['MFASerial'] . ' ' . $opt['MFAToken']);
}
// @codeCoverageIgnoreEnd
$opt['body'] = $xml->asXML();
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Gets an Amazon S3 bucket's versioning status.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* preauth - integer|string - Optional - Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with .
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
*/
public function get_versioning_status($bucket, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'GET';
$opt['sub_resource'] = 'versioning';
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Gets a list of all the versions of Amazon S3 objects in the specified bucket.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* delimiter - string - Optional - Unicode string parameter. Keys that contain the same string between the prefix and the first occurrence of the delimiter will be rolled up into a single result element in the CommonPrefixes collection.
* key-marker - string - Optional - Restricts the response to contain results that only occur alphabetically after the value of the key-marker.
* max-keys - string - Optional - Limits the number of results returned in response to your query. Will return no more than this number of results, but possibly less.
* prefix - string - Optional - Restricts the response to only contain results that begin with the specified prefix.
* version-id-marker - string - Optional - Restricts the response to contain results that only occur alphabetically after the value of the version-id-marker.
* preauth - integer|string - Optional - Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with .
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
*/
public function list_bucket_object_versions($bucket, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'GET';
$opt['sub_resource'] = 'versions';
foreach (array('delimiter', 'key-marker', 'max-keys', 'prefix', 'version-id-marker') as $param)
{
if (isset($opt[$param]))
{
$opt['query_string'][$param] = $opt[$param];
unset($opt[$param]);
}
}
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/*%******************************************************************************************%*/
// BUCKET POLICIES
/**
* Sets the policy sub-resource for the specified Amazon S3 bucket. The specified policy replaces any
* policy the bucket already has.
*
* To perform this operation, the caller must be authorized to set a policy for the bucket and have
* PutPolicy permissions. If the caller does not have PutPolicy permissions for the bucket, Amazon S3
* returns a `403 Access Denied` error. If the caller has the correct permissions but has not been
* authorized by the bucket owner, Amazon S3 returns a `405 Method Not Allowed` error.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param CFPolicy $policy (Required) The JSON policy to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/AccessPolicyLanguage.html Appendix: The Access Policy Language
*/
public function set_bucket_policy($bucket, CFPolicy $policy, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'PUT';
$opt['sub_resource'] = 'policy';
$opt['body'] = $policy->get_json();
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Gets the policy of the specified Amazon S3 bucket.
*
* To use this operation, the caller must have GetPolicy permissions for the specified bucket and must be
* the bucket owner. If the caller does not have GetPolicy permissions, this method will generate a
* `403 Access Denied` error. If the caller has the correct permissions but is not the bucket owner, this
* method will generate a `405 Method Not Allowed` error. If the bucket does not have a policy defined for
* it, this method will generate a `404 Policy Not Found` error.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
*/
public function get_bucket_policy($bucket, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'GET';
$opt['sub_resource'] = 'policy';
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Deletes the bucket policy for the specified Amazon S3 bucket. To delete the policy, the caller must
* be the bucket owner and have `DeletePolicy` permissions for the specified bucket.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response. If you do not have `DeletePolicy` permissions, Amazon S3 returns a `403 Access Denied` error. If you have the correct permissions, but are not the bucket owner, Amazon S3 returns a `405 Method Not Allowed` error. If the bucket doesn't have a policy, Amazon S3 returns a `204 No Content` error.
*/
public function delete_bucket_policy($bucket, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'DELETE';
$opt['sub_resource'] = 'policy';
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/*%******************************************************************************************%*/
// BUCKET NOTIFICATIONS
/**
* Enables notifications of specified events for an Amazon S3 bucket. Currently, the
* `s3:ReducedRedundancyLostObject` event is the only event supported for notifications. The
* `s3:ReducedRedundancyLostObject` event is triggered when Amazon S3 detects that it has lost all
* copies of an Amazon S3 object and can no longer service requests for that object.
*
* If the bucket owner and Amazon SNS topic owner are the same, the bucket owner has permission to
* publish notifications to the topic by default. Otherwise, the owner of the topic must create a
* policy to enable the bucket owner to publish to the topic.
*
* By default, only the bucket owner can configure notifications on a bucket. However, bucket owners
* can use bucket policies to grant permission to other users to set this configuration with the
* `s3:PutBucketNotification` permission.
*
* After a PUT operation is called to configure notifications on a bucket, Amazon S3 publishes a test
* notification to ensure that the topic exists and that the bucket owner has permission to publish
* to the specified topic. If the notification is successfully published to the SNS topic, the PUT
* operation updates the bucket configuration and returns the 200 OK responses with a
* `x-amz-sns-test-message-id` header containing the message ID of the test notification sent to topic.
*
* @param string $bucket (Required) The name of the bucket to create bucket notifications for.
* @param string $topic_arn (Required) The SNS topic ARN to send notifications to.
* @param string $event (Required) The event type to listen for.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/NotificationHowTo.html Setting Up Notification of Bucket Events
*/
public function create_bucket_notification($bucket, $topic_arn, $event, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'PUT';
$opt['sub_resource'] = 'notification';
$opt['headers'] = array(
'Content-Type' => 'application/xml'
);
$xml = simplexml_load_string($this->base_notification_xml);
$topic_config = $xml->addChild('TopicConfiguration');
$topic_config->addChild('Topic', $topic_arn);
$topic_config->addChild('Event', $event);
$opt['body'] = $xml->asXML();
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Gets the notification configuration of a bucket. Currently, the `s3:ReducedRedundancyLostObject` event
* is the only event supported for notifications. The `s3:ReducedRedundancyLostObject` event is triggered
* when Amazon S3 detects that it has lost all replicas of a Reduced Redundancy Storage object and can no
* longer service requests for that object.
*
* If notifications are not enabled on the bucket, the operation returns an empty
* `NotificatonConfiguration` element.
*
* By default, you must be the bucket owner to read the notification configuration of a bucket. However,
* the bucket owner can use a bucket policy to grant permission to other users to read this configuration
* with the `s3:GetBucketNotification` permission.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/NotificationHowTo.html Setting Up Notification of Bucket Events
*/
public function get_bucket_notifications($bucket, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'GET';
$opt['sub_resource'] = 'notification';
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Empties the list of SNS topics to send notifications to.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/NotificationHowTo.html Setting Up Notification of Bucket Events
*/
public function delete_bucket_notification($bucket, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'PUT';
$opt['sub_resource'] = 'notification';
$opt['body'] = $this->base_notification_xml;
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/*%******************************************************************************************%*/
// MULTIPART UPLOAD
/**
* Calculates the correct values for sequentially reading a file for multipart upload. This method should
* be used in conjunction with .
*
* @param integer $filesize (Required) The size in bytes of the entire file.
* @param integer $part_size (Required) The size in bytes of the part of the file to send.
* @return array An array containing key-value pairs. The keys are `seekTo` and `length`.
*/
public function get_multipart_counts($filesize, $part_size)
{
$i = 0;
$sizecount = $filesize;
$values = array();
while ($sizecount > 0)
{
$sizecount -= $part_size;
$values[] = array(
'seekTo' => ($part_size * $i),
'length' => (($sizecount > 0) ? $part_size : ($sizecount + $part_size)),
);
$i++;
}
return $values;
}
/**
* Initiates a multipart upload and returns an `UploadId`.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $filename (Required) The file name for the object.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* acl - string - Optional - The ACL settings for the specified object. Accepts any of the following constants: [Allowed values: AmazonS3::ACL_PRIVATE, AmazonS3::ACL_PUBLIC, AmazonS3::ACL_OPEN, AmazonS3::ACL_AUTH_READ, AmazonS3::ACL_OWNER_READ, AmazonS3::ACL_OWNER_FULL_CONTROL]. Alternatively, an array of associative arrays. Each associative array contains an id and a permission key. The default value is ACL_PRIVATE.
* contentType - string - Optional - The type of content that is being sent. The default value is application/octet-stream.
* encryption - string - Optional - The algorithm to use for encrypting the object. [Allowed values: AES256]
* headers - array - Optional - Standard HTTP headers to send along in the request. Accepts an associative array of key-value pairs.
* meta - array - Optional - An associative array of key-value pairs. Any header starting with x-amz-meta-: is considered user metadata. It will be stored with the object and returned when you retrieve the object. The total size of the HTTP request, not including the body, must be less than 4 KB.
* storage - string - Optional - Whether to use Standard or Reduced Redundancy storage. [Allowed values: AmazonS3::STORAGE_STANDARD, AmazonS3::STORAGE_REDUCED]. The default value is STORAGE_STANDARD.
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAccessPolicy.html REST Access Control Policy
*/
public function initiate_multipart_upload($bucket, $filename, $opt = null)
{
if (!$opt) $opt = array();
// Add this to our request
$opt['verb'] = 'POST';
$opt['resource'] = $filename;
$opt['sub_resource'] = 'uploads';
$opt['body'] = '';
// Handle content type. Can also be passed as an HTTP header.
if (isset($opt['contentType']))
{
$opt['headers']['Content-Type'] = $opt['contentType'];
unset($opt['contentType']);
}
// Set a default content type.
if (!isset($opt['headers']['Content-Type']))
{
$opt['headers']['Content-Type'] = 'application/octet-stream';
}
// Handle Access Control Lists. Can also be passed as an HTTP header.
if (isset($opt['acl']))
{
if (is_array($opt['acl']))
{
$opt['headers'] = array_merge($opt['headers'], $this->generate_access_policy_headers($opt['acl']));
}
else
{
$opt['headers']['x-amz-acl'] = $opt['acl'];
}
}
// Handle storage settings. Can also be passed as an HTTP header.
if (isset($opt['storage']))
{
$opt['headers']['x-amz-storage-class'] = $opt['storage'];
unset($opt['storage']);
}
// Handle encryption settings. Can also be passed as an HTTP header.
if (isset($opt['encryption']))
{
$opt['headers']['x-amz-server-side-encryption'] = $opt['encryption'];
unset($opt['encryption']);
}
// Handle meta tags. Can also be passed as an HTTP header.
if (isset($opt['meta']))
{
foreach ($opt['meta'] as $meta_key => $meta_value)
{
// e.g., `My Meta Header` is converted to `x-amz-meta-my-meta-header`.
$opt['headers']['x-amz-meta-' . strtolower(str_replace(' ', '-', $meta_key))] = $meta_value;
}
unset($opt['meta']);
}
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Uploads a single part of a multipart upload. The part size cannot be smaller than 5 MB
* or larger than 5 TB. A multipart upload can have no more than 10,000 parts.
*
* Amazon S3 charges for storage as well as requests to the service. Smaller part sizes (and more
* requests) allow for faster failures and better upload reliability. Larger part sizes (and fewer
* requests) costs slightly less but has lower upload reliability.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $filename (Required) The file name for the object.
* @param string $upload_id (Required) The upload ID identifying the multipart upload whose parts are being listed. The upload ID is retrieved from a call to .
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* fileUpload - string|resource - Required - The URL/path for the file to upload or an open resource.
* partNumber - integer - Required - The part number order of the multipart upload.
* expect - string - Optional - Specifies that the SDK not send the request body until it receives an acknowledgement. If the message is rejected based on the headers, the body of the message is not sent. For more information, see RFC 2616, section 14.20. The value can also be passed to the header option as Expect. [Allowed values: 100-continue]
* headers - array - Optional - Standard HTTP headers to send along in the request. Accepts an associative array of key-value pairs.
* length - integer - Optional - The size of the part in bytes. For more information, see RFC 2616, section 14.13. The value can also be passed to the header option as Content-Length.
* md5 - string - Optional - The base64 encoded 128-bit MD5 digest of the part data. This header can be used as a message integrity check to verify that the part data is the same data that was originally sent. Although it is optional, we recommend using this mechanism as an end-to-end integrity check. For more information, see RFC 1864. The value can also be passed to the header option as Content-MD5.
* seekTo - integer - Optional - The starting position in bytes for the piece of the file/stream to upload.
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
*/
public function upload_part($bucket, $filename, $upload_id, $opt = null)
{
if (!$opt) $opt = array();
// Add this to our request
$opt['verb'] = 'PUT';
$opt['resource'] = $filename;
$opt['uploadId'] = $upload_id;
if (!isset($opt['fileUpload']) || !isset($opt['partNumber']))
{
throw new S3_Exception('The `fileUpload` and `partNumber` options are both required in ' . __FUNCTION__ . '().');
}
// Handle expectation. Can also be passed as an HTTP header.
if (isset($opt['expect']))
{
$opt['headers']['Expect'] = $opt['expect'];
unset($opt['expect']);
}
// Handle content length. Can also be passed as an HTTP header.
if (isset($opt['length']))
{
$opt['headers']['Content-Length'] = $opt['length'];
unset($opt['length']);
}
// Handle content md5. Can also be passed as an HTTP header.
if (isset($opt['md5']))
{
$opt['headers']['Content-MD5'] = $opt['md5'];
unset($opt['md5']);
}
$opt['headers']['Expect'] = '100-continue';
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Lists the completed parts of an in-progress multipart upload.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $filename (Required) The file name for the object.
* @param string $upload_id (Required) The upload ID identifying the multipart upload whose parts are being listed. The upload ID is retrieved from a call to .
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* max-parts - integer - Optional - The maximum number of parts to return in the response body.
* part-number-marker - string - Optional - Restricts the response to contain results that only occur numerically after the value of the part-number-marker.
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
*/
public function list_parts($bucket, $filename, $upload_id, $opt = null)
{
if (!$opt) $opt = array();
// Add this to our request
$opt['verb'] = 'GET';
$opt['resource'] = $filename;
$opt['uploadId'] = $upload_id;
$opt['query_string'] = array();
foreach (array('max-parts', 'part-number-marker') as $param)
{
if (isset($opt[$param]))
{
$opt['query_string'][$param] = $opt[$param];
unset($opt[$param]);
}
}
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Aborts an in-progress multipart upload. This operation cannot be reversed.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $filename (Required) The file name for the object.
* @param string $upload_id (Required) The upload ID identifying the multipart upload whose parts are being listed. The upload ID is retrieved from a call to .
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
*/
public function abort_multipart_upload($bucket, $filename, $upload_id, $opt = null)
{
if (!$opt) $opt = array();
// Add this to our request
$opt['verb'] = 'DELETE';
$opt['resource'] = $filename;
$opt['uploadId'] = $upload_id;
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Completes an in-progress multipart upload. A multipart upload is completed by describing the part
* numbers and corresponding ETag values in order, and submitting that data to Amazon S3 as an XML document.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $filename (Required) The file name for the object.
* @param string $upload_id (Required) The upload ID identifying the multipart upload whose parts are being listed. The upload ID is retrieved from a call to .
* @param string|array|SimpleXMLElement|CFResponse $parts (Required) The completion XML document. This document can be provided in multiple ways; as a string of XML, as a object representing the XML document, as an indexed array of associative arrays where the keys are PartNumber and ETag, or as a object returned by .
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
*/
public function complete_multipart_upload($bucket, $filename, $upload_id, $parts, $opt = null)
{
if (!$opt) $opt = array();
// Add this to our request
$opt['verb'] = 'POST';
$opt['resource'] = $filename;
$opt['uploadId'] = $upload_id;
$opt['headers'] = array(
'Content-Type' => 'application/xml'
);
// Disable Content-MD5 calculation for this operation
$opt['NoContentMD5'] = true;
if (is_string($parts))
{
// Assume it's the intended XML.
$opt['body'] = $parts;
}
elseif ($parts instanceof SimpleXMLElement)
{
// Assume it's a SimpleXMLElement object representing the XML.
$opt['body'] = $parts->asXML();
}
elseif (is_array($parts) || $parts instanceof CFResponse)
{
$xml = simplexml_load_string($this->complete_mpu_xml);
if (is_array($parts))
{
// Generate the appropriate XML.
foreach ($parts as $node)
{
$part = $xml->addChild('Part');
$part->addChild('PartNumber', $node['PartNumber']);
$part->addChild('ETag', $node['ETag']);
}
}
elseif ($parts instanceof CFResponse)
{
// Assume it's a response from list_parts().
foreach ($parts->body->Part as $node)
{
$part = $xml->addChild('Part');
$part->addChild('PartNumber', (string) $node->PartNumber);
$part->addChild('ETag', (string) $node->ETag);
}
}
$opt['body'] = $xml->asXML();
}
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Lists the in-progress multipart uploads.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* delimiter - string - Optional - Keys that contain the same string between the prefix and the first occurrence of the delimiter will be rolled up into a single result element in the CommonPrefixes collection.
* key-marker - string - Optional - Restricts the response to contain results that only occur alphabetically after the value of the key-marker. If used in conjunction with upload-id-marker, the results will be filtered to include keys whose upload ID is alphabetically after the value of upload-id-marker.
* upload-id-marker - string - Optional - Restricts the response to contain results that only occur alphabetically after the value of the upload-id-marker. Must be used in conjunction with key-marker.
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
*/
public function list_multipart_uploads($bucket, $opt = null)
{
if (!$opt) $opt = array();
// Add this to our request
$opt['verb'] = 'GET';
$opt['sub_resource'] = 'uploads';
foreach (array('key-marker', 'max-uploads', 'upload-id-marker') as $param)
{
if (isset($opt[$param]))
{
$opt['query_string'][$param] = $opt[$param];
unset($opt[$param]);
}
}
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Since Amazon S3's standard operation only supports copying objects that are smaller than
* 5 GB, the ability to copy large objects (greater than 5 GB) requires the use of "Multipart Copy".
*
* Copying large objects requires the developer to initiate a new multipart "upload", copy pieces of the
* large object (specifying a range of bytes up to 5 GB from the large source file), then complete the
* multipart "upload".
*
* NOTE: This is a synchronous operation, not an asynchronous operation, which means
* that Amazon S3 will not return a response for this operation until the copy has completed across the Amazon
* S3 server fleet. Copying objects within a single region will complete more quickly than copying objects
* across regions. The synchronous nature of this operation is different from other services where
* responses are typically returned immediately, even if the operation itself has not yet been completed on
* the server-side.
*
* @param array $source (Required) The bucket and file name to copy from. The following keys must be set:
* bucket - string - Required - Specifies the name of the bucket containing the source object.
* filename - string - Required - Specifies the file name of the source object to copy.
* @param array $dest (Required) The bucket and file name to copy to. The following keys must be set:
* bucket - string - Required - Specifies the name of the bucket to copy the object to.
* filename - string - Required - Specifies the file name to copy the object to.
* @param string $upload_id (Required) The upload ID identifying the multipart upload whose parts are being listed. The upload ID is retrieved from a call to .
* @param integer $part_number (Required) A part number uniquely identifies a part and defines its position within the destination object. When you complete a multipart upload, a complete object is created by concatenating parts in ascending order based on part number. If you copy a new part using the same part number as a previously copied/uploaded part, the previously written part is overwritten.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* ifMatch - string - Optional - The ETag header from a previous request. Copies the object if its entity tag (ETag) matches the specified tag; otherwise, the request returns a 412 HTTP status code error (precondition failed). Used in conjunction with ifUnmodifiedSince.
* ifUnmodifiedSince - string - Optional - The LastModified header from a previous request. Copies the object if it hasn't been modified since the specified time; otherwise, the request returns a 412 HTTP status code error (precondition failed). Used in conjunction with ifMatch.
* ifNoneMatch - string - Optional - The ETag header from a previous request. Copies the object if its entity tag (ETag) is different than the specified ETag; otherwise, the request returns a 412 HTTP status code error (failed condition). Used in conjunction with ifModifiedSince.
* ifModifiedSince - string - Optional - The LastModified header from a previous request. Copies the object if it has been modified since the specified time; otherwise, the request returns a 412 HTTP status code error (failed condition). Used in conjunction with ifNoneMatch.
* range - string - Optional - The range of bytes to copy from the object. Specify this parameter when copying partial bits. The specified range must be notated with a hyphen (e.g., 0-10485759). Defaults to the byte range of the complete Amazon S3 object.
* versionId - string - Optional - The version of the object to copy. Version IDs are returned in the x-amz-version-id header of any previous object-related request.
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
*/
public function copy_part($source, $dest, $upload_id, $part_number, $opt = null)
{
if (!$opt) $opt = array();
// Add this to our request
$opt['verb'] = 'PUT';
$opt['resource'] = $dest['filename'];
$opt['uploadId'] = $upload_id;
$opt['partNumber'] = $part_number;
// Handle copy source
if (isset($source['bucket']) && isset($source['filename']))
{
$opt['headers']['x-amz-copy-source'] = '/' . $source['bucket'] . '/' . rawurlencode($source['filename'])
. (isset($opt['versionId']) ? ('?' . 'versionId=' . rawurlencode($opt['versionId'])) : ''); // Append the versionId to copy, if available
unset($opt['versionId']);
}
// Handle conditional-copy parameters
if (isset($opt['ifMatch']))
{
$opt['headers']['x-amz-copy-source-if-match'] = $opt['ifMatch'];
unset($opt['ifMatch']);
}
if (isset($opt['ifNoneMatch']))
{
$opt['headers']['x-amz-copy-source-if-none-match'] = $opt['ifNoneMatch'];
unset($opt['ifNoneMatch']);
}
if (isset($opt['ifUnmodifiedSince']))
{
$opt['headers']['x-amz-copy-source-if-unmodified-since'] = $opt['ifUnmodifiedSince'];
unset($opt['ifUnmodifiedSince']);
}
if (isset($opt['ifModifiedSince']))
{
$opt['headers']['x-amz-copy-source-if-modified-since'] = $opt['ifModifiedSince'];
unset($opt['ifModifiedSince']);
}
// Partial content range
if (isset($opt['range']))
{
$opt['headers']['x-amz-copy-source-range'] = 'bytes=' . $opt['range'];
}
// Authenticate to S3
return $this->authenticate($dest['bucket'], $opt);
}
/**
* Creates an Amazon S3 object using the multipart upload APIs. It is analogous to .
*
* While each individual part of a multipart upload can hold up to 5 GB of data, this method limits the
* part size to a maximum of 500 MB. The combined size of all parts can not exceed 5 TB of data. When an
* object is stored in Amazon S3, the data is streamed to multiple storage servers in multiple data
* centers. This ensures the data remains available in the event of internal network or hardware failure.
*
* Amazon S3 charges for storage as well as requests to the service. Smaller part sizes (and more
* requests) allow for faster failures and better upload reliability. Larger part sizes (and fewer
* requests) costs slightly less but has lower upload reliability.
*
* In certain cases with large objects, it's possible for this method to attempt to open more file system
* connections than allowed by the OS. In this case, either
* increase the number of connections
* allowed or increase the value of the partSize parameter to use a larger part size.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $filename (Required) The file name for the object.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* fileUpload - string|resource - Required - The URL/path for the file to upload, or an open resource.
* acl - string - Optional - The ACL settings for the specified object. [Allowed values: AmazonS3::ACL_PRIVATE, AmazonS3::ACL_PUBLIC, AmazonS3::ACL_OPEN, AmazonS3::ACL_AUTH_READ, AmazonS3::ACL_OWNER_READ, AmazonS3::ACL_OWNER_FULL_CONTROL]. The default value is ACL_PRIVATE.
* contentType - string - Optional - The type of content that is being sent in the body. The default value is application/octet-stream.
* headers - array - Optional - Standard HTTP headers to send along in the request. Accepts an associative array of key-value pairs.
* length - integer - Optional - The size of the object in bytes. For more information, see RFC 2616, section 14.13. The value can also be passed to the header option as Content-Length.
* limit - integer - Optional - The maximum number of concurrent uploads done by cURL. Gets passed to CFBatchRequest.
* meta - array - Optional - An associative array of key-value pairs. Any header starting with x-amz-meta-: is considered user metadata. It will be stored with the object and returned when you retrieve the object. The total size of the HTTP request, not including the body, must be less than 4 KB.
* partSize - integer - Optional - The size of an individual part. The size may not be smaller than 5 MB or larger than 500 MB. The default value is 50 MB.
* redirectTo - string - Optional - The URI to send an HTTP 301 redirect to when accessing this object. Value must be prefixed either /, http:// or https://.
* seekTo - integer - Optional - The starting position in bytes for the first piece of the file/stream to upload.
* storage - string - Optional - Whether to use Standard or Reduced Redundancy storage. [Allowed values: AmazonS3::STORAGE_STANDARD, AmazonS3::STORAGE_REDUCED]. The default value is STORAGE_STANDARD.
* uploadId - string - Optional - An upload ID identifying an existing multipart upload to use. If this option is not set, one will be created automatically.
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
* @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAccessPolicy.html REST Access Control Policy
*/
public function create_mpu_object($bucket, $filename, $opt = null)
{
if ($this->use_batch_flow)
{
throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested');
}
if (!$opt) $opt = array();
// Handle content length. Can also be passed as an HTTP header.
if (isset($opt['length']))
{
$opt['headers']['Content-Length'] = $opt['length'];
unset($opt['length']);
}
// URI to redirect to. Can also be passed as an HTTP header.
if (isset($opt['redirectTo']))
{
$opt['headers']['x-amz-website-redirect-location'] = $opt['redirectTo'];
unset($opt['redirectTo']);
}
if (!isset($opt['fileUpload']))
{
throw new S3_Exception('The `fileUpload` option is required in ' . __FUNCTION__ . '().');
}
elseif (is_resource($opt['fileUpload']))
{
$opt['limit'] = 1; // We can only read from this one resource.
$upload_position = isset($opt['seekTo']) ? (integer) $opt['seekTo'] : ftell($opt['fileUpload']);
$upload_filesize = isset($opt['headers']['Content-Length']) ? (integer) $opt['headers']['Content-Length'] : null;
if (!isset($upload_filesize) && $upload_position !== false)
{
$stats = fstat($opt['fileUpload']);
if ($stats && $stats['size'] >= 0)
{
$upload_filesize = $stats['size'] - $upload_position;
}
}
}
else
{
$upload_position = isset($opt['seekTo']) ? (integer) $opt['seekTo'] : 0;
if (isset($opt['headers']['Content-Length']))
{
$upload_filesize = (integer) $opt['headers']['Content-Length'];
}
else
{
$upload_filesize = filesize($opt['fileUpload']);
if ($upload_filesize !== false)
{
$upload_filesize -= $upload_position;
}
}
}
if ($upload_position === false || !isset($upload_filesize) || $upload_filesize === false || $upload_filesize < 0)
{
throw new S3_Exception('The size of `fileUpload` cannot be determined in ' . __FUNCTION__ . '().');
}
// Handle part size
if (isset($opt['partSize']))
{
// If less that 5 MB...
if ((integer) $opt['partSize'] < 5242880)
{
$opt['partSize'] = 5242880; // 5 MB
}
// If more than 500 MB...
elseif ((integer) $opt['partSize'] > 524288000)
{
$opt['partSize'] = 524288000; // 500 MB
}
}
else
{
$opt['partSize'] = 52428800; // 50 MB
}
// If the upload size is smaller than the piece size, failover to create_object().
if ($upload_filesize < $opt['partSize'] && !isset($opt['uploadId']))
{
return $this->create_object($bucket, $filename, $opt);
}
// Initiate multipart upload
if (isset($opt['uploadId']))
{
$upload_id = $opt['uploadId'];
}
else
{
// Compose options for initiate_multipart_upload().
$_opt = array();
foreach (array('contentType', 'acl', 'storage', 'headers', 'meta') as $param)
{
if (isset($opt[$param]))
{
$_opt[$param] = $opt[$param];
}
}
$upload = $this->initiate_multipart_upload($bucket, $filename, $_opt);
if (!$upload->isOK())
{
return $upload;
}
// Fetch the UploadId
$upload_id = (string) $upload->body->UploadId;
}
// Get the list of pieces
$pieces = $this->get_multipart_counts($upload_filesize, (integer) $opt['partSize']);
// Queue batch requests
$batch = new CFBatchRequest(isset($opt['limit']) ? (integer) $opt['limit'] : null);
foreach ($pieces as $i => $piece)
{
$this->batch($batch)->upload_part($bucket, $filename, $upload_id, array(
'expect' => '100-continue',
'fileUpload' => $opt['fileUpload'],
'partNumber' => ($i + 1),
'seekTo' => $upload_position + (integer) $piece['seekTo'],
'length' => (integer) $piece['length'],
));
}
// Send batch requests
$batch_responses = $this->batch($batch)->send();
if (!$batch_responses->areOK())
{
return $batch_responses;
}
// Compose completion XML
$parts = array();
foreach ($batch_responses as $i => $response)
{
$parts[] = array('PartNumber' => ($i + 1), 'ETag' => $response->header['etag']);
}
return $this->complete_multipart_upload($bucket, $filename, $upload_id, $parts);
}
/**
* Aborts all multipart uploads initiated before the specified date. This operation cannot be reversed.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string|integer $when (Optional) The time and date to use for comparison. Accepts any value that understands.
* @return CFArray A containing a series of 0 or more objects, containing a parsed HTTP response.
*/
public function abort_multipart_uploads_by_date($bucket, $when = null)
{
if ($this->use_batch_flow)
{
// @codeCoverageIgnoreStart
throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested');
// @codeCoverageIgnoreEnd
}
$when = $when ? $when : time();
$handles = array();
$data = $this->list_multipart_uploads($bucket)->body;
$when = is_int($when) ? $when : strtotime($when);
if (!($data instanceof CFSimpleXML))
{
return false;
}
$list = $data->query('descendant-or-self::Upload/Initiated');
if (count($list) > 0)
{
foreach ($list as $node)
{
if (strtotime((string) $node) < $when)
{
$q = new CFBatchRequest();
$parent = $node->parent();
$upload_id = $parent
->query('descendant-or-self::UploadId')
->first()
->to_string();
$filename = $parent
->query('descendant-or-self::Key')
->first()
->to_string();
$handles[] = $this->abort_multipart_upload($bucket, $filename, $upload_id, array(
'returnCurlHandle' => true
));
}
}
$http = new CFRequest();
$responses = $http->send_multi_request($handles);
if (is_array($responses) && count($responses) > 0)
{
return new CFArray($responses);
}
}
return new CFArray();
}
/*%******************************************************************************************%*/
// WEBSITE CONFIGURATION
/**
* Enables and configures an Amazon S3 website using the corresponding bucket as the content source.
* The website will have one default domain name associated with it, which is the bucket name. If you
* attempt to configure an Amazon S3 website for a bucket whose name is not compatible with DNS,
* Amazon S3 returns an InvalidBucketName error. For more information on bucket names and DNS,
* refer to Bucket Restrictions and Limitations.
*
* To visit the bucket as a website a new endpoint is created in the following pattern:
* http://<bucketName>.s3-website-<region>.amazonaws.com. This is a sample URL
* for a bucket called example-bucket in the us-east-1 region.
* (e.g., http://example-bucket.s3-website-us-east-1.amazonaws.com)
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* indexDocument - string - Optional - The file path to use as the root document. The default value is index.html.
* errorDocument - string - Optional - The file path to use as the error document. The default value is error.html.
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
*/
public function create_website_config($bucket, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'PUT';
$opt['sub_resource'] = 'website';
$xml = simplexml_load_string($this->website_config_xml);
if (isset($opt['indexDocument']))
{
$xml->IndexDocument->Suffix = $opt['indexDocument'];
}
if (isset($opt['errorDocument']))
{
$xml->ErrorDocument->Key = $opt['errorDocument'];
}
$opt['body'] = $xml->asXML();
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Retrieves the website configuration for a bucket. The contents of this response are identical to the
* content submitted by the user during the website creation operation. If a website configuration has
* never been set, Amazon S3 will return a 404 error.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
*/
public function get_website_config($bucket, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'GET';
$opt['sub_resource'] = 'website';
$opt['headers'] = array(
'Content-Type' => 'application/xml'
);
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Removes the website configuration for a bucket.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
*/
public function delete_website_config($bucket, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'DELETE';
$opt['sub_resource'] = 'website';
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/*%******************************************************************************************%*/
// OBJECT LIFECYCLE
/**
* Enables the ability to specify a configuration that relates to the object's _lifecycle_.
*
* **NOTE:** In cases where the lifecycle configuration dictates that an object should be deleted, Amazon S3
* guarantees that the object will be deleted when the expiration time is passed.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* rules - string - Required - The object expiration rule-sets to apply to the bucket.
* x - array - Required - This represents a simple array index.
* id - string - Optional - Unique identifier for the rule. The value cannot be longer than 255 characters.
* prefix - string - Required - The Amazon S3 object prefix which targets the file(s) for expiration.
* expiration - array - Optional - The container for the unit of measurement by which the expiration time is calculated. At least one action (either transition or expiration) is required within one lifecycle rule.
* date - string - Conditionally Required - The timestamp for when the targetted objects are to be moved or expired from the bucket. Should be in ISO 8601 Format. HH:MM:SS will be enforced as midnight GMT/UTC.
* days - integer - Conditionally Required - The number of days until the targetted objects are to be moved or expired from the bucket. Must be a positive integer.
*
* transition - array - Optional - The container for the element that describes a transition action. At least one action (either transition or expiration) is required within one lifecycle rule.
* date - string - Conditionally Required - The timestamp for when the targetted objects are to be moved or expired from the bucket. Should be in ISO 8601 Format. HH:MM:SS will be enforced as midnight GMT/UTC.
* days - integer - Conditionally Required - The number of days until the targetted objects are to be moved or expired from the bucket. Must be a positive integer.
* storage - string - Required - The storage setting of an object. [Allowed values: AmazonS3::STORAGE_STANDARD, AmazonS3::STORAGE_REDUCED, STORAGE_GLACIER]. The default value is STORAGE_STANDARD.
*
* enabled - boolean - Optional - Whether or not to enable this rule-set. A value of true enables the rule-set. A value of false disables the rule-set. The default value is true.
*
*
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
*/
public function create_lifecycle_config($bucket, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'PUT';
$opt['sub_resource'] = 'lifecycle';
$opt['headers'] = array(
'Content-Type' => 'application/xml'
);
$xml = simplexml_load_string($this->object_expiration_xml, $this->parser_class);
if (isset($opt['rules']) && is_array($opt['rules']) && count($opt['rules']))
{
foreach ($opt['rules'] as $rule)
{
$xrule = $xml->addChild('Rule');
// ID
if (isset($rule['id']))
{
if (strlen($rule['id']) > 255)
{
throw new S3_Exception('The "id" for a rule must not be more than 255 characters in the ' . __FUNCTION__ . ' method.');
}
$xrule->addChild('ID', $rule['id']);
}
// Prefix
if (isset($rule['prefix']))
{
$xrule->addChild('Prefix', $rule['prefix']);
}
else
{
throw new S3_Exception('Each rule requires a "prefix" in the ' . __FUNCTION__ . ' method.');
}
// Status
$enabled = 'Enabled';
if (isset($rule['enabled']))
{
if (is_bool($rule['enabled'])) // Boolean
{
$enabled = $rule['enabled'] ? 'Enabled' : 'Disabled';
}
elseif (is_string($rule['enabled'])) // String
{
$enabled = (strtolower($rule['enabled']) === 'true') ? 'Enabled' : 'Disabled';
}
$xrule->addChild('Status', $enabled);
}
else
{
$xrule->addChild('Status', 'Enabled');
}
// Expiration
if (isset($rule['expiration']))
{
$xexpiration = $xrule->addChild('Expiration');
if (isset($rule['expiration']['date']))
{
$xexpiration->addChild('Date', $rule['expiration']['date']);
}
elseif (isset($rule['expiration']['days']))
{
$xexpiration->addChild('Days', $rule['expiration']['days']);
}
}
// Transition
if (isset($rule['transition']))
{
$xtransition = $xrule->addChild('Transition');
if (isset($rule['transition']['date']))
{
$xtransition->addChild('Date', $rule['transition']['date']);
}
elseif (isset($rule['transition']['days']))
{
$xtransition->addChild('Days', $rule['transition']['days']);
}
if (isset($rule['transition']['storage']))
{
$xtransition->addChild('StorageClass', $rule['transition']['storage']);
}
}
if (!isset($rule['expiration']) && !isset($rule['transition']))
{
throw new S3_Exception('Each rule requires a either a "transition" or "expiration" entry in the ' . __FUNCTION__ . ' method.');
}
}
}
$opt['body'] = $xml->asXML();
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Retrieves the configuration that relates to the object's _lifecycle_.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
*/
public function get_lifecycle_config($bucket, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'GET';
$opt['sub_resource'] = 'lifecycle';
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Deletes the configuration that relates to the object's _lifecycle_.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
*/
public function delete_lifecycle_config($bucket, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'DELETE';
$opt['sub_resource'] = 'lifecycle';
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Restore an object archived in Amazon Glacier back to Amazon S3.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param string $filename (Required) The file name for the object.
* @param integer $days (Required) The number of days until the targeted objects are to be moved or expired from the bucket. Must be a positive integer.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
*/
public function restore_archived_object($bucket, $filename, $days, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'POST';
$opt['resource'] = $filename;
$opt['sub_resource'] = 'restore';
$opt['headers'] = array(
'Content-Type' => 'application/xml'
);
$xml = simplexml_load_string($this->restore_request_xml, $this->parser_class);
$xml->addChild('Days', (integer) $days);
$opt['body'] = $xml->asXML();
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Alias of {@see AmazonS3::create_lifecycle_config()}.
*
* @deprecated
*/
public function create_object_expiration_config($bucket, $opt = null)
{
return $this->create_lifecycle_config($bucket, $opt);
}
/**
* Alias of {@see AmazonS3::get_lifecycle_config()}.
*
* @deprecated
*/
public function get_object_expiration_config($bucket, $opt = null)
{
return $this->get_lifecycle_config($bucket, $opt);
}
/**
* Alias of {@see AmazonS3::delete_lifecycle_config()}.
*
* @deprecated
*/
public function delete_object_expiration_config($bucket, $opt = null)
{
return $this->delete_lifecycle_config($bucket, $opt);
}
/*%******************************************************************************************%*/
// BUCKET TAGS
/**
* Apply a set of tags to the specified bucket. Bucket Tags simplify the task of associating Amazon S3
* costs with specific buckets.
*
* This operation requires permission to perform s3:PutBucketTagging actions. By default,
* the bucket owner is permitted to perform these actions, and can grant permission to other users.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* tags - array - Required - An associative array of custom key-value pairs.
* [custom-key] - string - Optional - A custom key-value pair to tag the bucket with.
*
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
*/
public function create_bucket_tags($bucket, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'PUT';
$opt['sub_resource'] = 'tagging';
$xml = simplexml_load_string($this->bucket_tagging_xml);
if (isset($opt['tags']) && is_array($opt['tags']))
{
foreach ($opt['tags'] as $key => $value)
{
$xtag = $xml->TagSet->addChild('Tag');
$xtag->addChild('Key', $key);
$xtag->addChild('Value', $value);
}
}
$opt['body'] = $xml->asXML();
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Retrieve all associated tags for the specified bucket.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
*/
public function get_bucket_tags($bucket, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'GET';
$opt['sub_resource'] = 'tagging';
$opt['headers'] = array(
'Content-Type' => 'application/xml'
);
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Delete all associated tags from the specified bucket.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
*/
public function delete_bucket_tags($bucket, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'DELETE';
$opt['sub_resource'] = 'tagging';
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/*%******************************************************************************************%*/
// CROSS-ORIGIN RESOURCE SHARING (CORS)
/**
* Create a new CORS configuration.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* cors_rule - array - Required - One or more rule-sets.
* x - array - Required - This represents a simple array index.
* allowed_header - array - Required - Used in response to a preflight request to indicate which HTTP headers can be used when making the actual request.
* allowed_method - array - Required - An array of HTTP methods to allow. There must be at least one method set. [Allowed values: `GET`, `PUT`, `HEAD`, `POST`, `DELETE`]
* allowed_origin - array - Required - An array of hostnames to allow. This could be `*` to indicate it is open to all domains. If one of them contains the string `*`, then there can be exactly one.
* expose_header - string - Optional - Enable the browser to read this header.
* id - string - Optional - Unique identifier for the rule. The value cannot be longer than 255 characters.
* max_age - integer - Optional - Alter the client's caching behavior for the pre-flight request.
*
*
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
*/
public function create_cors_config($bucket, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'PUT';
$opt['sub_resource'] = 'cors';
$opt['headers'] = array(
'Content-Type' => 'application/xml'
);
$xml = simplexml_load_string($this->cors_config_xml, $this->parser_class);
if (isset($opt['cors_rule']) && is_array($opt['cors_rule']))
{
foreach ($opt['cors_rule'] as $rule_set)
{
// New rule node
$xrule = $xml->addChild('CORSRule');
// ID node
if (isset($rule_set['id']))
{
$xrule->addChild('ID', $rule_set['id']);
}
// ExposeHeader node
if (isset($rule_set['expose_header']))
{
$xrule->addChild('ExposeHeader', $rule_set['expose_header']);
}
// MaxAgeSeconds node
if (isset($rule_set['max_age']))
{
$xrule->addChild('MaxAgeSeconds', $rule_set['max_age']);
}
// AllowedHeader node
if (isset($rule_set['allowed_header']))
{
if (!is_array($rule_set['allowed_header']))
{
$rule_set['allowed_header'] = array($rule_set['allowed_header']);
}
foreach ($rule_set['allowed_header'] as $method)
{
$xrule->addChild('AllowedHeader', $method);
}
}
// AllowedMethod node
if (isset($rule_set['allowed_method']))
{
if (!is_array($rule_set['allowed_method']))
{
$rule_set['allowed_method'] = array($rule_set['allowed_method']);
}
foreach ($rule_set['allowed_method'] as $method)
{
$xrule->addChild('AllowedMethod', $method);
}
}
// AllowedOrigin node
if (isset($rule_set['allowed_origin']))
{
if (!is_array($rule_set['allowed_origin']))
{
$rule_set['allowed_origin'] = array($rule_set['allowed_origin']);
}
foreach ($rule_set['allowed_origin'] as $method)
{
$xrule->addChild('AllowedOrigin', $method);
}
}
}
}
$opt['body'] = $xml->asXML();
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Retrieves the CORS configuration.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
*/
public function get_cors_config($bucket, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'GET';
$opt['sub_resource'] = 'cors';
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/**
* Deletes the CORS configuration.
*
* @param string $bucket (Required) The name of the bucket to use.
* @param array $opt (Optional) An associative array of parameters that can have the following keys:
* curlopts - array - Optional - A set of values to pass directly into curl_setopt(), where the key is a pre-defined CURLOPT_* constant.
* returnCurlHandle - boolean - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request. This toggle is useful for manually managed batch requests.
* @return CFResponse A object containing a parsed HTTP response.
*/
public function delete_cors_config($bucket, $opt = null)
{
if (!$opt) $opt = array();
$opt['verb'] = 'DELETE';
$opt['sub_resource'] = 'cors';
// Authenticate to S3
return $this->authenticate($bucket, $opt);
}
/*%******************************************************************************************%*/
// MISCELLANEOUS
/**
* Gets the canonical user ID and display name from the Amazon S3 server.
*
* @return array An associative array containing the `id` and `display_name` values.
*/
public function get_canonical_user_id()
{
if ($this->use_batch_flow)
{
throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested');
}
$id = $this->list_buckets();
return array(
'id' => (string) $id->body->Owner->ID,
'display_name' => (string) $id->body->Owner->DisplayName
);
}
/**
* Loads and registers the S3StreamWrapper class as a stream wrapper.
*
* @param string $protocol (Optional) The name of the protocol to register.
* @return boolean Whether or not the registration succeeded.
*/
public function register_stream_wrapper($protocol = 's3')
{
require_once dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'extensions'
. DIRECTORY_SEPARATOR . 's3streamwrapper.class.php';
return S3StreamWrapper::register($this, $protocol);
}
}
================================================
FILE: utilities/array.class.php
================================================
object extends PHP's built-in object by providing convenience methods for
* rapidly manipulating array data. Specifically, the `CFArray` object is intended for working with
* and objects that are returned by AWS services.
*
* @version 2012.01.17
* @license See the included NOTICE.md file for more information.
* @copyright See the included NOTICE.md file for more information.
* @link http://aws.amazon.com/php/ PHP Developer Center
* @link http://php.net/ArrayObject ArrayObject
*/
class CFArray extends ArrayObject
{
/**
* Constructs a new instance of .
*
* @param mixed $input (Optional) The input parameter accepts an array or an Object. The default value is an empty array.
* @param integer $flags (Optional) Flags to control the behavior of the ArrayObject object. Defaults to .
* @param string $iterator_class (Optional) Specify the class that will be used for iteration of the object. is the default class used.
* @return mixed Either an array of matches, or a single element.
*/
public function __construct($input = array(), $flags = self::STD_PROP_LIST, $iterator_class = 'ArrayIterator')
{
// Provide a default value
$input = $input ? $input : array();
try {
return parent::__construct($input, $flags, $iterator_class);
}
catch (InvalidArgumentException $e)
{
throw new CFArray_Exception($e->getMessage());
}
}
/**
* Alternate approach to constructing a new instance. Supports chaining.
*
* @param mixed $input (Optional) The input parameter accepts an array or an Object. The default value is an empty array.
* @param integer $flags (Optional) Flags to control the behavior of the ArrayObject object. Defaults to .
* @param string $iterator_class (Optional) Specify the class that will be used for iteration of the object. is the default class used.
* @return mixed Either an array of matches, or a single element.
*/
public static function init($input = array(), $flags = self::STD_PROP_LIST, $iterator_class = 'ArrayIterator')
{
if (version_compare(PHP_VERSION, '5.3.0', '<'))
{
throw new Exception('PHP 5.3 or newer is required to instantiate a new class with CLASS::init().');
}
$self = get_called_class();
return new $self($input, $flags, $iterator_class);
}
/**
* Handles how the object is rendered when cast as a string.
*
* @return string The word "Array".
*/
public function __toString()
{
return 'Array';
}
/*%******************************************************************************************%*/
// REFORMATTING
/**
* Maps each element in the object as an integer.
*
* @return array The contents of the object mapped as integers.
*/
public function map_integer()
{
return array_map('intval', $this->getArrayCopy());
}
/**
* Maps each element in the CFArray object as a string.
*
* @param string $pcre (Optional) A Perl-Compatible Regular Expression (PCRE) to filter the names against.
* @return array The contents of the object mapped as strings. If there are no results, the method will return an empty array.
*/
public function map_string($pcre = null)
{
$list = array_map('strval', $this->getArrayCopy());
$dlist = array();
if ($pcre)
{
foreach ($list as $item)
{
$dlist[] = preg_match($pcre, $item) ? $item : null;
}
$list = array_values(array_filter($dlist));
}
return $list;
}
/*%******************************************************************************************%*/
// CONFIRMATION
/**
* Verifies that _all_ responses were successful. A single failed request will cause to return false. Equivalent to , except it applies to all responses.
*
* @return boolean Whether _all_ requests were successful or not.
*/
public function areOK()
{
$dlist = array();
$list = $this->getArrayCopy();
foreach ($list as $response)
{
if ($response instanceof CFResponse)
{
$dlist[] = $response->isOK();
}
}
return (array_search(false, $dlist, true) !== false) ? false : true;
}
/*%******************************************************************************************%*/
// ITERATING AND EXECUTING
/**
* Iterates over a object, and executes a function for each matched element.
*
* The callback function takes three parameters:
* $item - mixed - Optional - The individual node in the array.
* $key - mixed - Optional - The key for the array node.
* $bind - mixed - Optional - The variable that was passed into the $bind parameter.
*
* @param string|function $callback (Required) The callback function to execute. PHP 5.3 or newer can use an anonymous function.
* @param mixed $bind (Optional) A variable from the calling scope to pass-by-reference into the local scope of the callback function.
* @return CFArray The original object.
*/
public function each($callback, &$bind = null)
{
$items = $this->getArrayCopy();
foreach ($items as $key => &$item)
{
$callback($item, $key, $bind);
}
return $this;
}
/**
* Passes each element in the current object through a function, and produces a new object containing the return values.
*
* The callback function takes three parameters:
* $item - mixed - Optional - The individual node in the array.
* $key - mixed - Optional - The key for the array node.
* $bind - mixed - Optional - The variable that was passed into the $bind parameter.
*
* @param string|function $callback (Required) The callback function to execute. PHP 5.3 or newer can use an anonymous function.
* @param mixed $bind (Optional) A variable from the calling scope to pass-by-reference into the local scope of the callback function.
* @return CFArray A new object containing the return values.
*/
public function map($callback, &$bind = null)
{
$items = $this->getArrayCopy();
$collect = array();
foreach ($items as $key => &$item)
{
$collect[] = $callback($item, $key, $bind);
}
return new CFArray($collect);
}
/**
* Filters the list of nodes by passing each value in the current object through a function. The node will be removed if the function returns `false`.
*
* The callback function takes three parameters:
* $item - mixed - Optional - The individual node in the array.
* $key - mixed - Optional - The key for the array node.
* $bind - mixed - Optional - The variable that was passed into the $bind parameter.
*
* @param string|function $callback (Required) The callback function to execute. PHP 5.3 or newer can use an anonymous function.
* @param mixed $bind (Optional) A variable from the calling scope to pass-by-reference into the local scope of the callback function.
* @return CFArray A new object containing the return values.
*/
public function filter($callback, &$bind = null)
{
$items = $this->getArrayCopy();
$collect = array();
foreach ($items as $key => &$item)
{
if ($callback($item, $key, $bind) !== false)
{
$collect[] = $item;
}
}
return new CFArray($collect);
}
/**
* Alias for . This functionality was incorrectly named _reduce_ in earlier versions of the SDK.
*
* @param string|function $callback (Required) The callback function to execute. PHP 5.3 or newer can use an anonymous function.
* @param mixed $bind (Optional) A variable from the calling scope to pass-by-reference into the local scope of the callback function.
* @return CFArray A new object containing the return values.
*/
public function reduce($callback, &$bind = null)
{
return $this->filter($callback, $bind);
}
/*%******************************************************************************************%*/
// TRAVERSAL
/**
* Gets the first result in the array.
*
* @return mixed The first result in the object. Returns `false` if there are no items in the array.
*/
public function first()
{
$items = $this->getArrayCopy();
return count($items) ? $items[0] : false;
}
/**
* Gets the last result in the array.
*
* @return mixed The last result in the object. Returns `false` if there are no items in the array.
*/
public function last()
{
$items = $this->getArrayCopy();
return count($items) ? end($items) : false;
}
/**
* Removes all `null` values from an array.
*
* @return CFArray A new object containing the non-null values.
*/
public function compress()
{
return new CFArray(array_filter($this->getArrayCopy()));
}
/**
* Reindexes the array, starting from zero.
*
* @return CFArray A new object with indexes starting at zero.
*/
public function reindex()
{
return new CFArray(array_values($this->getArrayCopy()));
}
/*%******************************************************************************************%*/
// ALTERNATE FORMATS
/**
* Gets the current XML node as a JSON string.
*
* @return string The current XML node as a JSON string.
*/
public function to_json()
{
return json_encode($this->getArrayCopy());
}
/**
* Gets the current XML node as a YAML string.
*
* @return string The current XML node as a YAML string.
*/
public function to_yaml()
{
return sfYaml::dump($this->getArrayCopy(), 5);
}
}
class CFArray_Exception extends Exception {}
================================================
FILE: utilities/batchrequest.class.php
================================================
queue = array();
$this->limit = $limit ? $limit : -1;
$this->credentials = new CFCredential(array());
return $this;
}
/**
* Sets the AWS credentials to use for the batch request.
*
* @param CFCredential $credentials (Required) The credentials to use for signing and making requests.
* @return $this A reference to the current instance.
*/
public function use_credentials(CFCredential $credentials)
{
$this->credentials = $credentials;
return $this;
}
/**
* Adds a new cURL handle to the request queue.
*
* @param resource $handle (Required) A cURL resource to add to the queue.
* @return $this A reference to the current instance.
*/
public function add($handle)
{
$this->queue[] = $handle;
return $this;
}
/**
* Executes the batch request queue.
*
* @param array $opt (DO NOT USE) Enabled for compatibility with the method this overrides, although any values passed will be ignored.
* @return array An indexed array of objects.
*/
public function send($opt = null)
{
$http = new $this->request_class(null, $this->proxy, null, $this->credentials);
// Make the request
$response = $http->send_multi_request($this->queue, array(
'limit' => $this->limit
));
return $response;
}
}
================================================
FILE: utilities/complextype.class.php
================================================
function.
* @param string $member (Optional) The name of the "member" property that AWS uses for lists in certain services. Defaults to an empty string.
* @param string $default_key (Optional) The default key to use when the value for `$data` is a string. Defaults to an empty string.
* @return array The option group parameters to merge into another method's `$opt` parameter.
*/
public static function json($json, $member = '', $default_key = '')
{
return self::option_group(json_decode($json, true), $member, $default_key);
}
/**
* Takes a YAML object, as a string, to convert to query string keys.
*
* @param string $yaml (Required) A YAML object.
* @param string $member (Optional) The name of the "member" property that AWS uses for lists in certain services. Defaults to an empty string.
* @param string $default_key (Optional) The default key to use when the value for `$data` is a string. Defaults to an empty string.
* @return array The option group parameters to merge into another method's `$opt` parameter.
*/
public static function yaml($yaml, $member = '', $default_key = '')
{
return self::option_group(sfYaml::load($yaml), $member, $default_key);
}
/**
* Takes an associative array to convert to query string keys.
*
* @param array $map (Required) An associative array.
* @param string $member (Optional) The name of the "member" property that AWS uses for lists in certain services. Defaults to an empty string.
* @param string $default_key (Optional) The default key to use when the value for `$data` is a string. Defaults to an empty string.
* @return array The option group parameters to merge into another method's `$opt` parameter.
*/
public static function map($map, $member = '', $default_key = '')
{
return self::option_group($map, $member, $default_key);
}
/**
* A protected method that is used by , and