Full Code of smaranchand/bucky for AI

master bda01360514f cached
32 files
368.1 KB
102.0k tokens
270 symbols
1 requests
Download .txt
Showing preview only (382K chars total). Download the full file or copy to clipboard to get everything.
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
================================================

![Bucky](https://github.com/smaranchand/bucky/blob/master/bucky.gif?raw=true)

# 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

![run_bucky](https://github.com/smaranchand/bucky/blob/master/scr/run_bucky.png?raw=true)

Loading Addon

![load_addon](https://github.com/smaranchand/bucky/blob/master/scr/bucky_addon.png?raw=true)
User Interface

![dashboard](https://github.com/smaranchand/bucky/blob/master/scr/dashboard_loading.png?raw=true)

All Buckets

![all_buckets](https://github.com/smaranchand/bucky/blob/master/scr/all_buckets.png?raw=true)
Manual Check

![manual_check](https://github.com/smaranchand/bucky/blob/master/scr/manual_check.png?raw=true)

POC By Bucky

![Bucky_POC](https://github.com/smaranchand/bucky/blob/master/scr/vulnerable_poc.png?raw=true)



# 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
================================================
<!DOCTYPE html>
<html>
<title>All Buckets</title>
<style>
a {
    color: inherit;
    text-decoration: none;
}
table {
  font-family: arial, sans-serif;
  border-collapse: collapse;
  width: 100%;
}

td, th {
  border: 2px solid #0dbce7;
  text-align: left;
  padding: 8px;
}

.newcls{
   
 
 background-color:#0dbce7;
  
 border: #2e6da4;
  
 font-family: Arial, Helvetica,  sans-serif;
  
 font-size: 15px;
  
 color: #fff;
  
 letter-spacing: 1px;
  
 padding: 8px 12px;
  
 font-size: 14px;
  
 font-weight: normal;
  
 border-radius: 4px;
  
 line-height: 1.5;
  
 text-decoration:none
  
 }
</style>

<div class="newcls">

<h1> All Buckets |  <a href=../manual_check.php>Check Manually</a></h1></div>
<br>
<table border=1>
<tr>
<th>SN</th>
<th>Bucket Name</th>
<th>Source URL</th>
<th>IP</th>
<th>POC</th>
<th>VULNERABLE</th>
</tr>
<?php
   class MyDB extends SQLite3 {
      function __construct() {
         $this->open('bucky.db');
      }
   }
   
   $db = new MyDB();
   if(!$db) {
      echo $db->lastErrorMsg();
   } else {

   }

   $sql =<<<EOF
      SELECT * from BUCKY;
EOF;

   $ret = $db->query($sql);
   while($row = $ret->fetchArray(SQLITE3_ASSOC) ) {
      echo "<tr>";
      echo "<td>". $row['ID'] . "</td>";
      echo "<td>". $row['BUCKETNAME'] . "</td>";
      echo "<td>". $row['URL'] ."</td>";
      echo "<td>". $row['IP'] ."</td>";
      echo "<td><a>". $row['POC'] ."</a></td>";
      echo "<td>".$row['VULNERABLE'] ."</td>";
   }
   $db->close();
?>

================================================
FILE: config.inc.php
================================================
<?php if (!class_exists('CFRuntime')) die('No direct access allowed.');
/**
 * Stores your AWS account information. Add your account information, and then rename this file
 * to 'config.inc.php'.
 *
 * @version 2011.12.14
 * @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://aws.amazon.com/security-credentials AWS Security Credentials
 */


/*###################################################################################################

	As of version 1.5, the AWS SDK for PHP uses the CFCredentials class to handle credentials.
	This class has the advantage of being able to support multiple sets of credentials at a time,
	including the ability for credential sets to inherit settings from other credential sets.

	Some example uses are noted at https://gist.github.com/1478912

	Notes:

	* You can define one or more credential sets.

	* Credential sets can be named anything that PHP allows for an associative array key;
	  "production", "staging", etc., are just sample values. Feel free to rename them.

	* A credential set only has four required entries: key, secret, default_cache_config and
	  certificate_authority. Aside from these, you can add any additional bits of information
	  you'd like to keep easily accessible (e.g., multi-factor authentication device key, your
	  AWS Account ID, your canonical identifiers).

	* Additional credential sets can inherit the properties of another credential set using the
	  @inherit keyword.

	* If more than one credential set is provided, a default credential set must be specified
	  using the @default keyword.

	* If you only have one credential set, you can set it to the @default keyword.

	* View the documentation for the CFCredentials::set() method to view usage examples.

###################################################################################################*/


/**
 * Create a list of credential sets that can be used with the SDK.
 */
CFCredentials::set(array(

	// Credentials for the development environment.
	'development' => 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
================================================
<?php
//This script creates SQLite3 database and add tables.
   class MyDB extends SQLite3 {
      function __construct() {
         $this->open('bucky.db');
      }
   }
   $db = new MyDB();
   if(!$db) {
      echo $db->lastErrorMsg();
   } else {
   $sql =<<<EOF
      CREATE TABLE BUCKY
      (ID INTEGER PRIMARY KEY AUTOINCREMENT,
      BUCKETNAME           VARCHAR    NOT NULL,
      URL                  VARCHAR    NOT NULL,
      IP            VARCHAR     NOT NULL,
      POC        VARCHAR     NOT NULL,
      VULNERABLE         VARCHAR);
EOF;

   $ret = $db->exec($sql);
   if(!$ret){
      echo $db->lastErrorMsg();
   } else {
      echo "Database setup successful.";
   }
   $db->close();
}
?>


================================================
FILE: index.php
================================================
<?php
header("refresh:4; url=all_buckets.php");
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Bucky</title>
</head>
<body>
<style>
.loading{
display:block;
margin: 0 auto;
}
</style>
<img src="bucky.gif" class="loading">
</body>
</html>

================================================
FILE: lib/requestcore/requestcore.class.php
================================================
<?php
/**
 * Handles all HTTP requests using cURL and manages the responses.
 *
 * @version 2012.01.17
 * @copyright 2006-2011 Ryan Parman
 * @copyright 2006-2010 Foleeo Inc.
 * @copyright 2010-2011 Amazon.com, Inc. or its affiliates.
 * @copyright 2008-2011 Contributors
 * @license http://opensource.org/licenses/bsd-license.php Simplified BSD License
 */
class RequestCore
{
	/**
	 * The URL being requested.
	 */
	public $request_url;

	/**
	 * The headers being sent in the request.
	 */
	public $request_headers;

	/**
	 * The body being sent in the request.
	 */
	public $request_body;

	/**
	 * The response returned by the request.
	 */
	public $response;

	/**
	 * The headers returned by the request.
	 */
	public $response_headers;

	/**
	 * The body returned by the request.
	 */
	public $response_body;

	/**
	 * The HTTP status code returned by the request.
	 */
	public $response_code;

	/**
	 * Additional response data.
	 */
	public $response_info;

	/**
	 * The handle for the cURL object.
	 */
	public $curl_handle;

	/**
	 * The method by which the request is being made.
	 */
	public $method;

	/**
	 * Stores the proxy settings to use for the request.
	 */
	public $proxy = null;

	/**
	 * The username to use for the request.
	 */
	public $username = null;

	/**
	 * The password to use for the request.
	 */
	public $password = null;

	/**
	 * Custom CURLOPT settings.
	 */
	public $curlopts = null;

	/**
	 * The state of debug mode.
	 */
	public $debug_mode = false;

	/**
	 * The default class to use for HTTP Requests (defaults to <RequestCore>).
	 */
	public $request_class = 'RequestCore';

	/**
	 * The default class to use for HTTP Responses (defaults to <ResponseCore>).
	 */
	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: <HTTP_GET>, <HTTP_POST>, <HTTP_PUT>, <HTTP_HEAD>, <HTTP_DELETE>.
	 * @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 <php:fstat()> and
	 * <php:ftell()>.
	 *
	 * @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
	 * <CFRequest::streaming_read_callback()>.
	 *
	 * The user-defined callback function should accept three arguments:
	 *
	 * <ul>
	 * 	<li><code>$curl_handle</code> - <code>resource</code> - Required - The cURL handle resource that represents the in-progress transfer.</li>
	 * 	<li><code>$file_handle</code> - <code>resource</code> - Required - The file handle resource that represents the file on the local file system.</li>
	 * 	<li><code>$length</code> - <code>integer</code> - Required - The length in kilobytes of the data chunk that was transferred.</li>
	 * </ul>
	 *
	 * @param string|array|function $callback (Required) The callback function is called by <php:call_user_func()>, so you can pass the following values: <ul>
	 * 	<li>The name of a global function to execute, passed as a string.</li>
	 * 	<li>A method to execute, passed as <code>array('ClassName', 'MethodName')</code>.</li>
	 * 	<li>An anonymous function (PHP 5.3+).</li></ul>
	 * @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
	 * <CFRequest::streaming_write_callback()>.
	 *
	 * The user-defined callback function should accept two arguments:
	 *
	 * <ul>
	 * 	<li><code>$curl_handle</code> - <code>resource</code> - Required - The cURL handle resource that represents the in-progress transfer.</li>
	 * 	<li><code>$length</code> - <code>integer</code> - Required - The length in kilobytes of the data chunk that was transferred.</li>
	 * </ul>
	 *
	 * @param string|array|function $callback (Required) The callback function is called by <php:call_user_func()>, so you can pass the following values: <ul>
	 * 	<li>The name of a global function to execute, passed as a string.</li>
	 * 	<li>A method to execute, passed as <code>array('ClassName', 'MethodName')</code>.</li>
	 * 	<li>An anonymous function (PHP 5.3+).</li></ul>
	 * @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 <php:curl_multi_exec()>
	 * 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 <ResponseCore> 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 <php:curl_multi_exec()>, 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: <ul>
	 * 	<li><code>callback</code> - <code>string|array</code> - Optional - The string name of a function to pass the response data to. If this is a method, pass an array where the <code>[0]</code> index is the class and the <code>[1]</code> index is the method name.</li>
	 * 	<li><code>limit</code> - <code>integer</code> - 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.</li></ul>
	 * @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 <RequestCore::get_response_header()>).
	 * @param string $body (Required) XML-formatted response from AWS.
	 * @param integer $status (Optional) HTTP response status code from the request.
	 * @return object Contains an <php:array> `header` property (HTTP headers as an associative array), a <php:SimpleXMLElement> or <php:string> `body` property, and an <php:integer> `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 <php:integer> for a single acceptable value, or an <php:array> 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
================================================
<html>
<title>Manual Check</title>
<!--<body bgcolor="yellow"></body>-->
<body>
<style>
a {
    color: inherit;
    text-decoration: none;
}
.newcls{
 
 background-color:#0dbce7;
  
 border: #2e6da4;
  
 font-family: Arial, Helvetica,  sans-serif;
  
 font-size: 15px;
  
 color: #fff;
  
 letter-spacing: 1px;
  
 padding: 8px 12px;
  
 font-size: 14px;
  
 font-weight: normal;
  
 border-radius: 4px;
  
 line-height: 1.5;
  
 text-decoration:none
  
 }
</style>

<div class="newcls">
<h1> Check Manually |  <a href=../all_buckets.php>All Buckets</a></h1>
</div>
<br>

<form name="manualcheck" method="POST"  action="process.php">
<input type="text" class="newcls" name="bucketname" pattern="[a-z0-9-.]{3,63}" placeholder="Enter Bucket Name..." maxlength="63" size="25" required="required">
<input type="hidden" class="newcls" name="sourceurl" value="-">
<input type="hidden" class="newcls" name="hostname" value="-">
<input type="submit" class="newcls" value="Check Bucket">
</form>
</body>
</html>

================================================
FILE: process.php
================================================
<?php
//I admit that i am a noob programmer, if you do have suggestions to improve Bucky,  lets talk https://twitter.com/smaranchand
function bucketsuccess(){
    $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 =<<<EOF
        INSERT INTO BUCKY (BUCKETNAME,URL,IP,POC,VULNERABLE)
        VALUES ('$newbucket', '$sourceurl', '$serverip', 'https://$newbucket.s3.amazonaws.com/bucky.txt', '<b>YES</b>' );
  
  EOF;
  
     $ret = $db->exec($sql);
     if(!$ret) {
        echo $db->lastErrorMsg();
     } else {
   
   header("refresh:3; url=all_buckets.php");
   echo "S3 Bucket <b>$newbucket</b> 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 =<<<EOF
        INSERT INTO BUCKY (BUCKETNAME,URL,IP,POC,VULNERABLE)
        VALUES ('$newbucket', '$sourceurl', '$serverip', '-', 'NO' );
  
  EOF;
  
     $ret = $db->exec($sql);
     if(!$ret) {
        echo $db->lastErrorMsg();
     } else {
      header("refresh:3; url=manual_check.php");
      echo "S3 Bucket <b>$newbucket</b> 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
================================================
<?php
/*
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */


/*%******************************************************************************************%*/
// EXCEPTIONS

/**
 * Default CFRuntime Exception.
 */
class CFRuntime_Exception extends Exception {}

/**
 * Parsing Exception.
 */
class Parser_Exception extends Exception {}


/*%******************************************************************************************%*/
// INTERMEDIARY CONSTANTS

define('CFRUNTIME_NAME', 'aws-sdk-php');
define('CFRUNTIME_VERSION', '1.6.2');
define('CFRUNTIME_BUILD', '20130314130000');
$user_agent = sprintf('%s/%s PHP/%s', CFRUNTIME_NAME, CFRUNTIME_VERSION, PHP_VERSION);
if (function_exists('curl_version'))
{
	$curl_version = curl_version();
	$user_agent .= ' curl/' . $curl_version['version'];
}
if (defined('OPENSSL_VERSION_TEXT'))
{
	$openssl_version = explode(' ', OPENSSL_VERSION_TEXT);
	$user_agent .=  ' openssl/' . $openssl_version[1];
}
define('CFRUNTIME_USERAGENT', $user_agent);
unset($user_agent);


/*%******************************************************************************************%*/
// CLASS

/**
 * Core functionality and default settings shared across all SDK classes. All methods and properties in this
 * class are inherited by the service-specific classes.
 *
 * @version 2012.05.25
 * @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
 */
class CFRuntime
{
	/*%******************************************************************************************%*/
	// CONSTANTS

	/**
	 * Name of the software.
	 */
	const NAME = CFRUNTIME_NAME;

	/**
	 * Version of the software.
	 */
	const VERSION = CFRUNTIME_VERSION;

	/**
	 * Build ID of the software.
	 */
	const BUILD = CFRUNTIME_BUILD;

	/**
	 * User agent string used to identify the software.
	 */
	const USERAGENT = CFRUNTIME_USERAGENT;


	/*%******************************************************************************************%*/
	// PROPERTIES

	/**
	 * The Amazon API Key.
	 */
	public $key;

	/**
	 * The Amazon API Secret Key.
	 */
	public $secret_key;

	/**
	 * The Amazon Authentication Token.
	 */
	public $auth_token;

	/**
	 * Handle for the utility functions.
	 */
	public $util;

	/**
	 * An identifier for the current AWS service.
	 */
	public $service = null;

	/**
	 * The supported API version.
	 */
	public $api_version = null;

	/**
	 * The state of whether auth should be handled as AWS Query.
	 */
	public $use_aws_query = true;

	/**
	 * The default class to use for utilities (defaults to <CFUtilities>).
	 */
	public $utilities_class = 'CFUtilities';

	/**
	 * The default class to use for HTTP requests (defaults to <CFRequest>).
	 */
	public $request_class = 'CFRequest';

	/**
	 * The default class to use for HTTP responses (defaults to <CFResponse>).
	 */
	public $response_class = 'CFResponse';

	/**
	 * The default class to use for parsing XML (defaults to <CFSimpleXML>).
	 */
	public $parser_class = 'CFSimpleXML';

	/**
	 * The default class to use for handling batch requests (defaults to <CFBatchRequest>).
	 */
	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 <set_hostname()>.
	 */
	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: <ul>
	 * 	<li><code>certificate_authority</code> - <code>boolean</code> - Optional - Determines which Cerificate Authority file to use. A value of boolean <code>false</code> will use the Certificate Authority file available on the system. A value of boolean <code>true</code> will use the Certificate Authority provided by the SDK. Passing a file system path to a Certificate Authority file (chmodded to <code>0755</code>) will use that. Leave this set to <code>false</code> if you're not sure.</li>
	 * 	<li><code>credentials</code> - <code>string</code> - Optional - The name of the credential set to use for authentication.</li>
	 * 	<li><code>default_cache_config</code> - <code>string</code> - Optional - This option allows a preferred storage type to be configured for long-term caching. This can be changed later using the <set_cache_config()> method. Valid values are: <code>apc</code>, <code>xcache</code>, or a file system path such as <code>./cache</code> or <code>/tmp/cache/</code>.</li>
	 * 	<li><code>key</code> - <code>string</code> - Optional - Your AWS key, or a session key. If blank, the default credential set will be used.</li>
	 * 	<li><code>instance_profile_timeout</code> - <code>integer</code> - 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.</li>
	 * 	<li><code>secret</code> - <code>string</code> - Optional - Your AWS secret key, or a session secret key. If blank, the default credential set will be used.</li>
	 * 	<li><code>token</code> - <code>string</code> - Optional - An AWS session token.</li>
	 * 	<li><code>use_instance_profile_credentials</code> - <code>boolean</code> - Optional - Forces the use of IAM Instance Profile credentials, even when regular credentials are provided.</li></ul>
	 * @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: <ul>
	 * 	<li><code>certificate_authority</code> - <code>boolean</code> - Optional - Determines which Cerificate Authority file to use. A value of boolean <code>false</code> will use the Certificate Authority file available on the system. A value of boolean <code>true</code> will use the Certificate Authority provided by the SDK. Passing a file system path to a Certificate Authority file (chmodded to <code>0755</code>) will use that. Leave this set to <code>false</code> if you're not sure.</li>
	 * 	<li><code>credentials</code> - <code>string</code> - Optional - The name of the credential set to use for authentication.</li>
	 * 	<li><code>default_cache_config</code> - <code>string</code> - Optional - This option allows a preferred storage type to be configured for long-term caching. This can be changed later using the <set_cache_config()> method. Valid values are: <code>apc</code>, <code>xcache</code>, or a file system path such as <code>./cache</code> or <code>/tmp/cache/</code>.</li>
	 * 	<li><code>key</code> - <code>string</code> - Optional - Your AWS key, or a session key. If blank, the default credential set will be used.</li>
	 * 	<li><code>secret</code> - <code>string</code> - Optional - Your AWS secret key, or a session secret key. If blank, the default credential set will be used.</li>
	 * 	<li><code>token</code> - <code>string</code> - Optional - An AWS session token.</li></ul>
	 * @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 <set_hostname()> method.
	 *
	 * @param boolean $override (Optional) Whether or not subsequent calls to <set_hostname()> should be obeyed. A `false` value disables the further effectiveness of <set_hostname()>. 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
	 * <php:error_reporting()> 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
	 * <php:error_reporting()> 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) <p>The location to store the cache object in. This may vary by cache method.</p><ul><li>File - The local file system paths such as <code>./cache</code> (relative) or <code>/tmp/cache/</code> (absolute). The location must be server-writable.</li><li>APC - Pass in <code>apc</code> to use this lightweight cache. You must have the <a href="http://php.net/apc">APC extension</a> installed.</li><li>XCache - Pass in <code>xcache</code> to use this lightweight cache. You must have the <a href="http://xcache.lighttpd.net">XCache</a> extension installed.</li><li>Memcached - Pass in an indexed array of associative arrays. Each associative array should have a <code>host</code> and a <code>port</code> value representing a <a href="http://php.net/memcached">Memcached</a> server to connect to.</li><li>PDO - A URL-style string (e.g. <code>pdo.mysql://user:pass@localhost/cache</code>) or a standard DSN-style string (e.g. <code>pdo.sqlite:/sqlite/cache.db</code>). MUST be prefixed with <code>pdo.</code>. See <code>CachePDO</code> and <a href="http://php.net/pdo">PDO</a> for more details.</li></ul>
	 * @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
	 * <CFRequest::streaming_read_callback()>.
	 *
	 * The user-defined callback function should accept three arguments:
	 *
	 * <ul>
	 * 	<li><code>$curl_handle</code> - <code>resource</code> - Required - The cURL handle resource that represents the in-progress transfer.</li>
	 * 	<li><code>$file_handle</code> - <code>resource</code> - Required - The file handle resource that represents the file on the local file system.</li>
	 * 	<li><code>$length</code> - <code>integer</code> - Required - The length in kilobytes of the data chunk that was transferred.</li>
	 * </ul>
	 *
	 * @param string|array|function $callback (Required) The callback function is called by <php:call_user_func()>, so you can pass the following values: <ul>
	 * 	<li>The name of a global function to execute, passed as a string.</li>
	 * 	<li>A method to execute, passed as <code>array('ClassName', 'MethodName')</code>.</li>
	 * 	<li>An anonymous function (PHP 5.3+).</li></ul>
	 * @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
	 * <CFRequest::streaming_write_callback()>.
	 *
	 * The user-defined callback function should accept two arguments:
	 *
	 * <ul>
	 * 	<li><code>$curl_handle</code> - <code>resource</code> - Required - The cURL handle resource that represents the in-progress transfer.</li>
	 * 	<li><code>$length</code> - <code>integer</code> - Required - The length in kilobytes of the data chunk that was transferred.</li>
	 * </ul>
	 *
	 * @param string|array|function $callback (Required) The callback function is called by <php:call_user_func()>, so you can pass the following values: <ul>
	 * 	<li>The name of a global function to execute, passed as a string.</li>
	 * 	<li>A method to execute, passed as <code>array('ClassName', 'MethodName')</code>.</li>
	 * 	<li>An anonymous function (PHP 5.3+).</li></ul>
	 * @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 <CFUtilities>.
	 *
	 * @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 <CFRequest>.
	 *
	 * @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 <CFResponse>.
	 *
	 * @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 <CFSimpleXML>.
	 *
	 * @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 <CFBatchRequest>.
	 *
	 * @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 <CFBatchRequest> instance to use for managing batch requests. If not available, it generates a new instance of <CFBatchRequest>.
	 * @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 <CFResponse> 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 <CFResponse> 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 <CFResponse> 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, '<?xml') === 0 || strpos($body, '<Error>') === 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 <CFResponse> object should be cached according to the settings from
	 * <set_cache_config()>.
	 *
	 * @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 <php:strtotime()> (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 <authenticate()> 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 <CFResponse> 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
================================================
<?php
/*
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */


/*%******************************************************************************************%*/
// EXCEPTIONS

/**
 * Default S3 Exception.
 */
class S3_Exception extends Exception {}


/*%******************************************************************************************%*/
// MAIN CLASS

/**
 * Amazon S3 is a web service that enables you to store data in the cloud. You can then download the data
 * or use the data with other AWS services, such as Amazon Elastic Cloud Computer (EC2).
 *
 * Amazon Simple Storage Service (Amazon S3) is storage for the Internet. You can use Amazon S3 to store
 * and retrieve any amount of data at any time, from anywhere on the web. You can accomplish these tasks
 * using the AWS Management Console, which is a simple and intuitive web interface.
 *
 * To get the most out of Amazon S3, you need to understand a few simple concepts. Amazon S3 stores data
 * as objects in buckets. An object is comprised of a file and optionally any metadata that describes
 * that file.
 *
 * To store an object in Amazon S3, you upload the file you want to store to a bucket. When you upload a
 * file, you can set permissions on the object as well as any metadata.
 *
 * Buckets are the containers for objects. You can have one or more buckets. For each bucket, you can control
 * access to the bucket (who can create, delete, and list objects in the bucket), view access logs for the
 * bucket and its objects, and choose the geographical region where Amazon S3 will store the bucket and its
 * contents.
 *
 * Visit <http://aws.amazon.com/s3/> 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 <AmazonS3>.
	 *
	 * @param array $options (Optional) An associative array of parameters that can have the following keys: <ul>
	 * 	<li><code>certificate_authority</code> - <code>boolean</code> - Optional - Determines which Cerificate Authority file to use. A value of boolean <code>false</code> will use the Certificate Authority file available on the system. A value of boolean <code>true</code> will use the Certificate Authority provided by the SDK. Passing a file system path to a Certificate Authority file (chmodded to <code>0755</code>) will use that. Leave this set to <code>false</code> if you're not sure.</li>
	 * 	<li><code>credentials</code> - <code>string</code> - Optional - The name of the credential set to use for authentication.</li>
	 * 	<li><code>default_cache_config</code> - <code>string</code> - Optional - This option allows a preferred storage type to be configured for long-term caching. This can be changed later using the <set_cache_config()> method. Valid values are: <code>apc</code>, <code>xcache</code>, or a file system path such as <code>./cache</code> or <code>/tmp/cache/</code>.</li>
	 * 	<li><code>key</code> - <code>string</code> - Optional - Your AWS key, or a session key. If blank, the default credential set will be used.</li>
	 * 	<li><code>secret</code> - <code>string</code> - Optional - Your AWS secret key, or a session secret key. If blank, the default credential set will be used.</li>
	 * 	<li><code>token</code> - <code>string</code> - Optional - An AWS session token.</li></ul>
	 * @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             = '<?xml version="1.0" encoding="UTF-8"?><AccessControlPolicy xmlns="http://s3.amazonaws.com/doc/latest/"/>';
		$this->base_location_constraint = '<?xml version="1.0" encoding="UTF-8"?><CreateBucketConfiguration xmlns="http://s3.amazonaws.com/doc/' . $this->api_version . '/"><LocationConstraint/></CreateBucketConfiguration>';
		$this->base_logging_xml         = '<?xml version="1.0" encoding="utf-8"?><BucketLoggingStatus xmlns="http://doc.s3.amazonaws.com/' . $this->api_version . '"/>';
		$this->base_notification_xml    = '<?xml version="1.0" encoding="utf-8"?><NotificationConfiguration/>';
		$this->base_versioning_xml      = '<?xml version="1.0" encoding="utf-8"?><VersioningConfiguration xmlns="http://s3.amazonaws.com/doc/' . $this->api_version . '/"/>';
		$this->complete_mpu_xml         = '<?xml version="1.0" encoding="utf-8"?><CompleteMultipartUpload/>';
		$this->website_config_xml       = '<?xml version="1.0" encoding="utf-8"?><WebsiteConfiguration xmlns="http://s3.amazonaws.com/doc/' . $this->api_version . '/"><IndexDocument><Suffix>index.html</Suffix></IndexDocument><ErrorDocument><Key>error.html</Key></ErrorDocument></WebsiteConfiguration>';
		$this->multi_object_delete_xml  = '<?xml version="1.0" encoding="utf-8"?><Delete/>';
		$this->object_expiration_xml    = '<?xml version="1.0" encoding="utf-8"?><LifecycleConfiguration/>';
		$this->bucket_tagging_xml       = '<?xml version="1.0" encoding="utf-8"?><Tagging><TagSet/></Tagging>';
		$this->cors_config_xml          = '<?xml version="1.0" encoding="utf-8"?><CORSConfiguration />';
		$this->restore_request_xml      = '<?xml version="1.0" encoding="utf-8"?><RestoreRequest xmlns="http://s3.amazonaws.com/doc/' . $this->api_version . '"/>';

		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 <CFResponse> 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 ##
		 * 	<verb> /<resource>?<sub_resource>&<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 <code>true</code> means that the bucket name is valid. A value of <code>false</code> 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 <code>true</code> means that the bucket name is valid. A value of <code>false</code> 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
	 * <enable_path_style()>.
	 *
	 * @param string $region (Required) The region to use for subsequent Amazon S3 operations. For a complete list of REGION constants, see the <code>AmazonS3</code> 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 <code>true</code>.
	 * @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: <code>AmazonS3::ACL_PRIVATE</code>, <code>AmazonS3::ACL_PUBLIC</code>, <code>AmazonS3::ACL_OPEN</code>, <code>AmazonS3::ACL_AUTH_READ</code>, <code>AmazonS3::ACL_OWNER_READ</code>, <code>AmazonS3::ACL_OWNER_FULL_CONTROL</code>]. Alternatively, an array of associative arrays. Each associative array contains an <code>id</code> and a <code>permission</code> key. The default value is <code>ACL_PRIVATE</code>.
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
	 * 	<li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
	 * 	<li><code>returnCurlHandle</code> - <code>boolean</code> - Optional - A private toggle specifying that the cURL handle be returned rather than actually completing the request.</li></ul>
	 * @return CFResponse A <CFResponse> 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., <bucketname>.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: <ul>
	 * 	<li><code>preauth</code> - <code>integer|string</code> - 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 <php:strtotime()>.</li>
	 * 	<li><code>returnCurlHandle</code> - <code>boolean</code> - 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.</li></ul>
	 * @return CFResponse A <CFResponse> 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: <ul>
	 * 	<li><code>preauth</code> - <code>integer|string</code> - 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 <php:strtotime()>.</li>
	 * 	<li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
	 * 	<li><code>returnCurlHandle</code> - <code>boolean</code> - 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.</li></ul>
	 * @return CFResponse A <CFResponse> 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 <code>false</code>.
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
	 * 	<li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
	 * 	<li><code>returnCurlHandle</code> - <code>boolean</code> - 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.</li></ul>
	 * @return mixed A <CFResponse> object if the bucket was deleted successfully. Returns boolean <code>false</code> 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: <ul>
	 * 	<li><code>preauth</code> - <code>integer|string</code> - 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 <php:strtotime()>.</li>
	 * 	<li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
	 * 	<li><code>returnCurlHandle</code> - <code>boolean</code> - 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.</li></ul>
	 * @return CFResponse A <CFResponse> 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: <ul>
	 * 	<li><code>preauth</code> - <code>integer|string</code> - 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 <php:strtotime()>.</li>
	 * 	<li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
	 * 	<li><code>returnCurlHandle</code> - <code>boolean</code> - 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.</li></ul>
	 * @return CFResponse A <CFResponse> 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: <code>AmazonS3::ACL_PRIVATE</code>, <code>AmazonS3::ACL_PUBLIC</code>, <code>AmazonS3::ACL_OPEN</code>, <code>AmazonS3::ACL_AUTH_READ</code>, <code>AmazonS3::ACL_OWNER_READ</code>, <code>AmazonS3::ACL_OWNER_FULL_CONTROL</code>]. 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: <ul>
	 * 	<li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
	 * 	<li><code>returnCurlHandle</code> - <code>boolean</code> - 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.</li></ul>
	 * @return CFResponse A <CFResponse> 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: <ul>
	 * 	<li><code>acl</code> - <code>string</code> - Optional - The ACL settings for the specified object. Accepts any of the following constants: [Allowed values: <code>AmazonS3::ACL_PRIVATE</code>, <code>AmazonS3::ACL_PUBLIC</code>, <code>AmazonS3::ACL_OPEN</code>, <code>AmazonS3::ACL_AUTH_READ</code>, <code>AmazonS3::ACL_OWNER_READ</code>, <code>AmazonS3::ACL_OWNER_FULL_CONTROL</code>]. Alternatively, an array of associative arrays. Each associative array contains an <code>id</code> and a <code>permission</code> key. The default value is <code>ACL_PRIVATE</code>.</li>
	 * 	<li><code>body</code> - <code>string</code> - Required; Conditional - The data to be stored in the object. Either this parameter or <code>fileUpload</code> must be specified.</li>
	 * 	<li><code>contentType</code> - <code>string</code> - Optional - The type of content that is being sent in the body. If a file is being uploaded via <code>fileUpload</code> as a file system path, it will attempt to determine the correct mime-type based on the file extension. The default value is <code>application/octet-stream</code>.</li>
	 * 	<li><code>encryption</code> - <code>string</code> - Optional - The algorithm to use for encrypting the object. [Allowed values: <code>AES256</code>]</li>
	 * 	<li><code>fileUpload</code> - <code>string|resource</code> - Required; Conditional - The URL/path for the file to upload, or an open resource. Either this parameter or <code>body</code> is required.</li>
	 * 	<li><code>headers</code> - <code>array</code> - Optional - Standard HTTP headers to send along in the request. Accepts an associative array of key-value pairs.</li>
	 * 	<li><code>length</code> - <code>integer</code> - Optional - The size of the object in bytes. For more information, see <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.13">RFC 2616, section 14.13</a>. The value can also be passed to the <code>header</code> option as <code>Content-Length</code>.</li>
	 * 	<li><code>meta</code> - <code>array</code> - Optional - An associative array of key-value pairs. Represented by <code>x-amz-meta-:</code>. 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.</li>
	 * 	<li><code>redirectTo</code> - <code>string</code> - Optional - The URI to send an HTTP 301 redirect to when accessing this object. Value must be prefixed either <code>/</code>, <code>http://</code> or <code>https://</code>.</li>
	 * 	<li><code>seekTo</code> - <code>integer</code> - Optional - The starting position in bytes within the file/stream to upload from.</li>
	 * 	<li><code>storage</code> - <code>string</code> - Optional - Whether to use Standard or Reduced Redundancy storage. [Allowed values: <code>AmazonS3::STORAGE_STANDARD</code>, <code>AmazonS3::STORAGE_REDUCED</code>]. The default value is <code>STORAGE_STANDARD</code>.</li>
	 * 	<li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
	 * 	<li><code>returnCurlHandle</code> - <code>boolean</code> - 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.</li></ul>
	 * @return CFResponse A <CFResponse> 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: <ul>
	 * 	<li><code>etag</code> - <code>string</code> - Optional - The <code>ETag</code> header passed in from a previous request. If specified, request <code>LastModified</code> option must be specified as well. Will trigger a <code>304 Not Modified</code> status code if the file hasn't changed.</li>
	 * 	<li><code>fileDownload</code> - <code>string|resource</code> - Optional - The file system location to download the file to, or an open file resource. Must be a server-writable location.</li>
	 * 	<li><code>headers</code> - <code>array</code> - Optional - Standard HTTP headers to send along in the request. Accepts an associative array of key-value pairs.</li>
	 * 	<li><code>lastmodified</code> - <code>string</code> - Optional - The <code>LastModified</code> header passed in from a previous request. If specified, request <code>ETag</code> option must be specified as well. Will trigger a <code>304 Not Modified</code> status code if the file hasn't changed.</li>
	 * 	<li><code>preauth</code> - <code>integer|string</code> - 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 <php:strtotime()>.</li>
	 * 	<li><code>range</code> - <code>string</code> - 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.</li>
	 * 	<li><code>response</code> - <code>array</code> - Optional - Allows adjustments to specific response headers. Pass an associative array where each key is one of the following: <code>cache-control</code>, <code>content-disposition</code>, <code>content-encoding</code>, <code>content-language</code>, <code>content-type</code>, <code>expires</code>. The <code>expires</code> value should use <php:gmdate()> and be formatted with the <code>DATE_RFC2822</code> constant.</li>
	 * 	<li><code>versionId</code> - <code>string</code> - Optional - The version of the object to retrieve. Version IDs are returned in the <code>x-amz-version-id</code> header of any previous object-related request.</li>
	 * 	<li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
	 * 	<li><code>returnCurlHandle</code> - <code>boolean</code> - 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.</li></ul>
	 * @return CFResponse A <CFResponse> 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: <ul>
	 * 	<li><code>versionId</code> - <code>string</code> - Optional - The version of the object to retrieve. Version IDs are returned in the <code>x-amz-version-id</code> header of any previous object-related request.</li>
	 * 	<li><code>preauth</code> - <code>integer|string</code> - 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 <php:strtotime()>.</li>
	 * 	<li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
	 * 	<li><code>returnCurlHandle</code> - <code>boolean</code> - 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.</li></ul>
	 * @return CFResponse A <CFResponse> 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: <ul>
	 * 	<li><code>versionId</code> - <code>string</code> - Optional - The version of the object to delete. Version IDs are returned in the <code>x-amz-version-id</code> header of any previous object-related request.</li>
	 * 	<li><code>MFASerial</code> - <code>string</code> - Optional - The serial number on the back of the Gemalto device. <code>MFASerial</code> and <code>MFAToken</code> must both be set for MFA to work.</li>
	 * 	<li><code>MFAToken</code> - <code>string</code> - Optional - The current token displayed on the Gemalto device. <code>MFASerial</code> and <code>MFAToken</code> must both be set for MFA to work.</li>
	 * 	<li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
	 * 	<li><code>returnCurlHandle</code> - <code>boolean</code> - 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.</li></ul>
	 * @return CFResponse A <CFResponse> 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: <ul>
	 * 	<li><code>objects</code> - <code>array</code> - Required - The object references to delete from the bucket. <ul>
	 * 		<li><code>key</code> - <code>string</code> - Required - The name of the object (e.g., the "key") to delete. This should include the entire file path including all "subdirectories".</li>
	 * 		<li><code>version_id</code> - <code>string</code> - Optional - If the object is versioned, include the version ID to delete.</li>
	 * 	</ul></li>
	 * 	<li><code>quiet</code> - <code>boolean</code> - Optional - Whether or not Amazon S3 should use "Quiet" mode for this operation. A value of <code>true</code> will enable Quiet mode. A value of <code>false</code> will use Verbose mode. The default value is <code>false</code>.</li>
	 * 	<li><code>MFASerial</code> - <code>string</code> - Optional - The serial number on the back of the Gemalto device. <code>MFASerial</code> and <code>MFAToken</code> must both be set for MFA to work.</li>
	 * 	<li><code>MFAToken</code> - <code>string</code> - Optional - The current token displayed on the Gemalto device. <code>MFASerial</code> and <code>MFAToken</code> must both be set for MFA to work.</li>
	 * 	<li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
	 * 	<li><code>returnCurlHandle</code> - <code>boolean</code> - 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.</li></ul>
	 * @return CFResponse A <CFResponse> 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: <strong>This method is paginated</strong>, and will not return more than <code>max-keys</code> keys. If you want to retrieve a list of all keys, you will need to make multiple calls to this function using the <code>marker</code> option to specify the pagination offset (the key of the last processed key--lexically ordered) and the <code>IsTruncated</code> response key to detect when all results have been processed. See: <a href="http://docs.amazonwebservices.com/AmazonS3/latest/API/index.html?RESTBucketGET.html">the S3 REST documentation for get_bucket</a> 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: <ul>
	 * 	<li><code>delimiter</code> - <code>string</code> - 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.</li>
	 * 	<li><code>marker</code> - <code>string</code> - Optional - Restricts the response to contain results that only occur alphabetically after the value of the marker.</li>
	 * 	<li><code>max-keys</code> - <code>string</code> - 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.</li>
	 * 	<li><code>preauth</code> - <code>integer|string</code> - 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 <php:strtotime()>.</li>
	 * 	<li><code>prefix</code> - <code>string</code> - Optional - Restricts the response to contain results that begin only with the specified prefix.</li>
	 * 	<li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
	 * 	<li><code>returnCurlHandle</code> - <code>boolean</code> - 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.</li></ul>
	 * @return CFResponse A <CFResponse> 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: <ul>
	 * 	<li><code>bucket</code> - <code>string</code> - Required - Specifies the name of the bucket containing the source object.</li>
	 * 	<li><code>filename</code> - <code>string</code> - Required - Specifies the file name of the source object to copy.</li></ul>
	 * @param array $dest (Required) The bucket and file name to copy to. The following keys must be set: <ul>
	 * 	<li><code>bucket</code> - <code>string</code> - Required - Specifies the name of the bucket to copy the object to.</li>
	 * 	<li><code>filename</code> - <code>string</code> - Required - Specifies the file name to copy the object to.</li></ul>
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
	 * 	<li><code>acl</code> - <code>string</code> - Optional - The ACL settings for the specified object. [Allowed values: <code>AmazonS3::ACL_PRIVATE</code>, <code>AmazonS3::ACL_PUBLIC</code>, <code>AmazonS3::ACL_OPEN</code>, <code>AmazonS3::ACL_AUTH_READ</code>, <code>AmazonS3::ACL_OWNER_READ</code>, <code>AmazonS3::ACL_OWNER_FULL_CONTROL</code>]. Alternatively, an array of associative arrays. Each associative array contains an <code>id</code> and a <code>permission</code> key. The default value is <code>ACL_PRIVATE</code>.</li>
	 * 	<li><code>encryption</code> - <code>string</code> - Optional - The algorithm to use for encrypting the object. [Allowed values: <code>AES256</code>]</li>
	 * 	<li><code>storage</code> - <code>string</code> - Optional - Whether to use Standard or Reduced Redundancy storage. [Allowed values: <code>AmazonS3::STORAGE_STANDARD</code>, <code>AmazonS3::STORAGE_REDUCED</code>]. The default value is <code>STORAGE_STANDARD</code>.</li>
	 * 	<li><code>versionId</code> - <code>string</code> - Optional - The version of the object to copy. Version IDs are returned in the <code>x-amz-version-id</code> header of any previous object-related request.</li>
	 * 	<li><code>ifMatch</code> - <code>string</code> - 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 <code>412</code> HTTP status code error (precondition failed). Used in conjunction with <code>ifUnmodifiedSince</code>.</li>
	 * 	<li><code>ifUnmodifiedSince</code> - <code>string</code> - 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 <code>412</code> HTTP status code error (precondition failed). Used in conjunction with <code>ifMatch</code>.</li>
	 * 	<li><code>ifNoneMatch</code> - <code>string</code> - 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 <code>412</code> HTTP status code error (failed condition). Used in conjunction with <code>ifModifiedSince</code>.</li>
	 * 	<li><code>ifModifiedSince</code> - <code>string</code> - 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 <code>412</code> HTTP status code error (failed condition). Used in conjunction with <code>ifNoneMatch</code>.</li>
	 * 	<li><code>headers</code> - <code>array</code> - Optional - Standard HTTP headers to send along in the request. Accepts an associative array of key-value pairs.</li>
	 * 	<li><code>meta</code> - <code>array</code> - Optional - Associative array of key-value pairs. Represented by <code>x-amz-meta-:</code> 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.</li>
	 * 	<li><code>metadataDirective</code> - <code>string</code> - Optional - Accepts either COPY or REPLACE. You will likely never need to use this, as it manages itself with no issues.</li>
	 * 	<li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
	 * 	<li><code>returnCurlHandle</code> - <code>boolean</code> - 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.</li></ul>
	 * @return CFResponse A <CFResponse> 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 <create_object()> 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: <ul>
	 * 	<li><code>acl</code> - <code>string</code> - Optional - The ACL settings for the specified object. [Allowed values: <code>AmazonS3::ACL_PRIVATE</code>, <code>AmazonS3::ACL_PUBLIC</code>, <code>AmazonS3::ACL_OPEN</code>, <code>AmazonS3::ACL_AUTH_READ</code>, <code>AmazonS3::ACL_OWNER_READ</code>, <code>AmazonS3::ACL_OWNER_FULL_CONTROL</code>]. The default value is <ACL_PRIVATE>.</li>
	 * 	<li><code>headers</code> - <code>array</code> - Optional - Standard HTTP headers to send along in the request. Accepts an associative array of key-value pairs.</li>
	 * 	<li><code>meta</code> - <code>array</code> - Optional - An associative array of key-value pairs. Any header with the <code>x-amz-meta-</code> 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.</li>
	 * 	<li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
	 * 	<li><code>returnCurlHandle</code> - <code>boolean</code> - 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.</li></ul>
	 * @return CFResponse A <CFResponse> 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: <ul>
	 * 	<li><code>versionId</code> - <code>string</code> - Optional - The version of the object to retrieve. Version IDs are returned in the <code>x-amz-version-id</code> header of any previous object-related request.</li>
	 * 	<li><code>preauth</code> - <code>integer|string</code> - 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 <php:strtotime()>.</li>
	 * 	<li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
	 * 	<li><code>returnCurlHandle</code> - <code>boolean</code> - 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.</li></ul>
	 * @return CFResponse A <CFResponse> 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: <code>AmazonS3::ACL_PRIVATE</code>, <code>AmazonS3::ACL_PUBLIC</code>, <code>AmazonS3::ACL_OPEN</code>, <code>AmazonS3::ACL_AUTH_READ</code>, <code>AmazonS3::ACL_OWNER_READ</code>, <code>AmazonS3::ACL_OWNER_FULL_CONTROL</code>]. Alternatively, an array of associative arrays. Each associative array contains an <code>id</code> and a <code>permission</code> key. The default value is <code>ACL_PRIVATE</code>.
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
	 * 	<li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
	 * 	<li><code>returnCurlHandle</code> - <code>boolean</code> - 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.</li></ul>
	 * @return CFResponse A <CFResponse> 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 <get_canonical_user_id()>.
	 * @param string $canonical_name (Required) The canonical display name for the bucket owner. This is provided as the `display_name` value from <get_canonical_user_id()>.
	 * @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 <set_vhost()> method.
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
	 * 	<li><code>preauth</code> - <code>integer|string</code> - 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 <php:strtotime()>.</li>
	 * 	<li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
	 * 	<li><code>returnCurlHandle</code> - <code>boolean</code> - 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.</li></ul>
	 * @return CFResponse A <CFResponse> 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 <set_vhost()> 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: <ul>
	 * 	<li><code>users</code> - <code>array</code> - Optional - An array of associative arrays specifying any user to give access to. Each associative array contains an <code>id</code> and <code>permission</code> value.</li>
	 * 	<li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
	 * 	<li><code>returnCurlHandle</code> - <code>boolean</code> - 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.</li></ul>
	 * @return CFResponse A <CFResponse> 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 <set_vhost()>.
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
	 * 	<li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
	 * 	<li><code>returnCurlHandle</code> - <code>boolean</code> - 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.</li></ul>
	 * @return CFResponse A <CFResponse> 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 <code>true</code> if the bucket exists, or a value of <code>false</code> 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 <code>true</code> if the object exists, or a value of <code>false</code> 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 <code>true</code> if a bucket policy exists, or a value of <code>false</code> 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 <code>true</code> will format the return value to 2 decimal points using the largest possible unit (i.e., 3.42 GB). A value of <code>false</code> 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 <code>true</code> will format the return value to 2 decimal points using the largest possible unit (i.e., 3.42 GB). A value of <code>false</code> 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: <ul>
	 * 	<li><code>curlopts</code> - <code>array</code> - Optional - A set of values to pass directly into <code>curl_setopt()</code>, where the key is a pre-defined <code>CURLOPT_*</code> constant.</li>
	 * 	<li><code>returnCurlHandle</code> - <code>boolean</code> - 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.</li></ul>
	 * @return CFResponse A <CFResponse> 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 ($meta
Download .txt
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
Download .txt
SYMBOL INDEX (270 symbols across 23 files)

FILE: addon/bucky.js
  function sendBucket (line 22) | function sendBucket() {

FILE: all_buckets.php
  class MyDB (line 65) | class MyDB extends SQLite3 {
    method __construct (line 66) | function __construct() {

FILE: dbreset.php
  class MyDB (line 3) | class MyDB extends SQLite3 {
    method __construct (line 4) | function __construct() {

FILE: lib/requestcore/requestcore.class.php
  class RequestCore (line 12) | class RequestCore
    method __construct (line 210) | public function __construct($url = null, $proxy = null, $helpers = null)
    method __destruct (line 249) | public function __destruct()
    method set_credentials (line 275) | public function set_credentials($user, $pass)
    method add_header (line 289) | public function add_header($key, $value)
    method remove_header (line 301) | public function remove_header($key)
    method set_method (line 316) | public function set_method($method)
    method set_useragent (line 328) | public function set_useragent($ua)
    method set_body (line 340) | public function set_body($body)
    method set_request_url (line 352) | public function set_request_url($url)
    method set_curlopts (line 365) | public function set_curlopts($curlopts)
    method set_read_stream_size (line 377) | public function set_read_stream_size($size)
    method set_read_stream (line 393) | public function set_read_stream($resource, $size = null)
    method set_read_file (line 421) | public function set_read_file($location)
    method set_write_stream (line 435) | public function set_write_stream($resource)
    method set_write_file (line 448) | public function set_write_file($location)
    method set_proxy (line 462) | public function set_proxy($proxy)
    method set_seek_position (line 478) | public function set_seek_position($position)
    method register_streaming_read_callback (line 503) | public function register_streaming_read_callback($callback)
    method register_streaming_write_callback (line 527) | public function register_streaming_write_callback($callback)
    method streaming_read_callback (line 546) | public function streaming_read_callback($curl_handle, $file_handle, $l...
    method streaming_write_callback (line 585) | public function streaming_write_callback($curl_handle, $data)
    method prep_request (line 618) | public function prep_request()
    method process_response (line 782) | public function process_response($curl_handle = null, $response = null)
    method send_request (line 836) | public function send_request($parse = false)
    method send_multi_request (line 872) | public function send_multi_request($handles, $opt = null)
    method get_response_header (line 968) | public function get_response_header($header = null)
    method get_response_body (line 982) | public function get_response_body()
    method get_response_code (line 992) | public function get_response_code()
  class ResponseCore (line 1002) | class ResponseCore
    method __construct (line 1027) | public function __construct($header, $body, $status = null)
    method isOK (line 1042) | public function isOK($codes = array(200, 201, 204, 206))
  class cURL_Exception (line 1053) | class cURL_Exception extends Exception {}
  class cURL_Multi_Exception (line 1054) | class cURL_Multi_Exception extends cURL_Exception {}
  class RequestCore_Exception (line 1055) | class RequestCore_Exception extends Exception {}

FILE: process.php
  function bucketsuccess (line 3) | function bucketsuccess(){
  function bucketfailed (line 39) | function bucketfailed(){

FILE: sdk.class.php
  class CFRuntime_Exception (line 24) | class CFRuntime_Exception extends Exception {}
  class Parser_Exception (line 29) | class Parser_Exception extends Exception {}
  class CFRuntime (line 65) | class CFRuntime
    method __construct (line 313) | public function __construct(array $options = array())
    method factory (line 407) | public static function factory(array $options = array())
    method __call (line 429) | public function  __call($name, $arguments)
    method set_proxy (line 452) | public function set_proxy($proxy)
    method set_hostname (line 466) | public function set_hostname($hostname, $port_number = null)
    method set_resource_prefix (line 489) | public function set_resource_prefix($prefix)
    method allow_hostname_override (line 501) | public function allow_hostname_override($override = true)
    method disable_ssl (line 516) | public function disable_ssl()
    method disable_ssl_verification (line 534) | public function disable_ssl_verification($ssl_verification = false)
    method enable_debug_mode (line 547) | public function enable_debug_mode($enabled = true)
    method set_max_retries (line 559) | public function set_max_retries($retries = 3)
    method set_cache_config (line 572) | public function set_cache_config($location, $gzip = true)
    method register_streaming_read_callback (line 635) | public function register_streaming_read_callback($callback)
    method register_streaming_write_callback (line 658) | public function register_streaming_write_callback($callback)
    method cache_sts_credentials (line 672) | public function cache_sts_credentials($cache, $options)
    method cache_instance_profile_credentials (line 711) | public function cache_instance_profile_credentials($cache, $options)
    method set_utilities_class (line 782) | public function set_utilities_class($class = 'CFUtilities')
    method set_request_class (line 798) | public function set_request_class($class = 'CFRequest')
    method set_response_class (line 813) | public function set_response_class($class = 'CFResponse')
    method set_parser_class (line 828) | public function set_parser_class($class = 'CFSimpleXML')
    method set_batch_class (line 843) | public function set_batch_class($class = 'CFBatchRequest')
    method authenticate (line 860) | public function authenticate($operation, $payload)
    method batch (line 1049) | public function batch(CFBatchRequest &$queue = null)
    method send (line 1076) | public function send($clear_after_send = true)
    method parse_callback (line 1141) | public function parse_callback($response, $headers = null)
    method cache (line 1246) | public function cache($expires)
    method cache_callback (line 1278) | public function cache_callback($operation, $payload)
    method cache_callback_batch (line 1302) | public function cache_callback_batch(CFBatchRequest $batch)
    method delete_cache (line 1312) | public function delete_cache()
  class CFLoader (line 1325) | class CFLoader
    method autoloader (line 1336) | public static function autoloader($class)

FILE: services/s3.class.php
  class S3_Exception (line 24) | class S3_Exception extends Exception {}
  class AmazonS3 (line 58) | class AmazonS3 extends CFRuntime
    method __construct (line 473) | public function __construct(array $options = array())
    method authenticate (line 508) | public function authenticate($operation, $payload)
    method validate_bucketname_create (line 1007) | public function validate_bucketname_create($bucket)
    method validate_bucketname_support (line 1034) | public function validate_bucketname_support($bucket)
    method set_region (line 1061) | public function set_region($region)
    method set_vhost (line 1093) | public function set_vhost($vhost)
    method enable_path_style (line 1105) | public function enable_path_style($style = true)
    method create_bucket (line 1131) | public function create_bucket($bucket, $region, $acl = self::ACL_PRIVA...
    method get_bucket_region (line 1208) | public function get_bucket_region($bucket, $opt = null)
    method get_bucket_headers (line 1237) | public function get_bucket_headers($bucket, $opt = null)
    method delete_bucket (line 1255) | public function delete_bucket($bucket, $force = false, $opt = null)
    method list_buckets (line 1289) | public function list_buckets($opt = null)
    method get_bucket_acl (line 1308) | public function get_bucket_acl($bucket, $opt = null)
    method set_bucket_acl (line 1330) | public function set_bucket_acl($bucket, $acl = self::ACL_PRIVATE, $opt...
    method create_object (line 1395) | public function create_object($bucket, $filename, $opt = null)
    method get_object (line 1489) | public function get_object($bucket, $filename, $opt = null)
    method get_object_headers (line 1551) | public function get_object_headers($bucket, $filename, $opt = null)
    method delete_object (line 1576) | public function delete_object($bucket, $filename, $opt = null)
    method delete_objects (line 1617) | public function delete_objects($bucket, $opt = null)
    method list_objects (line 1694) | public function list_objects($bucket, $opt = null)
    method copy_object (line 1742) | public function copy_object($source, $dest, $opt = null)
    method update_object (line 1858) | public function update_object($bucket, $filename, $opt = null)
    method get_object_acl (line 1911) | public function get_object_acl($bucket, $filename, $opt = null)
    method set_object_acl (line 1935) | public function set_object_acl($bucket, $filename, $acl = self::ACL_PR...
    method generate_access_policy (line 1987) | public function generate_access_policy($canonical_id, $canonical_name,...
    method generate_access_policy_headers (line 2049) | public function generate_access_policy_headers($users)
    method get_logs (line 2110) | public function get_logs($bucket, $opt = null)
    method enable_logging (line 2134) | public function enable_logging($bucket, $target_bucket, $target_prefix...
    method disable_logging (line 2213) | public function disable_logging($bucket, $opt = null)
    method if_bucket_exists (line 2239) | public function if_bucket_exists($bucket)
    method if_object_exists (line 2257) | public function if_object_exists($bucket, $filename)
    method if_bucket_policy_exists (line 2280) | public function if_bucket_policy_exists($bucket)
    method get_bucket_object_count (line 2305) | public function get_bucket_object_count($bucket)
    method get_bucket_filesize (line 2324) | public function get_bucket_filesize($bucket, $friendly_format = false)
    method get_object_filesize (line 2368) | public function get_object_filesize($bucket, $filename, $friendly_form...
    method change_content_type (line 2401) | public function change_content_type($bucket, $filename, $contentType, ...
    method change_storage_redundancy (line 2442) | public function change_storage_redundancy($bucket, $filename, $storage...
    method get_bucket_list (line 2477) | public function get_bucket_list($pcre = null)
    method get_object_list (line 2511) | public function get_object_list($bucket, $opt = null)
    method delete_all_objects (line 2603) | public function delete_all_objects($bucket, $pcre = self::PCRE_ALL)
    method delete_all_object_versions (line 2654) | public function delete_all_object_versions($bucket, $pcre = null)
    method get_object_metadata (line 2766) | public function get_object_metadata($bucket, $filename, $opt = null)
    method get_object_url (line 2851) | public function get_object_url($bucket, $filename, $preauth = 0, $opt ...
    method get_torrent_url (line 2898) | public function get_torrent_url($bucket, $filename, $preauth = 0)
    method enable_versioning (line 2922) | public function enable_versioning($bucket, $opt = null)
    method disable_versioning (line 2964) | public function disable_versioning($bucket, $opt = null)
    method get_versioning_status (line 3003) | public function get_versioning_status($bucket, $opt = null)
    method list_bucket_object_versions (line 3028) | public function list_bucket_object_versions($bucket, $opt = null)
    method set_bucket_policy (line 3068) | public function set_bucket_policy($bucket, CFPolicy $policy, $opt = null)
    method get_bucket_policy (line 3094) | public function get_bucket_policy($bucket, $opt = null)
    method delete_bucket_policy (line 3114) | public function delete_bucket_policy($bucket, $opt = null)
    method create_bucket_notification (line 3157) | public function create_bucket_notification($bucket, $topic_arn, $event...
    method get_bucket_notifications (line 3197) | public function get_bucket_notifications($bucket, $opt = null)
    method delete_bucket_notification (line 3217) | public function delete_bucket_notification($bucket, $opt = null)
    method get_multipart_counts (line 3240) | public function get_multipart_counts($filesize, $part_size)
    method initiate_multipart_upload (line 3276) | public function initiate_multipart_upload($bucket, $filename, $opt = n...
    method upload_part (line 3364) | public function upload_part($bucket, $filename, $upload_id, $opt = null)
    method list_parts (line 3418) | public function list_parts($bucket, $filename, $upload_id, $opt = null)
    method abort_multipart_upload (line 3452) | public function abort_multipart_upload($bucket, $filename, $upload_id,...
    method complete_multipart_upload (line 3478) | public function complete_multipart_upload($bucket, $filename, $upload_...
    method list_multipart_uploads (line 3548) | public function list_multipart_uploads($bucket, $opt = null)
    method copy_part (line 3603) | public function copy_part($source, $dest, $upload_id, $part_number, $o...
    method create_mpu_object (line 3690) | public function create_mpu_object($bucket, $filename, $opt = null)
    method abort_multipart_uploads_by_date (line 3849) | public function abort_multipart_uploads_by_date($bucket, $when = null)
    method create_website_config (line 3931) | public function create_website_config($bucket, $opt = null)
    method get_website_config (line 3964) | public function get_website_config($bucket, $opt = null)
    method delete_website_config (line 3986) | public function delete_website_config($bucket, $opt = null)
    method create_lifecycle_config (line 4028) | public function create_lifecycle_config($bucket, $opt = null)
    method get_lifecycle_config (line 4143) | public function get_lifecycle_config($bucket, $opt = null)
    method delete_lifecycle_config (line 4162) | public function delete_lifecycle_config($bucket, $opt = null)
    method restore_archived_object (line 4183) | public function restore_archived_object($bucket, $filename, $days, $op...
    method create_object_expiration_config (line 4207) | public function create_object_expiration_config($bucket, $opt = null)
    method get_object_expiration_config (line 4217) | public function get_object_expiration_config($bucket, $opt = null)
    method delete_object_expiration_config (line 4227) | public function delete_object_expiration_config($bucket, $opt = null)
    method create_bucket_tags (line 4252) | public function create_bucket_tags($bucket, $opt = null)
    method get_bucket_tags (line 4284) | public function get_bucket_tags($bucket, $opt = null)
    method delete_bucket_tags (line 4306) | public function delete_bucket_tags($bucket, $opt = null)
    method create_cors_config (line 4339) | public function create_cors_config($bucket, $opt = null)
    method get_cors_config (line 4434) | public function get_cors_config($bucket, $opt = null)
    method delete_cors_config (line 4453) | public function delete_cors_config($bucket, $opt = null)
    method get_canonical_user_id (line 4472) | public function get_canonical_user_id()
    method register_stream_wrapper (line 4493) | public function register_stream_wrapper($protocol = 's3')

FILE: utilities/array.class.php
  class CFArray (line 32) | class CFArray extends ArrayObject
    method __construct (line 42) | public function __construct($input = array(), $flags = self::STD_PROP_...
    method init (line 64) | public static function init($input = array(), $flags = self::STD_PROP_...
    method __toString (line 80) | public function __toString()
    method map_integer (line 94) | public function map_integer()
    method map_string (line 105) | public function map_string($pcre = null)
    method areOK (line 132) | public function areOK()
    method each (line 164) | public function each($callback, &$bind = null)
    method map (line 188) | public function map($callback, &$bind = null)
    method filter (line 213) | public function filter($callback, &$bind = null)
    method reduce (line 236) | public function reduce($callback, &$bind = null)
    method first (line 250) | public function first()
    method last (line 261) | public function last()
    method compress (line 272) | public function compress()
    method reindex (line 282) | public function reindex()
    method to_json (line 296) | public function to_json()
    method to_yaml (line 306) | public function to_yaml()
  class CFArray_Exception (line 312) | class CFArray_Exception extends Exception {}

FILE: utilities/batchrequest.class.php
  class CFBatchRequest_Exception (line 24) | class CFBatchRequest_Exception extends Exception {}
  class CFBatchRequest (line 40) | class CFBatchRequest extends CFRuntime
    method __construct (line 77) | public function __construct($limit = null)
    method use_credentials (line 91) | public function use_credentials(CFCredential $credentials)
    method add (line 103) | public function add($handle)
    method send (line 115) | public function send($opt = null)

FILE: utilities/complextype.class.php
  class CFComplexType (line 29) | class CFComplexType
    method json (line 39) | public static function json($json, $member = '', $default_key = '')
    method yaml (line 52) | public static function yaml($yaml, $member = '', $default_key = '')
    method map (line 65) | public static function map($map, $member = '', $default_key = '')
    method option_group (line 79) | public static function option_group($data, $member = '', $key = '', &$...

FILE: utilities/credential.class.php
  class CFCredential (line 29) | class CFCredential implements ArrayAccess
    method __get (line 43) | public function __get($name)
    method __set (line 55) | public function __set($name, $value)
    method __clone (line 66) | public function __clone()
    method __construct (line 78) | public function __construct($value = array())
    method offsetExists (line 89) | public function offsetExists($offset)
    method offsetGet (line 100) | public function offsetGet($offset)
    method offsetSet (line 117) | public function offsetSet($offset, $value)
    method offsetUnset (line 129) | public function offsetUnset($offset)
    method merge (line 141) | public function merge(CFCredential $credential)
    method to_array (line 153) | public function to_array()

FILE: utilities/credentials.class.php
  class CFCredentials (line 29) | class CFCredentials
    method __construct (line 49) | final private function __construct() {}
    method set (line 57) | public static function set(array $credential_sets)
    method get (line 111) | public static function get($credential_name = self::DEFAULT_KEY)
    method list_sets (line 128) | public static function list_sets()
  class CFCredentials_Exception (line 134) | class CFCredentials_Exception extends Exception {}

FILE: utilities/gzipdecode.class.php
  class CFGzipDecode (line 74) | class CFGzipDecode
    method __set (line 187) | public function __set($name, $value)
    method __construct (line 197) | public function __construct($data)
    method parse (line 208) | public function parse()

FILE: utilities/info.class.php
  class CFInfo (line 29) | class CFInfo
    method api_support (line 36) | public static function api_support()

FILE: utilities/json.class.php
  class CFJSON (line 29) | class CFJSON
    method to_xml (line 38) | public static function to_xml($json, $parser = 'CFSimpleXML')
  class JSON_Exception (line 89) | class JSON_Exception extends Exception {}

FILE: utilities/manifest.class.php
  class CFManifest (line 29) | class CFManifest
    method json (line 38) | public static function json($json)
    method map (line 50) | public static function map($map)

FILE: utilities/mimetypes.class.php
  class CFMimeTypes (line 29) | class CFMimeTypes
    method get_mimetype (line 219) | public static function get_mimetype($ext)

FILE: utilities/policy.class.php
  class CFPolicy (line 29) | class CFPolicy
    method __construct (line 50) | public function __construct($auth, $policy)
    method init (line 73) | public static function init($auth, $policy)
    method get_key (line 89) | public function get_key()
    method get_policy (line 99) | public function get_policy()
    method get_json (line 109) | public function get_json()
    method get_policy_signature (line 119) | public function get_policy_signature()
    method decode_policy (line 130) | public static function decode_policy($response)

FILE: utilities/request.class.php
  class CFRequest (line 29) | class CFRequest extends RequestCore
    method __construct (line 59) | public function __construct($url = null, $proxy = null, $helpers = nul...

FILE: utilities/response.class.php
  class CFResponse (line 30) | class CFResponse extends ResponseCore {}

FILE: utilities/simplexml.class.php
  class CFSimpleXML (line 33) | class CFSimpleXML extends SimpleXMLIterator
    method __call (line 57) | public function __call($name, $arguments)
    method __toString (line 92) | public function __toString()
    method init (line 107) | public static function init($data, $options = 0, $data_is_url, $ns, $i...
    method query (line 128) | public function query($expr)
    method parent (line 139) | public function parent($node = null)
    method to_string (line 162) | public function to_string()
    method to_array (line 179) | public function to_array()
    method to_stdClass (line 189) | public function to_stdClass()
    method to_json (line 199) | public function to_json()
    method to_yaml (line 209) | public function to_yaml()
    method is (line 224) | public function is($value)
    method contains (line 235) | public function contains($value)
    method matches (line 246) | public function matches($pattern)
    method starts_with (line 257) | public function starts_with($value)
    method ends_with (line 268) | public function ends_with($value)

FILE: utilities/stepconfig.class.php
  class CFStepConfig (line 29) | class CFStepConfig
    method __construct (line 43) | public function __construct($config)
    method init (line 60) | public static function init($config)
    method __toString (line 77) | public function __toString()
    method get_config (line 87) | public function get_config()

FILE: utilities/utilities.class.php
  class CFUtilities (line 29) | class CFUtilities
    method __construct (line 64) | public function __construct()
    method konst (line 76) | public function konst($class, $const)
    method hex_to_base64 (line 96) | public function hex_to_base64($str)
    method to_query_string (line 114) | public function to_query_string($array)
    method to_signable_string (line 135) | public function to_signable_string($array)
    method encode_signature2 (line 153) | public function encode_signature2($string)
    method query_to_array (line 165) | public function query_to_array($qs)
    method size_readable (line 203) | public function size_readable($size, $unit = null, $default = null)
    method time_hms (line 240) | public function time_hms($seconds)
    method try_these (line 267) | public function try_these($attrs, $base = null, $default = null)
    method json_encode (line 300) | public function json_encode($obj)
    method convert_response_to_array (line 311) | public function convert_response_to_array(ResponseCore $response)
    method convert_date_to_iso8601 (line 322) | public function convert_date_to_iso8601($datestamp)
    method is_base64 (line 339) | public function is_base64($s)
    method is_json (line 350) | public function is_json($s)
    method decode_uhex (line 361) | public function decode_uhex($s)
    method generate_guid (line 385) | public function generate_guid()
Condensed preview — 32 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (405K chars).
[
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 834,
    "preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the b"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 595,
    "preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your fea"
  },
  {
    "path": "README.md",
    "chars": 3207,
    "preview": "\n![Bucky](https://github.com/smaranchand/bucky/blob/master/bucky.gif?raw=true)\n\n# Project is on temporary hold.\n\n# Bucky"
  },
  {
    "path": "addon/bucky.js",
    "chars": 940,
    "preview": "//If you think we can improve Bucky, then suggestions are welcomed.\nvar sourcecode = document.documentElement.outerHTML;"
  },
  {
    "path": "addon/manifest.json",
    "chars": 410,
    "preview": "{\n  \"manifest_version\": 2,\n  \"name\": \"Bucky\",\n  \"version\": \"1.0\",\n\n\n  \"description\": \"Bucky discovers AWS S3 buckets fro"
  },
  {
    "path": "all_buckets.php",
    "chars": 1482,
    "preview": "<!DOCTYPE html>\n<html>\n<title>All Buckets</title>\n<style>\na {\n    color: inherit;\n    text-decoration: none;\n}\ntable {\n "
  },
  {
    "path": "config.inc.php",
    "chars": 3408,
    "preview": "<?php if (!class_exists('CFRuntime')) die('No direct access allowed.');\n/**\n * Stores your AWS account information. Add "
  },
  {
    "path": "dbreset.php",
    "chars": 707,
    "preview": "<?php\n//This script creates SQLite3 database and add tables.\n   class MyDB extends SQLite3 {\n      function __construct("
  },
  {
    "path": "index.php",
    "chars": 271,
    "preview": "<?php\nheader(\"refresh:4; url=all_buckets.php\");\n?>\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n  "
  },
  {
    "path": "lib/requestcore/requestcore.class.php",
    "chars": 30209,
    "preview": "<?php\n/**\n * Handles all HTTP requests using cURL and manages the responses.\n *\n * @version 2012.01.17\n * @copyright 200"
  },
  {
    "path": "manual_check.php",
    "chars": 1002,
    "preview": "<html>\n<title>Manual Check</title>\n<!--<body bgcolor=\"yellow\"></body>-->\n<body>\n<style>\na {\n    color: inherit;\n    text"
  },
  {
    "path": "process.php",
    "chars": 2285,
    "preview": "<?php\n//I admit that i am a noob programmer, if you do have suggestions to improve Bucky,  lets talk https://twitter.com"
  },
  {
    "path": "reset.sh",
    "chars": 39,
    "preview": "#!/bin/bash\nrm bucky.db\nphp dbreset.php"
  },
  {
    "path": "run.sh",
    "chars": 821,
    "preview": "#!/bin/bash\n#Bucky is AWS S3 bucket discovery tool developed by  https://twitter.com/smaranchand\n#Bucky Backend Engine a"
  },
  {
    "path": "sdk.class.php",
    "chars": 49523,
    "preview": "<?php\n/*\n * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache"
  },
  {
    "path": "services/s3.class.php",
    "chars": 202722,
    "preview": "<?php\n/*\n * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache"
  },
  {
    "path": "utilities/array.class.php",
    "chars": 10625,
    "preview": "<?php\n/*\n * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache"
  },
  {
    "path": "utilities/batchrequest.class.php",
    "chars": 3473,
    "preview": "<?php\n/*\n * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache"
  },
  {
    "path": "utilities/complextype.class.php",
    "chars": 4469,
    "preview": "<?php\n/*\n * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache"
  },
  {
    "path": "utilities/credential.class.php",
    "chars": 4562,
    "preview": "<?php\n/*\n * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache"
  },
  {
    "path": "utilities/credentials.class.php",
    "chars": 4070,
    "preview": "<?php\n/*\n * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache"
  },
  {
    "path": "utilities/gzipdecode.class.php",
    "chars": 9355,
    "preview": "<?php\n/*\n * Copyright 2011-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache"
  },
  {
    "path": "utilities/info.class.php",
    "chars": 1893,
    "preview": "<?php\n/*\n * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache"
  },
  {
    "path": "utilities/json.class.php",
    "chars": 2722,
    "preview": "<?php\n/*\n * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache"
  },
  {
    "path": "utilities/manifest.class.php",
    "chars": 1692,
    "preview": "<?php\n/*\n * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache"
  },
  {
    "path": "utilities/mimetypes.class.php",
    "chars": 7203,
    "preview": "<?php\n/*\n * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache"
  },
  {
    "path": "utilities/policy.class.php",
    "chars": 4034,
    "preview": "<?php\n/*\n * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache"
  },
  {
    "path": "utilities/request.class.php",
    "chars": 2630,
    "preview": "<?php\n/*\n * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache"
  },
  {
    "path": "utilities/response.class.php",
    "chars": 1119,
    "preview": "<?php\n/*\n * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache"
  },
  {
    "path": "utilities/simplexml.class.php",
    "chars": 7860,
    "preview": "<?php\n/*\n * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache"
  },
  {
    "path": "utilities/stepconfig.class.php",
    "chars": 2559,
    "preview": "<?php\n/*\n * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache"
  },
  {
    "path": "utilities/utilities.class.php",
    "chars": 10187,
    "preview": "<?php\n/*\n * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache"
  }
]

About this extraction

This page contains the full source code of the smaranchand/bucky GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 32 files (368.1 KB), approximately 102.0k tokens, and a symbol index with 270 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!