[
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nSteps to reproduce the behavior:\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n\n**Desktop (please complete the following information):**\n - OS: [e.g. iOS]\n - Browser [e.g. chrome, safari]\n - Version [e.g. 22]\n\n**Smartphone (please complete the following information):**\n - Device: [e.g. iPhone6]\n - OS: [e.g. iOS8.1]\n - Browser [e.g. stock browser, safari]\n - Version [e.g. 22]\n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": "README.md",
    "content": "\n![Bucky](https://github.com/smaranchand/bucky/blob/master/bucky.gif?raw=true)\n\n# Project is on temporary hold.\n\n# Bucky\nBucky 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.\n\n\n# Working\nBucky addon sends the details of s3 bucket name discovered from a user visited web pages to backend engine.\nIt uses [AWS PHP SDK](https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/getting-started_installation.html) to discover misconfiguration.\nUsers can also check for S3 bucket misconfiguration manually. All the results from automatic and manuall check are populated to dashboard.\n\nCheckout video https://vimeo.com/444442588\n\n# Installation\n\n```\ngit clone https://github.com/smaranchand/bucky.git\ncd bucky\n\n```\n\nRequirements: AWS Access Keys and PHP installation\n\nGet AWS Access Keys: https://console.aws.amazon.com/iam/home?#/security_credentials\n\nPHP installation: Install according to your OS,  apt install php7.3 / brew install php7.3\n\n\nCurrently, Bucky addon is not published in the Firefox addon store; as soon as the addon will be published, the addon link will be provided.\n\nFor now, users can  manually load the addon into the browser to do so\n\n1. Open Firefox browser and visit about:debugging\n2. Click on \"This Firefox\" > Load Temporary Add-on\n3. Select the addon  located at bucky/addon/bucky.js\n\nAdd AWS Access keys:\n```\ncd bucky/\nnano config.inc.php\nAdd your AWS Access Key ID and Secret Access Key. (On-Line 57 and 61)\n```\n\n\n# Usage\n\nTo use Bucky, load the Bucky addon to the browser and start backend engine.\n```\ncd bucky/\nchmod +x run.sh\n./run.sh\n\nThe backend engine runs on http://127.0.0.1:13337\nBrowse websites, Bucky will discover S3 buckets automatically and will be reflected in the dashboard.\nVisit the above address to access Bucky dashboard.\n```\n\n# Screenshots\nRunning Bucky\n\n![run_bucky](https://github.com/smaranchand/bucky/blob/master/scr/run_bucky.png?raw=true)\n\nLoading Addon\n\n![load_addon](https://github.com/smaranchand/bucky/blob/master/scr/bucky_addon.png?raw=true)\nUser Interface\n\n![dashboard](https://github.com/smaranchand/bucky/blob/master/scr/dashboard_loading.png?raw=true)\n\nAll Buckets\n\n![all_buckets](https://github.com/smaranchand/bucky/blob/master/scr/all_buckets.png?raw=true)\nManual Check\n\n![manual_check](https://github.com/smaranchand/bucky/blob/master/scr/manual_check.png?raw=true)\n\nPOC By Bucky\n\n![Bucky_POC](https://github.com/smaranchand/bucky/blob/master/scr/vulnerable_poc.png?raw=true)\n\n\n\n# Note\nBucky 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.\n\n\n\n\n"
  },
  {
    "path": "addon/bucky.js",
    "content": "//If you think we can improve Bucky, then suggestions are welcomed.\nvar sourcecode = document.documentElement.outerHTML;\nvar pageurl= document.URL;\nvar hostname= window.location.hostname;\nvar s3bucketurl= sourcecode.match(/https:\\/\\/[a-z0-9-.]{3,63}\\.s3\\.([a-z0-9-]{5,19}\\.|)amazonaws\\.com/g);\nif (s3bucketurl==null)\n{\nvar s3bucketname= sourcecode.match(/(?<=\\/\\/s3\\.amazonaws.com\\/)[a-z0-9-.]{3,63}(?=\\/)/);\nif (s3bucketname==null)\n{\n}\nelse\nsendBucket();\n\n}\nelse\n{\n  var s3bucketname= String(s3bucketurl[0]).match(/(?<=\\/\\/).*(?=\\.s3)/);\n  sendBucket();\n}\n\nfunction sendBucket() {\n\n  var xhr = new XMLHttpRequest();\n    xhr.open(\"POST\", \"http://127.0.0.1:13337/process.php\", true);\n    xhr.setRequestHeader(\"Accept-Language\", \"en-US,en;q=0.5\");\n    xhr.setRequestHeader(\"Content-Type\", \"application/x-www-form-urlencoded\");\n    var body = (\"bucketname=\"+s3bucketname+\"&sourceurl=\"+pageurl+\"&hostname=\"+hostname);\n    xhr.send(body);\n    \n}"
  },
  {
    "path": "addon/manifest.json",
    "content": "{\n  \"manifest_version\": 2,\n  \"name\": \"Bucky\",\n  \"version\": \"1.0\",\n\n\n  \"description\": \"Bucky discovers AWS S3 buckets from the web pages and sends it to the backend engine to check for misconfiguration.\",\n\n  \"icons\": {\n    \"48\": \"bucky.png\"\n  },\n\n  \"content_scripts\": [\n    {\n      \"matches\": [\"*://*/*\"],\n      \"js\": [\"bucky.js\"]\n    }\n  ],\n\n  \"permissions\": [\n    \"http://127.0.0.1/*\",\n    \"webRequest\"\n  ]\n}\n"
  },
  {
    "path": "all_buckets.php",
    "content": "<!DOCTYPE html>\n<html>\n<title>All Buckets</title>\n<style>\na {\n    color: inherit;\n    text-decoration: none;\n}\ntable {\n  font-family: arial, sans-serif;\n  border-collapse: collapse;\n  width: 100%;\n}\n\ntd, th {\n  border: 2px solid #0dbce7;\n  text-align: left;\n  padding: 8px;\n}\n\n.newcls{\n   \n \n background-color:#0dbce7;\n  \n border: #2e6da4;\n  \n font-family: Arial, Helvetica,  sans-serif;\n  \n font-size: 15px;\n  \n color: #fff;\n  \n letter-spacing: 1px;\n  \n padding: 8px 12px;\n  \n font-size: 14px;\n  \n font-weight: normal;\n  \n border-radius: 4px;\n  \n line-height: 1.5;\n  \n text-decoration:none\n  \n }\n</style>\n\n<div class=\"newcls\">\n\n<h1> All Buckets |  <a href=../manual_check.php>Check Manually</a></h1></div>\n<br>\n<table border=1>\n<tr>\n<th>SN</th>\n<th>Bucket Name</th>\n<th>Source URL</th>\n<th>IP</th>\n<th>POC</th>\n<th>VULNERABLE</th>\n</tr>\n<?php\n   class MyDB extends SQLite3 {\n      function __construct() {\n         $this->open('bucky.db');\n      }\n   }\n   \n   $db = new MyDB();\n   if(!$db) {\n      echo $db->lastErrorMsg();\n   } else {\n\n   }\n\n   $sql =<<<EOF\n      SELECT * from BUCKY;\nEOF;\n\n   $ret = $db->query($sql);\n   while($row = $ret->fetchArray(SQLITE3_ASSOC) ) {\n      echo \"<tr>\";\n      echo \"<td>\". $row['ID'] . \"</td>\";\n      echo \"<td>\". $row['BUCKETNAME'] . \"</td>\";\n      echo \"<td>\". $row['URL'] .\"</td>\";\n      echo \"<td>\". $row['IP'] .\"</td>\";\n      echo \"<td><a>\". $row['POC'] .\"</a></td>\";\n      echo \"<td>\".$row['VULNERABLE'] .\"</td>\";\n   }\n   $db->close();\n?>"
  },
  {
    "path": "config.inc.php",
    "content": "<?php if (!class_exists('CFRuntime')) die('No direct access allowed.');\n/**\n * Stores your AWS account information. Add your account information, and then rename this file\n * to 'config.inc.php'.\n *\n * @version 2011.12.14\n * @license See the included NOTICE.md file for more information.\n * @copyright See the included NOTICE.md file for more information.\n * @link http://aws.amazon.com/php/ PHP Developer Center\n * @link http://aws.amazon.com/security-credentials AWS Security Credentials\n */\n\n\n/*###################################################################################################\n\n\tAs of version 1.5, the AWS SDK for PHP uses the CFCredentials class to handle credentials.\n\tThis class has the advantage of being able to support multiple sets of credentials at a time,\n\tincluding the ability for credential sets to inherit settings from other credential sets.\n\n\tSome example uses are noted at https://gist.github.com/1478912\n\n\tNotes:\n\n\t* You can define one or more credential sets.\n\n\t* Credential sets can be named anything that PHP allows for an associative array key;\n\t  \"production\", \"staging\", etc., are just sample values. Feel free to rename them.\n\n\t* A credential set only has four required entries: key, secret, default_cache_config and\n\t  certificate_authority. Aside from these, you can add any additional bits of information\n\t  you'd like to keep easily accessible (e.g., multi-factor authentication device key, your\n\t  AWS Account ID, your canonical identifiers).\n\n\t* Additional credential sets can inherit the properties of another credential set using the\n\t  @inherit keyword.\n\n\t* If more than one credential set is provided, a default credential set must be specified\n\t  using the @default keyword.\n\n\t* If you only have one credential set, you can set it to the @default keyword.\n\n\t* View the documentation for the CFCredentials::set() method to view usage examples.\n\n###################################################################################################*/\n\n\n/**\n * Create a list of credential sets that can be used with the SDK.\n */\nCFCredentials::set(array(\n\n\t// Credentials for the development environment.\n\t'development' => array(\n\n\t\t// Amazon Web Services Key. Found in the AWS Security Credentials. You can also pass\n\t\t// this value as the first parameter to a service constructor.\n\t\t'key' => 'YOUR_AWS_KEY',\n\n\t\t// Amazon Web Services Secret Key. Found in the AWS Security Credentials. You can also\n\t\t// pass this value as the second parameter to a service constructor.\n\t\t'secret' => 'YOUR_SECRET',\n\n\t\t// This option allows you to configure a preferred storage type to use for caching by\n\t\t// default. This can be changed later using the set_cache_config() method.\n\t\t//\n\t\t// Valid values are: `apc`, `xcache`, or a file system path such as `./cache` or\n\t\t// `/tmp/cache/`.\n\t\t'default_cache_config' => '',\n\n\t\t// Determines which Cerificate Authority file to use.\n\t\t//\n\t\t// A value of boolean `false` will use the Certificate Authority file available on the\n\t\t// system. A value of boolean `true` will use the Certificate Authority provided by the\n\t\t// SDK. Passing a file system path to a Certificate Authority file (chmodded to `0755`)\n\t\t// will use that.\n\t\t//\n\t\t// Leave this set to `false` if you're not sure.\n\t\t'certificate_authority' => false\n\t),\n\n\t// Specify a default credential set to use if there are more than one.\n\t'@default' => 'development'\n));\n"
  },
  {
    "path": "dbreset.php",
    "content": "<?php\n//This script creates SQLite3 database and add tables.\n   class MyDB extends SQLite3 {\n      function __construct() {\n         $this->open('bucky.db');\n      }\n   }\n   $db = new MyDB();\n   if(!$db) {\n      echo $db->lastErrorMsg();\n   } else {\n   $sql =<<<EOF\n      CREATE TABLE BUCKY\n      (ID INTEGER PRIMARY KEY AUTOINCREMENT,\n      BUCKETNAME           VARCHAR    NOT NULL,\n      URL                  VARCHAR    NOT NULL,\n      IP            VARCHAR     NOT NULL,\n      POC        VARCHAR     NOT NULL,\n      VULNERABLE         VARCHAR);\nEOF;\n\n   $ret = $db->exec($sql);\n   if(!$ret){\n      echo $db->lastErrorMsg();\n   } else {\n      echo \"Database setup successful.\";\n   }\n   $db->close();\n}\n?>\n"
  },
  {
    "path": "index.php",
    "content": "<?php\nheader(\"refresh:4; url=all_buckets.php\");\n?>\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title>Bucky</title>\n</head>\n<body>\n<style>\n.loading{\ndisplay:block;\nmargin: 0 auto;\n}\n</style>\n<img src=\"bucky.gif\" class=\"loading\">\n</body>\n</html>"
  },
  {
    "path": "lib/requestcore/requestcore.class.php",
    "content": "<?php\n/**\n * Handles all HTTP requests using cURL and manages the responses.\n *\n * @version 2012.01.17\n * @copyright 2006-2011 Ryan Parman\n * @copyright 2006-2010 Foleeo Inc.\n * @copyright 2010-2011 Amazon.com, Inc. or its affiliates.\n * @copyright 2008-2011 Contributors\n * @license http://opensource.org/licenses/bsd-license.php Simplified BSD License\n */\nclass RequestCore\n{\n\t/**\n\t * The URL being requested.\n\t */\n\tpublic $request_url;\n\n\t/**\n\t * The headers being sent in the request.\n\t */\n\tpublic $request_headers;\n\n\t/**\n\t * The body being sent in the request.\n\t */\n\tpublic $request_body;\n\n\t/**\n\t * The response returned by the request.\n\t */\n\tpublic $response;\n\n\t/**\n\t * The headers returned by the request.\n\t */\n\tpublic $response_headers;\n\n\t/**\n\t * The body returned by the request.\n\t */\n\tpublic $response_body;\n\n\t/**\n\t * The HTTP status code returned by the request.\n\t */\n\tpublic $response_code;\n\n\t/**\n\t * Additional response data.\n\t */\n\tpublic $response_info;\n\n\t/**\n\t * The handle for the cURL object.\n\t */\n\tpublic $curl_handle;\n\n\t/**\n\t * The method by which the request is being made.\n\t */\n\tpublic $method;\n\n\t/**\n\t * Stores the proxy settings to use for the request.\n\t */\n\tpublic $proxy = null;\n\n\t/**\n\t * The username to use for the request.\n\t */\n\tpublic $username = null;\n\n\t/**\n\t * The password to use for the request.\n\t */\n\tpublic $password = null;\n\n\t/**\n\t * Custom CURLOPT settings.\n\t */\n\tpublic $curlopts = null;\n\n\t/**\n\t * The state of debug mode.\n\t */\n\tpublic $debug_mode = false;\n\n\t/**\n\t * The default class to use for HTTP Requests (defaults to <RequestCore>).\n\t */\n\tpublic $request_class = 'RequestCore';\n\n\t/**\n\t * The default class to use for HTTP Responses (defaults to <ResponseCore>).\n\t */\n\tpublic $response_class = 'ResponseCore';\n\n\t/**\n\t * Default useragent string to use.\n\t */\n\tpublic $useragent = 'RequestCore/1.4.4';\n\n\t/**\n\t * File to read from while streaming up.\n\t */\n\tpublic $read_file = null;\n\n\t/**\n\t * The resource to read from while streaming up.\n\t */\n\tpublic $read_stream = null;\n\n\t/**\n\t * The size of the stream to read from.\n\t */\n\tpublic $read_stream_size = null;\n\n\t/**\n\t * The length already read from the stream.\n\t */\n\tpublic $read_stream_read = 0;\n\n\t/**\n\t * File to write to while streaming down.\n\t */\n\tpublic $write_file = null;\n\n\t/**\n\t * The resource to write to while streaming down.\n\t */\n\tpublic $write_stream = null;\n\n\t/**\n\t * Stores the intended starting seek position.\n\t */\n\tpublic $seek_position = null;\n\n\t/**\n\t * The location of the cacert.pem file to use.\n\t */\n\tpublic $cacert_location = false;\n\n\t/**\n\t * The state of SSL certificate verification.\n\t */\n\tpublic $ssl_verification = true;\n\n\t/**\n\t * The user-defined callback function to call when a stream is read from.\n\t */\n\tpublic $registered_streaming_read_callback = null;\n\n\t/**\n\t * The user-defined callback function to call when a stream is written to.\n\t */\n\tpublic $registered_streaming_write_callback = null;\n\n\t/**\n\t * Whether or not the set_time_limit function should be called.\n\t */\n\tpublic $allow_set_time_limit = true;\n\n\t/**\n\t * Whether or not to use gzip encoding via CURLOPT_ENCODING\n\t */\n\tpublic $use_gzip_enconding = true;\n\n\n\t/*%******************************************************************************************%*/\n\t// CONSTANTS\n\n\t/**\n\t * GET HTTP Method\n\t */\n\tconst HTTP_GET = 'GET';\n\n\t/**\n\t * POST HTTP Method\n\t */\n\tconst HTTP_POST = 'POST';\n\n\t/**\n\t * PUT HTTP Method\n\t */\n\tconst HTTP_PUT = 'PUT';\n\n\t/**\n\t * DELETE HTTP Method\n\t */\n\tconst HTTP_DELETE = 'DELETE';\n\n\t/**\n\t * HEAD HTTP Method\n\t */\n\tconst HTTP_HEAD = 'HEAD';\n\n\n\t/*%******************************************************************************************%*/\n\t// CONSTRUCTOR/DESTRUCTOR\n\n\t/**\n\t * Constructs a new instance of this class.\n\t *\n\t * @param string $url (Optional) The URL to request or service endpoint to query.\n\t * @param string $proxy (Optional) The faux-url to use for proxy settings. Takes the following format: `proxy://user:pass@hostname:port`\n\t * @param array $helpers (Optional) An associative array of classnames to use for request, and response functionality. Gets passed in automatically by the calling class.\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function __construct($url = null, $proxy = null, $helpers = null)\n\t{\n\t\t// Set some default values.\n\t\t$this->request_url = $url;\n\t\t$this->method = self::HTTP_GET;\n\t\t$this->request_headers = array();\n\t\t$this->request_body = '';\n\n\t\t// Determine if set_time_limit can be called\n\t\tif (strpos(ini_get('disable_functions'), 'set_time_limit') !== false)\n\t\t{\n\t\t\t$this->allow_set_time_limit = false;\n\t\t}\n\n\t\t// Set a new Request class if one was set.\n\t\tif (isset($helpers['request']) && !empty($helpers['request']))\n\t\t{\n\t\t\t$this->request_class = $helpers['request'];\n\t\t}\n\n\t\t// Set a new Request class if one was set.\n\t\tif (isset($helpers['response']) && !empty($helpers['response']))\n\t\t{\n\t\t\t$this->response_class = $helpers['response'];\n\t\t}\n\n\t\tif ($proxy)\n\t\t{\n\t\t\t$this->set_proxy($proxy);\n\t\t}\n\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Destructs the instance. Closes opened file handles.\n\t *\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function __destruct()\n\t{\n\t\tif (isset($this->read_file) && isset($this->read_stream))\n\t\t{\n\t\t\tfclose($this->read_stream);\n\t\t}\n\n\t\tif (isset($this->write_file) && isset($this->write_stream))\n\t\t{\n\t\t\tfclose($this->write_stream);\n\t\t}\n\n\t\treturn $this;\n\t}\n\n\n\t/*%******************************************************************************************%*/\n\t// REQUEST METHODS\n\n\t/**\n\t * Sets the credentials to use for authentication.\n\t *\n\t * @param string $user (Required) The username to authenticate with.\n\t * @param string $pass (Required) The password to authenticate with.\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function set_credentials($user, $pass)\n\t{\n\t\t$this->username = $user;\n\t\t$this->password = $pass;\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Adds a custom HTTP header to the cURL request.\n\t *\n\t * @param string $key (Required) The custom HTTP header to set.\n\t * @param mixed $value (Required) The value to assign to the custom HTTP header.\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function add_header($key, $value)\n\t{\n\t\t$this->request_headers[$key] = $value;\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Removes an HTTP header from the cURL request.\n\t *\n\t * @param string $key (Required) The custom HTTP header to set.\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function remove_header($key)\n\t{\n\t\tif (isset($this->request_headers[$key]))\n\t\t{\n\t\t\tunset($this->request_headers[$key]);\n\t\t}\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Set the method type for the request.\n\t *\n\t * @param string $method (Required) One of the following constants: <HTTP_GET>, <HTTP_POST>, <HTTP_PUT>, <HTTP_HEAD>, <HTTP_DELETE>.\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function set_method($method)\n\t{\n\t\t$this->method = strtoupper($method);\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Sets a custom useragent string for the class.\n\t *\n\t * @param string $ua (Required) The useragent string to use.\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function set_useragent($ua)\n\t{\n\t\t$this->useragent = $ua;\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Set the body to send in the request.\n\t *\n\t * @param string $body (Required) The textual content to send along in the body of the request.\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function set_body($body)\n\t{\n\t\t$this->request_body = $body;\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Set the URL to make the request to.\n\t *\n\t * @param string $url (Required) The URL to make the request to.\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function set_request_url($url)\n\t{\n\t\t$this->request_url = $url;\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Set additional CURLOPT settings. These will merge with the default settings, and override if\n\t * there is a duplicate.\n\t *\n\t * @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.\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function set_curlopts($curlopts)\n\t{\n\t\t$this->curlopts = $curlopts;\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Sets the length in bytes to read from the stream while streaming up.\n\t *\n\t * @param integer $size (Required) The length in bytes to read from the stream.\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function set_read_stream_size($size)\n\t{\n\t\t$this->read_stream_size = $size;\n\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Sets the resource to read from while streaming up. Reads the stream from its current position until\n\t * EOF or `$size` bytes have been read. If `$size` is not given it will be determined by <php:fstat()> and\n\t * <php:ftell()>.\n\t *\n\t * @param resource $resource (Required) The readable resource to read from.\n\t * @param integer $size (Optional) The size of the stream to read.\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function set_read_stream($resource, $size = null)\n\t{\n\t\tif (!isset($size) || $size < 0)\n\t\t{\n\t\t\t$stats = fstat($resource);\n\n\t\t\tif ($stats && $stats['size'] >= 0)\n\t\t\t{\n\t\t\t\t$position = ftell($resource);\n\n\t\t\t\tif ($position !== false && $position >= 0)\n\t\t\t\t{\n\t\t\t\t\t$size = $stats['size'] - $position;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t$this->read_stream = $resource;\n\n\t\treturn $this->set_read_stream_size($size);\n\t}\n\n\t/**\n\t * Sets the file to read from while streaming up.\n\t *\n\t * @param string $location (Required) The readable location to read from.\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function set_read_file($location)\n\t{\n\t\t$this->read_file = $location;\n\t\t$read_file_handle = fopen($location, 'r');\n\n\t\treturn $this->set_read_stream($read_file_handle);\n\t}\n\n\t/**\n\t * Sets the resource to write to while streaming down.\n\t *\n\t * @param resource $resource (Required) The writeable resource to write to.\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function set_write_stream($resource)\n\t{\n\t\t$this->write_stream = $resource;\n\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Sets the file to write to while streaming down.\n\t *\n\t * @param string $location (Required) The writeable location to write to.\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function set_write_file($location)\n\t{\n\t\t$this->write_file = $location;\n\t\t$write_file_handle = fopen($location, 'w');\n\n\t\treturn $this->set_write_stream($write_file_handle);\n\t}\n\n\t/**\n\t * Set the proxy to use for making requests.\n\t *\n\t * @param string $proxy (Required) The faux-url to use for proxy settings. Takes the following format: `proxy://user:pass@hostname:port`\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function set_proxy($proxy)\n\t{\n\t\t$proxy = parse_url($proxy);\n\t\t$proxy['user'] = isset($proxy['user']) ? $proxy['user'] : null;\n\t\t$proxy['pass'] = isset($proxy['pass']) ? $proxy['pass'] : null;\n\t\t$proxy['port'] = isset($proxy['port']) ? $proxy['port'] : null;\n\t\t$this->proxy = $proxy;\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Set the intended starting seek position.\n\t *\n\t * @param integer $position (Required) The byte-position of the stream to begin reading from.\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function set_seek_position($position)\n\t{\n\t\t$this->seek_position = isset($position) ? (integer) $position : null;\n\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Register a callback function to execute whenever a data stream is read from using\n\t * <CFRequest::streaming_read_callback()>.\n\t *\n\t * The user-defined callback function should accept three arguments:\n\t *\n\t * <ul>\n\t * \t<li><code>$curl_handle</code> - <code>resource</code> - Required - The cURL handle resource that represents the in-progress transfer.</li>\n\t * \t<li><code>$file_handle</code> - <code>resource</code> - Required - The file handle resource that represents the file on the local file system.</li>\n\t * \t<li><code>$length</code> - <code>integer</code> - Required - The length in kilobytes of the data chunk that was transferred.</li>\n\t * </ul>\n\t *\n\t * @param string|array|function $callback (Required) The callback function is called by <php:call_user_func()>, so you can pass the following values: <ul>\n\t * \t<li>The name of a global function to execute, passed as a string.</li>\n\t * \t<li>A method to execute, passed as <code>array('ClassName', 'MethodName')</code>.</li>\n\t * \t<li>An anonymous function (PHP 5.3+).</li></ul>\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function register_streaming_read_callback($callback)\n\t{\n\t\t$this->registered_streaming_read_callback = $callback;\n\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Register a callback function to execute whenever a data stream is written to using\n\t * <CFRequest::streaming_write_callback()>.\n\t *\n\t * The user-defined callback function should accept two arguments:\n\t *\n\t * <ul>\n\t * \t<li><code>$curl_handle</code> - <code>resource</code> - Required - The cURL handle resource that represents the in-progress transfer.</li>\n\t * \t<li><code>$length</code> - <code>integer</code> - Required - The length in kilobytes of the data chunk that was transferred.</li>\n\t * </ul>\n\t *\n\t * @param string|array|function $callback (Required) The callback function is called by <php:call_user_func()>, so you can pass the following values: <ul>\n\t * \t<li>The name of a global function to execute, passed as a string.</li>\n\t * \t<li>A method to execute, passed as <code>array('ClassName', 'MethodName')</code>.</li>\n\t * \t<li>An anonymous function (PHP 5.3+).</li></ul>\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function register_streaming_write_callback($callback)\n\t{\n\t\t$this->registered_streaming_write_callback = $callback;\n\n\t\treturn $this;\n\t}\n\n\n\t/*%******************************************************************************************%*/\n\t// PREPARE, SEND, AND PROCESS REQUEST\n\n\t/**\n\t * A callback function that is invoked by cURL for streaming up.\n\t *\n\t * @param resource $curl_handle (Required) The cURL handle for the request.\n\t * @param resource $file_handle (Required) The open file handle resource.\n\t * @param integer $length (Required) The maximum number of bytes to read.\n\t * @return binary Binary data from a stream.\n\t */\n\tpublic function streaming_read_callback($curl_handle, $file_handle, $length)\n\t{\n\t\t// Once we've sent as much as we're supposed to send...\n\t\tif ($this->read_stream_read >= $this->read_stream_size)\n\t\t{\n\t\t\t// Send EOF\n\t\t\treturn '';\n\t\t}\n\n\t\t// If we're at the beginning of an upload and need to seek...\n\t\tif ($this->read_stream_read == 0 && isset($this->seek_position) && $this->seek_position !== ftell($this->read_stream))\n\t\t{\n\t\t\tif (fseek($this->read_stream, $this->seek_position) !== 0)\n\t\t\t{\n\t\t\t\tthrow new RequestCore_Exception('The stream does not support seeking and is either not at the requested position or the position is unknown.');\n\t\t\t}\n\t\t}\n\n\t\t$read = fread($this->read_stream, min($this->read_stream_size - $this->read_stream_read, $length)); // Remaining upload data or cURL's requested chunk size\n\t\t$this->read_stream_read += strlen($read);\n\n\t\t$out = $read === false ? '' : $read;\n\n\t\t// Execute callback function\n\t\tif ($this->registered_streaming_read_callback)\n\t\t{\n\t\t\tcall_user_func($this->registered_streaming_read_callback, $curl_handle, $file_handle, $out);\n\t\t}\n\n\t\treturn $out;\n\t}\n\n\t/**\n\t * A callback function that is invoked by cURL for streaming down.\n\t *\n\t * @param resource $curl_handle (Required) The cURL handle for the request.\n\t * @param binary $data (Required) The data to write.\n\t * @return integer The number of bytes written.\n\t */\n\tpublic function streaming_write_callback($curl_handle, $data)\n\t{\n\t\t$length = strlen($data);\n\t\t$written_total = 0;\n\t\t$written_last = 0;\n\n\t\twhile ($written_total < $length)\n\t\t{\n\t\t\t$written_last = fwrite($this->write_stream, substr($data, $written_total));\n\n\t\t\tif ($written_last === false)\n\t\t\t{\n\t\t\t\treturn $written_total;\n\t\t\t}\n\n\t\t\t$written_total += $written_last;\n\t\t}\n\n\t\t// Execute callback function\n\t\tif ($this->registered_streaming_write_callback)\n\t\t{\n\t\t\tcall_user_func($this->registered_streaming_write_callback, $curl_handle, $written_total);\n\t\t}\n\n\t\treturn $written_total;\n\t}\n\n\t/**\n\t * Prepares and adds the details of the cURL request. This can be passed along to a <php:curl_multi_exec()>\n\t * function.\n\t *\n\t * @return resource The handle for the cURL object.\n\t */\n\tpublic function prep_request()\n\t{\n\t\t$curl_handle = curl_init();\n\n\t\t// Set default options.\n\t\tcurl_setopt($curl_handle, CURLOPT_URL, $this->request_url);\n\t\tcurl_setopt($curl_handle, CURLOPT_FILETIME, true);\n\t\tcurl_setopt($curl_handle, CURLOPT_FRESH_CONNECT, false);\n\t\tcurl_setopt($curl_handle, CURLOPT_CLOSEPOLICY, CURLCLOSEPOLICY_LEAST_RECENTLY_USED);\n\t\tcurl_setopt($curl_handle, CURLOPT_MAXREDIRS, 5);\n\t\tcurl_setopt($curl_handle, CURLOPT_HEADER, true);\n\t\tcurl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, true);\n\t\tcurl_setopt($curl_handle, CURLOPT_TIMEOUT, 5184000);\n\t\tcurl_setopt($curl_handle, CURLOPT_CONNECTTIMEOUT, 120);\n\t\tcurl_setopt($curl_handle, CURLOPT_NOSIGNAL, true);\n\t\tcurl_setopt($curl_handle, CURLOPT_REFERER, $this->request_url);\n\t\tcurl_setopt($curl_handle, CURLOPT_USERAGENT, $this->useragent);\n\t\tcurl_setopt($curl_handle, CURLOPT_READFUNCTION, array($this, 'streaming_read_callback'));\n\n\t\t// Verification of the SSL cert\n\t\tif ($this->ssl_verification)\n\t\t{\n\t\t\tcurl_setopt($curl_handle, CURLOPT_SSL_VERIFYPEER, true);\n\t\t\tcurl_setopt($curl_handle, CURLOPT_SSL_VERIFYHOST, 2);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tcurl_setopt($curl_handle, CURLOPT_SSL_VERIFYPEER, false);\n\t\t\tcurl_setopt($curl_handle, CURLOPT_SSL_VERIFYHOST, false);\n\t\t}\n\n\t\t// chmod the file as 0755\n\t\tif ($this->cacert_location === true)\n\t\t{\n\t\t\tcurl_setopt($curl_handle, CURLOPT_CAINFO, dirname(__FILE__) . '/cacert.pem');\n\t\t}\n\t\telseif (is_string($this->cacert_location))\n\t\t{\n\t\t\tcurl_setopt($curl_handle, CURLOPT_CAINFO, $this->cacert_location);\n\t\t}\n\n\t\t// Debug mode\n\t\tif ($this->debug_mode)\n\t\t{\n\t\t\tcurl_setopt($curl_handle, CURLOPT_VERBOSE, true);\n\t\t}\n\n\t\t// Handle open_basedir & safe mode\n\t\tif (!ini_get('safe_mode') && !ini_get('open_basedir'))\n\t\t{\n\t\t\tcurl_setopt($curl_handle, CURLOPT_FOLLOWLOCATION, true);\n\t\t}\n\n\t\t// Enable a proxy connection if requested.\n\t\tif ($this->proxy)\n\t\t{\n\t\t\tcurl_setopt($curl_handle, CURLOPT_HTTPPROXYTUNNEL, true);\n\n\t\t\t$host = $this->proxy['host'];\n\t\t\t$host .= ($this->proxy['port']) ? ':' . $this->proxy['port'] : '';\n\t\t\tcurl_setopt($curl_handle, CURLOPT_PROXY, $host);\n\n\t\t\tif (isset($this->proxy['user']) && isset($this->proxy['pass']))\n\t\t\t{\n\t\t\t\tcurl_setopt($curl_handle, CURLOPT_PROXYUSERPWD, $this->proxy['user'] . ':' . $this->proxy['pass']);\n\t\t\t}\n\t\t}\n\n\t\t// Set credentials for HTTP Basic/Digest Authentication.\n\t\tif ($this->username && $this->password)\n\t\t{\n\t\t\tcurl_setopt($curl_handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY);\n\t\t\tcurl_setopt($curl_handle, CURLOPT_USERPWD, $this->username . ':' . $this->password);\n\t\t}\n\n\t\t// Handle the encoding if we can.\n\t\tif ($this->use_gzip_enconding && extension_loaded('zlib'))\n\t\t{\n\t\t\tcurl_setopt($curl_handle, CURLOPT_ENCODING, 'gzip, deflate');\n\t\t}\n\n\t\t// Process custom headers\n\t\tif (isset($this->request_headers) && count($this->request_headers))\n\t\t{\n\t\t\t$temp_headers = array();\n\n\t\t\tif (!array_key_exists('Expect', $this->request_headers))\n\t\t\t{\n\t\t\t\t$this->request_headers['Expect'] = '';\n\t\t\t}\n\n\t\t\tforeach ($this->request_headers as $k => $v)\n\t\t\t{\n\t\t\t\t$temp_headers[] = $k . ': ' . $v;\n\t\t\t}\n\n\t\t\tcurl_setopt($curl_handle, CURLOPT_HTTPHEADER, $temp_headers);\n\t\t}\n\n\t\tswitch ($this->method)\n\t\t{\n\t\t\tcase self::HTTP_PUT:\n\t\t\t\tcurl_setopt($curl_handle, CURLOPT_CUSTOMREQUEST, 'PUT');\n\t\t\t\tif (isset($this->read_stream))\n\t\t\t\t{\n\t\t\t\t\tif (!isset($this->read_stream_size) || $this->read_stream_size < 0)\n\t\t\t\t\t{\n\t\t\t\t\t\tthrow new RequestCore_Exception('The stream size for the streaming upload cannot be determined.');\n\t\t\t\t\t}\n\n\t\t\t\t\tcurl_setopt($curl_handle, CURLOPT_INFILESIZE, $this->read_stream_size);\n\t\t\t\t\tcurl_setopt($curl_handle, CURLOPT_UPLOAD, true);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tcurl_setopt($curl_handle, CURLOPT_POSTFIELDS, $this->request_body);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase self::HTTP_POST:\n\t\t\t\tcurl_setopt($curl_handle, CURLOPT_POST, true);\n\t\t\t\tcurl_setopt($curl_handle, CURLOPT_POSTFIELDS, $this->request_body);\n\t\t\t\tbreak;\n\n\t\t\tcase self::HTTP_HEAD:\n\t\t\t\tcurl_setopt($curl_handle, CURLOPT_CUSTOMREQUEST, self::HTTP_HEAD);\n\t\t\t\tcurl_setopt($curl_handle, CURLOPT_NOBODY, 1);\n\t\t\t\tbreak;\n\n\t\t\tdefault: // Assumed GET\n\t\t\t\tcurl_setopt($curl_handle, CURLOPT_CUSTOMREQUEST, $this->method);\n\t\t\t\tif (isset($this->write_stream))\n\t\t\t\t{\n\t\t\t\t\tcurl_setopt($curl_handle, CURLOPT_WRITEFUNCTION, array($this, 'streaming_write_callback'));\n\t\t\t\t\tcurl_setopt($curl_handle, CURLOPT_HEADER, false);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tcurl_setopt($curl_handle, CURLOPT_POSTFIELDS, $this->request_body);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\n\t\t// Merge in the CURLOPTs\n\t\tif (isset($this->curlopts) && sizeof($this->curlopts) > 0)\n\t\t{\n\t\t\tforeach ($this->curlopts as $k => $v)\n\t\t\t{\n\t\t\t\tcurl_setopt($curl_handle, $k, $v);\n\t\t\t}\n\t\t}\n\n\t\treturn $curl_handle;\n\t}\n\n\t/**\n\t * Take the post-processed cURL data and break it down into useful header/body/info chunks. Uses the\n\t * data stored in the `curl_handle` and `response` properties unless replacement data is passed in via\n\t * parameters.\n\t *\n\t * @param resource $curl_handle (Optional) The reference to the already executed cURL request.\n\t * @param string $response (Optional) The actual response content itself that needs to be parsed.\n\t * @return ResponseCore A <ResponseCore> object containing a parsed HTTP response.\n\t */\n\tpublic function process_response($curl_handle = null, $response = null)\n\t{\n\t\t// Accept a custom one if it's passed.\n\t\tif ($curl_handle && $response)\n\t\t{\n\t\t\t$this->curl_handle = $curl_handle;\n\t\t\t$this->response = $response;\n\t\t}\n\n\t\t// As long as this came back as a valid resource...\n\t\tif (is_resource($this->curl_handle))\n\t\t{\n\t\t\t// Determine what's what.\n\t\t\t$header_size = curl_getinfo($this->curl_handle, CURLINFO_HEADER_SIZE);\n\t\t\t$this->response_headers = substr($this->response, 0, $header_size);\n\t\t\t$this->response_body = substr($this->response, $header_size);\n\t\t\t$this->response_code = curl_getinfo($this->curl_handle, CURLINFO_HTTP_CODE);\n\t\t\t$this->response_info = curl_getinfo($this->curl_handle);\n\n\t\t\t// Parse out the headers\n\t\t\t$this->response_headers = explode(\"\\r\\n\\r\\n\", trim($this->response_headers));\n\t\t\t$this->response_headers = array_pop($this->response_headers);\n\t\t\t$this->response_headers = explode(\"\\r\\n\", $this->response_headers);\n\t\t\tarray_shift($this->response_headers);\n\n\t\t\t// Loop through and split up the headers.\n\t\t\t$header_assoc = array();\n\t\t\tforeach ($this->response_headers as $header)\n\t\t\t{\n\t\t\t\t$kv = explode(': ', $header);\n\t\t\t\t$header_assoc[strtolower($kv[0])] = $kv[1];\n\t\t\t}\n\n\t\t\t// Reset the headers to the appropriate property.\n\t\t\t$this->response_headers = $header_assoc;\n\t\t\t$this->response_headers['_info'] = $this->response_info;\n\t\t\t$this->response_headers['_info']['method'] = $this->method;\n\n\t\t\tif ($curl_handle && $response)\n\t\t\t{\n\t\t\t\treturn new $this->response_class($this->response_headers, $this->response_body, $this->response_code, $this->curl_handle);\n\t\t\t}\n\t\t}\n\n\t\t// Return false\n\t\treturn false;\n\t}\n\n\t/**\n\t * Sends the request, calling necessary utility functions to update built-in properties.\n\t *\n\t * @param boolean $parse (Optional) Whether to parse the response with ResponseCore or not.\n\t * @return string The resulting unparsed data from the request.\n\t */\n\tpublic function send_request($parse = false)\n\t{\n\t\tif ($this->allow_set_time_limit)\n\t\t{\n\t\t\tset_time_limit(0);\n\t\t}\n\n\t\t$curl_handle = $this->prep_request();\n\t\t$this->response = curl_exec($curl_handle);\n\n\t\tif ($this->response === false)\n\t\t{\n\t\t\tthrow 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.');\n\t\t}\n\n\t\t$parsed_response = $this->process_response($curl_handle, $this->response);\n\n\t\tcurl_close($curl_handle);\n\n\t\tif ($parse)\n\t\t{\n\t\t\treturn $parsed_response;\n\t\t}\n\n\t\treturn $this->response;\n\t}\n\n\t/**\n\t * Sends the request using <php:curl_multi_exec()>, enabling parallel requests. Uses the \"rolling\" method.\n\t *\n\t * @param array $handles (Required) An indexed array of cURL handles to process simultaneously.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * @return array Post-processed cURL responses.\n\t */\n\tpublic function send_multi_request($handles, $opt = null)\n\t{\n\t\tif ($this->allow_set_time_limit)\n\t\t{\n\t\t\tset_time_limit(0);\n\t\t}\n\n\t\t// Skip everything if there are no handles to process.\n\t\tif (count($handles) === 0) return array();\n\n\t\tif (!$opt) $opt = array();\n\n\t\t// Initialize any missing options\n\t\t$limit = isset($opt['limit']) ? $opt['limit'] : -1;\n\n\t\t// Initialize\n\t\t$handle_list = $handles;\n\t\t$http = new $this->request_class();\n\t\t$multi_handle = curl_multi_init();\n\t\t$handles_post = array();\n\t\t$added = count($handles);\n\t\t$last_handle = null;\n\t\t$count = 0;\n\t\t$i = 0;\n\n\t\t// Loop through the cURL handles and add as many as it set by the limit parameter.\n\t\twhile ($i < $added)\n\t\t{\n\t\t\tif ($limit > 0 && $i >= $limit) break;\n\t\t\tcurl_multi_add_handle($multi_handle, array_shift($handles));\n\t\t\t$i++;\n\t\t}\n\n\t\tdo\n\t\t{\n\t\t\t$active = false;\n\n\t\t\t// Start executing and wait for a response.\n\t\t\twhile (($status = curl_multi_exec($multi_handle, $active)) === CURLM_CALL_MULTI_PERFORM)\n\t\t\t{\n\t\t\t\t// Start looking for possible responses immediately when we have to add more handles\n\t\t\t\tif (count($handles) > 0) break;\n\t\t\t}\n\n\t\t\t// Figure out which requests finished.\n\t\t\t$to_process = array();\n\n\t\t\twhile ($done = curl_multi_info_read($multi_handle))\n\t\t\t{\n\t\t\t\t// 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 )\n\t\t\t\tif ($done['result'] > 0)\n\t\t\t\t{\n\t\t\t\t\tthrow 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.');\n\t\t\t\t}\n\n\t\t\t\t// 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\n\t\t\t\telseif (!isset($to_process[(int) $done['handle']]))\n\t\t\t\t{\n\t\t\t\t\t$to_process[(int) $done['handle']] = $done;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Actually deal with the request\n\t\t\tforeach ($to_process as $pkey => $done)\n\t\t\t{\n\t\t\t\t$response = $http->process_response($done['handle'], curl_multi_getcontent($done['handle']));\n\t\t\t\t$key = array_search($done['handle'], $handle_list, true);\n\t\t\t\t$handles_post[$key] = $response;\n\n\t\t\t\tif (count($handles) > 0)\n\t\t\t\t{\n\t\t\t\t\tcurl_multi_add_handle($multi_handle, array_shift($handles));\n\t\t\t\t}\n\n\t\t\t\tcurl_multi_remove_handle($multi_handle, $done['handle']);\n\t\t\t\tcurl_close($done['handle']);\n\t\t\t}\n\t\t}\n\t\twhile ($active || count($handles_post) < $added);\n\n\t\tcurl_multi_close($multi_handle);\n\n\t\tksort($handles_post, SORT_NUMERIC);\n\t\treturn $handles_post;\n\t}\n\n\n\t/*%******************************************************************************************%*/\n\t// RESPONSE METHODS\n\n\t/**\n\t * Get the HTTP response headers from the request.\n\t *\n\t * @param string $header (Optional) A specific header value to return. Defaults to all headers.\n\t * @return string|array All or selected header values.\n\t */\n\tpublic function get_response_header($header = null)\n\t{\n\t\tif ($header)\n\t\t{\n\t\t\treturn $this->response_headers[strtolower($header)];\n\t\t}\n\t\treturn $this->response_headers;\n\t}\n\n\t/**\n\t * Get the HTTP response body from the request.\n\t *\n\t * @return string The response body.\n\t */\n\tpublic function get_response_body()\n\t{\n\t\treturn $this->response_body;\n\t}\n\n\t/**\n\t * Get the HTTP response code from the request.\n\t *\n\t * @return string The HTTP response code.\n\t */\n\tpublic function get_response_code()\n\t{\n\t\treturn $this->response_code;\n\t}\n}\n\n\n/**\n * Container for all response-related methods.\n */\nclass ResponseCore\n{\n\t/**\n\t * Stores the HTTP header information.\n\t */\n\tpublic $header;\n\n\t/**\n\t * Stores the SimpleXML response.\n\t */\n\tpublic $body;\n\n\t/**\n\t * Stores the HTTP response code.\n\t */\n\tpublic $status;\n\n\t/**\n\t * Constructs a new instance of this class.\n\t *\n\t * @param array $header (Required) Associative array of HTTP headers (typically returned by <RequestCore::get_response_header()>).\n\t * @param string $body (Required) XML-formatted response from AWS.\n\t * @param integer $status (Optional) HTTP response status code from the request.\n\t * @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.\n\t */\n\tpublic function __construct($header, $body, $status = null)\n\t{\n\t\t$this->header = $header;\n\t\t$this->body = $body;\n\t\t$this->status = $status;\n\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Did we receive the status code we expected?\n\t *\n\t * @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.\n\t * @return boolean Whether we received the expected status code or not.\n\t */\n\tpublic function isOK($codes = array(200, 201, 204, 206))\n\t{\n\t\tif (is_array($codes))\n\t\t{\n\t\t\treturn in_array($this->status, $codes);\n\t\t}\n\n\t\treturn $this->status === $codes;\n\t}\n}\n\nclass cURL_Exception extends Exception {}\nclass cURL_Multi_Exception extends cURL_Exception {}\nclass RequestCore_Exception extends Exception {}\n"
  },
  {
    "path": "manual_check.php",
    "content": "<html>\n<title>Manual Check</title>\n<!--<body bgcolor=\"yellow\"></body>-->\n<body>\n<style>\na {\n    color: inherit;\n    text-decoration: none;\n}\n.newcls{\n \n background-color:#0dbce7;\n  \n border: #2e6da4;\n  \n font-family: Arial, Helvetica,  sans-serif;\n  \n font-size: 15px;\n  \n color: #fff;\n  \n letter-spacing: 1px;\n  \n padding: 8px 12px;\n  \n font-size: 14px;\n  \n font-weight: normal;\n  \n border-radius: 4px;\n  \n line-height: 1.5;\n  \n text-decoration:none\n  \n }\n</style>\n\n<div class=\"newcls\">\n<h1> Check Manually |  <a href=../all_buckets.php>All Buckets</a></h1>\n</div>\n<br>\n\n<form name=\"manualcheck\" method=\"POST\"  action=\"process.php\">\n<input type=\"text\" class=\"newcls\" name=\"bucketname\" pattern=\"[a-z0-9-.]{3,63}\" placeholder=\"Enter Bucket Name...\" maxlength=\"63\" size=\"25\" required=\"required\">\n<input type=\"hidden\" class=\"newcls\" name=\"sourceurl\" value=\"-\">\n<input type=\"hidden\" class=\"newcls\" name=\"hostname\" value=\"-\">\n<input type=\"submit\" class=\"newcls\" value=\"Check Bucket\">\n</form>\n</body>\n</html>"
  },
  {
    "path": "process.php",
    "content": "<?php\n//I admit that i am a noob programmer, if you do have suggestions to improve Bucky,  lets talk https://twitter.com/smaranchand\nfunction bucketsuccess(){\n    $newbucket=$_POST['bucketname'];\n    $sourceurl=$_POST['sourceurl'];\n    $hostname=$_POST['hostname'];\n    $serverip= gethostbyname(\"$hostname\"); \n    \n    class MyDB extends SQLite3 {\n        function __construct() {\n           $this->open('bucky.db');\n        }\n     }\n     \n     $db = new MyDB();\n     if(!$db){\n        echo $db->lastErrorMsg();\n     } else {\n     }\n  \n     $sql =<<<EOF\n        INSERT INTO BUCKY (BUCKETNAME,URL,IP,POC,VULNERABLE)\n        VALUES ('$newbucket', '$sourceurl', '$serverip', 'https://$newbucket.s3.amazonaws.com/bucky.txt', '<b>YES</b>' );\n  \n  EOF;\n  \n     $ret = $db->exec($sql);\n     if(!$ret) {\n        echo $db->lastErrorMsg();\n     } else {\n   \n   header(\"refresh:3; url=all_buckets.php\");\n   echo \"S3 Bucket <b>$newbucket</b> seems vulnerable, Check dashboard for more information.\";\n     \n     }\n     $db->close();\n}\n\nfunction bucketfailed(){\n    $newbucket=$_POST['bucketname'];\n    $sourceurl=$_POST['sourceurl'];\n    $hostname=$_POST['hostname'];\n    $serverip= gethostbyname(\"$hostname\"); \n    class MyDB extends SQLite3 {\n        function __construct() {\n           $this->open('bucky.db');\n        }\n     }\n     \n     $db = new MyDB();\n     if(!$db){\n        echo $db->lastErrorMsg();\n     } else {\n     }\n  \n     $sql =<<<EOF\n        INSERT INTO BUCKY (BUCKETNAME,URL,IP,POC,VULNERABLE)\n        VALUES ('$newbucket', '$sourceurl', '$serverip', '-', 'NO' );\n  \n  EOF;\n  \n     $ret = $db->exec($sql);\n     if(!$ret) {\n        echo $db->lastErrorMsg();\n     } else {\n      header(\"refresh:3; url=manual_check.php\");\n      echo \"S3 Bucket <b>$newbucket</b> doesnot seems vulnerable, Check dashboard for more information.\";\n     }\n     $db->close();\n}\n\n$newbucket=$_POST['bucketname'];\nerror_reporting(0);\nrequire_once 'sdk.class.php';\n$s3 = new AmazonS3();\n$response = $s3->create_object($newbucket, 'bucky.txt', array(\n'contentType' => 'text/plain',\n'body' => 'S3 bucket misconfiguration is discovered.\nDiscovered by Bucky.\nDeveloped by: https://twitter.com/smaranchand',\n'acl'=>AmazonS3::ACL_PUBLIC,\n));\nif ((int) $response->isOK()) \nbucketsuccess();\nelse\nbucketfailed();\n?>\n"
  },
  {
    "path": "reset.sh",
    "content": "#!/bin/bash\nrm bucky.db\nphp dbreset.php"
  },
  {
    "path": "run.sh",
    "content": "#!/bin/bash\n#Bucky is AWS S3 bucket discovery tool developed by  https://twitter.com/smaranchand\n#Bucky Backend Engine automatically  takes input from Bucky addon and check for misconfiguration.\n#Download Bucky aaddon from https://addons.mozilla.org/en-US/firefox/addon/bucky/\n#No changes done.\necho \"\n  ____             _                  __   ___  \n |  _ \\           | |                /_ | / _ \\ \n | |_) |_   _  ___| | ___   _   __   _| || | | |\n |  _ <| | | |/ __| |/ / | | |  \\ \\ / / || | | |\n | |_) | |_| | (__|   <| |_| |   \\ V /| || |_| |\n |____/ \\__,_|\\___|_|\\_\\\\__, |    \\_/ |_(_)___/ \n                         __/ |                  \n                        |___/     \nAn automatic tool to find misconfigured AWS S3 buckets.  \nDeveloped by : https://twitter.com/smaranchand          \n\";\nphp -S 127.0.0.1:13337\n"
  },
  {
    "path": "sdk.class.php",
    "content": "<?php\n/*\n * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\").\n * You may not use this file except in compliance with the License.\n * A copy of the License is located at\n *\n *  http://aws.amazon.com/apache2.0\n *\n * or in the \"license\" file accompanying this file. This file is distributed\n * on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either\n * express or implied. See the License for the specific language governing\n * permissions and limitations under the License.\n */\n\n\n/*%******************************************************************************************%*/\n// EXCEPTIONS\n\n/**\n * Default CFRuntime Exception.\n */\nclass CFRuntime_Exception extends Exception {}\n\n/**\n * Parsing Exception.\n */\nclass Parser_Exception extends Exception {}\n\n\n/*%******************************************************************************************%*/\n// INTERMEDIARY CONSTANTS\n\ndefine('CFRUNTIME_NAME', 'aws-sdk-php');\ndefine('CFRUNTIME_VERSION', '1.6.2');\ndefine('CFRUNTIME_BUILD', '20130314130000');\n$user_agent = sprintf('%s/%s PHP/%s', CFRUNTIME_NAME, CFRUNTIME_VERSION, PHP_VERSION);\nif (function_exists('curl_version'))\n{\n\t$curl_version = curl_version();\n\t$user_agent .= ' curl/' . $curl_version['version'];\n}\nif (defined('OPENSSL_VERSION_TEXT'))\n{\n\t$openssl_version = explode(' ', OPENSSL_VERSION_TEXT);\n\t$user_agent .=  ' openssl/' . $openssl_version[1];\n}\ndefine('CFRUNTIME_USERAGENT', $user_agent);\nunset($user_agent);\n\n\n/*%******************************************************************************************%*/\n// CLASS\n\n/**\n * Core functionality and default settings shared across all SDK classes. All methods and properties in this\n * class are inherited by the service-specific classes.\n *\n * @version 2012.05.25\n * @license See the included NOTICE.md file for more information.\n * @copyright See the included NOTICE.md file for more information.\n * @link http://aws.amazon.com/php/ PHP Developer Center\n */\nclass CFRuntime\n{\n\t/*%******************************************************************************************%*/\n\t// CONSTANTS\n\n\t/**\n\t * Name of the software.\n\t */\n\tconst NAME = CFRUNTIME_NAME;\n\n\t/**\n\t * Version of the software.\n\t */\n\tconst VERSION = CFRUNTIME_VERSION;\n\n\t/**\n\t * Build ID of the software.\n\t */\n\tconst BUILD = CFRUNTIME_BUILD;\n\n\t/**\n\t * User agent string used to identify the software.\n\t */\n\tconst USERAGENT = CFRUNTIME_USERAGENT;\n\n\n\t/*%******************************************************************************************%*/\n\t// PROPERTIES\n\n\t/**\n\t * The Amazon API Key.\n\t */\n\tpublic $key;\n\n\t/**\n\t * The Amazon API Secret Key.\n\t */\n\tpublic $secret_key;\n\n\t/**\n\t * The Amazon Authentication Token.\n\t */\n\tpublic $auth_token;\n\n\t/**\n\t * Handle for the utility functions.\n\t */\n\tpublic $util;\n\n\t/**\n\t * An identifier for the current AWS service.\n\t */\n\tpublic $service = null;\n\n\t/**\n\t * The supported API version.\n\t */\n\tpublic $api_version = null;\n\n\t/**\n\t * The state of whether auth should be handled as AWS Query.\n\t */\n\tpublic $use_aws_query = true;\n\n\t/**\n\t * The default class to use for utilities (defaults to <CFUtilities>).\n\t */\n\tpublic $utilities_class = 'CFUtilities';\n\n\t/**\n\t * The default class to use for HTTP requests (defaults to <CFRequest>).\n\t */\n\tpublic $request_class = 'CFRequest';\n\n\t/**\n\t * The default class to use for HTTP responses (defaults to <CFResponse>).\n\t */\n\tpublic $response_class = 'CFResponse';\n\n\t/**\n\t * The default class to use for parsing XML (defaults to <CFSimpleXML>).\n\t */\n\tpublic $parser_class = 'CFSimpleXML';\n\n\t/**\n\t * The default class to use for handling batch requests (defaults to <CFBatchRequest>).\n\t */\n\tpublic $batch_class = 'CFBatchRequest';\n\n\t/**\n\t * The state of SSL/HTTPS use.\n\t */\n\tpublic $use_ssl = true;\n\n\t/**\n\t * The state of SSL certificate verification.\n\t */\n\tpublic $ssl_verification = true;\n\n\t/**\n\t * The proxy to use for connecting.\n\t */\n\tpublic $proxy = null;\n\n\t/**\n\t * The alternate hostname to use, if any.\n\t */\n\tpublic $hostname = null;\n\n\t/**\n\t * The state of the capability to override the hostname with <set_hostname()>.\n\t */\n\tpublic $override_hostname = true;\n\n\t/**\n\t * The alternate port number to use, if any.\n\t */\n\tpublic $port_number = null;\n\n\t/**\n\t * The alternate resource prefix to use, if any.\n\t */\n\tpublic $resource_prefix = null;\n\n\t/**\n\t * The state of cache flow usage.\n\t */\n\tpublic $use_cache_flow = false;\n\n\t/**\n\t * The caching class to use.\n\t */\n\tpublic $cache_class = null;\n\n\t/**\n\t * The caching location to use.\n\t */\n\tpublic $cache_location = null;\n\n\t/**\n\t * When the cache should be considered stale.\n\t */\n\tpublic $cache_expires = null;\n\n\t/**\n\t * The state of cache compression.\n\t */\n\tpublic $cache_compress = null;\n\n\t/**\n\t * The current instantiated cache object.\n\t */\n\tpublic $cache_object = null;\n\n\t/**\n\t * The current instantiated batch request object.\n\t */\n\tpublic $batch_object = null;\n\n\t/**\n\t * The internally instantiated batch request object.\n\t */\n\tpublic $internal_batch_object = null;\n\n\t/**\n\t * The state of batch flow usage.\n\t */\n\tpublic $use_batch_flow = false;\n\n\t/**\n\t * The state of the cache deletion setting.\n\t */\n\tpublic $delete_cache = false;\n\n\t/**\n\t * The state of the debug mode setting.\n\t */\n\tpublic $debug_mode = false;\n\n\t/**\n\t * The number of times to retry failed requests.\n\t */\n\tpublic $max_retries = 3;\n\n\t/**\n\t * The user-defined callback function to call when a stream is read from.\n\t */\n\tpublic $registered_streaming_read_callback = null;\n\n\t/**\n\t * The user-defined callback function to call when a stream is written to.\n\t */\n\tpublic $registered_streaming_write_callback = null;\n\n\t/**\n\t * The credentials to use for authentication.\n\t */\n\tpublic $credentials = array();\n\n\t/**\n\t * The authentication class to use.\n\t */\n\tpublic $auth_class = null;\n\n\t/**\n\t * The operation to execute.\n\t */\n\tpublic $operation = null;\n\n\t/**\n\t * The payload to send.\n\t */\n\tpublic $payload = array();\n\n\t/**\n\t * The string prefix to prepend to the operation name.\n\t */\n\tpublic $operation_prefix = '';\n\n\t/**\n\t * The number of times a request has been retried.\n\t */\n\tpublic $redirects = 0;\n\n\t/**\n\t * The state of whether the response should be parsed or not.\n\t */\n\tpublic $parse_the_response = true;\n\n\n\t/*%******************************************************************************************%*/\n\t// CONSTRUCTOR\n\n\t/**\n\t * The constructor. This class should not be instantiated directly. Rather, a service-specific class\n\t * should be instantiated.\n\t *\n\t * @param array $options (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<li><code>credentials</code> - <code>string</code> - Optional - The name of the credential set to use for authentication.</li>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * \t<li><code>token</code> - <code>string</code> - Optional - An AWS session token.</li>\n\t * \t<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>\n\t * @return void\n\t */\n\tpublic function __construct(array $options = array())\n\t{\n\t\t// Instantiate the utilities class.\n\t\t$this->util = new $this->utilities_class();\n\n\t\t// Determine the current service.\n\t\t$this->service = get_class($this);\n\n\t\t// Create credentials based on the options\n\t\t$runtime_credentials = new CFCredential($options);\n\t\t$credentials_provided = false;\n\n\t\t// Retrieve a credential set from config.inc.php if it exists\n\t\tif (isset($options['credentials']))\n\t\t{\n\t\t\t// Use a specific credential set and merge with the runtime credentials\n\t\t\t$this->credentials = CFCredentials::get($options['credentials'])\n\t\t\t\t->merge($runtime_credentials);\n\t\t}\n\t\telse\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\t// Use the default credential set and merge with the runtime credentials\n\t\t\t\t$this->credentials = CFCredentials::get(CFCredentials::DEFAULT_KEY)\n\t\t\t\t\t->merge($runtime_credentials);\n\t\t\t}\n\t\t\tcatch (CFCredentials_Exception $e)\n\t\t\t{\n\t\t\t\t// Only the runtime credentials were provided\n\t\t\t\t$this->credentials = $runtime_credentials;\n\t\t\t}\n\t\t}\n\n\t\t// Check if keys were actually provided\n\t\tif (isset($this->credentials['key']) && isset($this->credentials['secret']))\n\t\t{\n\t\t\t$credentials_provided = true;\n\t\t}\n\n\t\t// Check for an instance profile credentials override\n\t\tif (isset($this->credentials['use_instance_profile_credentials']) && $this->credentials['use_instance_profile_credentials'])\n\t\t{\n\t\t\t$credentials_provided = false;\n\t\t}\n\n\t\t// Automatically enable whichever caching mechanism is set to default.\n\t\t$this->set_cache_config($this->credentials->default_cache_config);\n\n\t\t// If no credentials were provided, try to get them from the EC2 instance profile\n\t\tif (!$credentials_provided)\n\t\t{\n\t\t\t// Default caching mechanism is required\n\t\t\tif (!$this->credentials->default_cache_config)\n\t\t\t{\n\t\t\t\t// @codeCoverageIgnoreStart\n\t\t\t\tthrow new CFCredentials_Exception('No credentials were provided. The SDK attempts to retrieve Instance '\n\t\t\t\t\t. 'Profile credentials from the EC2 Instance Metadata Service, but doing this requires the '\n\t\t\t\t\t. '\"default_cache_config\" option to be set in the config.inc.php file or constructor. In order to '\n\t\t\t\t\t. 'cache the retrieved credentials.');\n\t\t\t\t// @codeCoverageIgnoreEnd\n\t\t\t}\n\n\t\t\t// Instantiate and invoke the cache for instance profile credentials\n\t\t\t$cache = new $this->cache_class('instance_profile_credentials', $this->cache_location, 0, $this->cache_compress);\n\t\t\tif ($data = $cache->read())\n\t\t\t{\n\t\t\t\t$cache->expire_in((strtotime($data['expires']) - time()) * 0.85);\n\t\t\t}\n\t\t\t$instance_profile_credentials = $cache->response_manager(array($this, 'cache_instance_profile_credentials'), array($cache, $options));\n\n\t\t\t$this->credentials->key    = $instance_profile_credentials['key'];\n\t\t\t$this->credentials->secret = $instance_profile_credentials['secret'];\n\t\t\t$this->credentials->token  = $instance_profile_credentials['token'];\n\t\t}\n\n\t\t// Set internal credentials after they are resolved\n\t\t$this->key = $this->credentials->key;\n\t\t$this->secret_key = $this->credentials->secret;\n\t\t$this->auth_token = $this->credentials->token;\n\t}\n\n\t/**\n\t * Alternate approach to constructing a new instance. Supports chaining.\n\t *\n\t * @param array $options (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<li><code>credentials</code> - <code>string</code> - Optional - The name of the credential set to use for authentication.</li>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * \t<li><code>token</code> - <code>string</code> - Optional - An AWS session token.</li></ul>\n\t * @return void\n\t */\n\tpublic static function factory(array $options = array())\n\t{\n\t\tif (version_compare(PHP_VERSION, '5.3.0', '<'))\n\t\t{\n\t\t\tthrow new Exception('PHP 5.3 or newer is required to instantiate a new class with CLASS::factory().');\n\t\t}\n\n\t\t$self = get_called_class();\n\t\treturn new $self($options);\n\t}\n\n\n\t/*%******************************************************************************************%*/\n\t// MAGIC METHODS\n\n\t/**\n\t * A magic method that allows `camelCase` method names to be translated into `snake_case` names.\n\t *\n\t * @param string $name (Required) The name of the method.\n\t * @param array $arguments (Required) The arguments passed to the method.\n\t * @return mixed The results of the intended method.\n\t */\n\tpublic function  __call($name, $arguments)\n\t{\n\t\t// Convert camelCase method calls to snake_case.\n\t\t$method_name = strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', $name));\n\n\t\tif (method_exists($this, $method_name))\n\t\t{\n\t\t\treturn call_user_func_array(array($this, $method_name), $arguments);\n\t\t}\n\n\t\tthrow new CFRuntime_Exception('The method ' . $name . '() is undefined. Attempted to map to ' . $method_name . '() which is also undefined. Error occurred');\n\t}\n\n\n\t/*%******************************************************************************************%*/\n\t// SET CUSTOM SETTINGS\n\n\t/**\n\t * Set the proxy settings to use.\n\t *\n\t * @param string $proxy (Required) Accepts proxy credentials in the following format: `proxy://user:pass@hostname:port`\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function set_proxy($proxy)\n\t{\n\t\t$this->proxy = $proxy;\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Set the hostname to connect to. This is useful for alternate services that are API-compatible with\n\t * AWS, but run from a different hostname.\n\t *\n\t * @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.\n\t * @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.\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function set_hostname($hostname, $port_number = null)\n\t{\n\t\tif ($this->override_hostname)\n\t\t{\n\t\t\t$this->hostname = $hostname;\n\n\t\t\tif ($port_number)\n\t\t\t{\n\t\t\t\t$this->port_number = $port_number;\n\t\t\t\t$this->hostname .= ':' . (string) $this->port_number;\n\t\t\t}\n\t\t}\n\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Set the resource prefix to use. This method is useful for alternate services that are API-compatible\n\t * with AWS.\n\t *\n\t * @param string $prefix (Required) An alternate prefix to prepend to the resource path. Useful for mock or test applications.\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function set_resource_prefix($prefix)\n\t{\n\t\t$this->resource_prefix = $prefix;\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Disables any subsequent use of the <set_hostname()> method.\n\t *\n\t * @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`.\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function allow_hostname_override($override = true)\n\t{\n\t\t$this->override_hostname = $override;\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Disables SSL/HTTPS connections for hosts that don't support them. Some services, however, still\n\t * require SSL support.\n\t *\n\t * This method will throw a user warning when invoked, which can be hidden by changing your\n\t * <php:error_reporting()> settings.\n\t *\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function disable_ssl()\n\t{\n\t\ttrigger_error('Disabling SSL connections is potentially unsafe and highly discouraged.', E_USER_WARNING);\n\t\t$this->use_ssl = false;\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Disables the verification of the SSL Certificate Authority. Doing so can enable an attacker to carry\n\t * out a man-in-the-middle attack.\n\t *\n\t * https://secure.wikimedia.org/wikipedia/en/wiki/Man-in-the-middle_attack\n\t *\n\t * This method will throw a user warning when invoked, which can be hidden by changing your\n\t * <php:error_reporting()> settings.\n\t *\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function disable_ssl_verification($ssl_verification = false)\n\t{\n\t\ttrigger_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);\n\t\t$this->ssl_verification = $ssl_verification;\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Enables HTTP request/response header logging to `STDERR`.\n\t *\n\t * @param boolean $enabled (Optional) Whether or not to enable debug mode. Defaults to `true`.\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function enable_debug_mode($enabled = true)\n\t{\n\t\t$this->debug_mode = $enabled;\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Sets the maximum number of times to retry failed requests.\n\t *\n\t * @param integer $retries (Optional) The maximum number of times to retry failed requests. Defaults to `3`.\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function set_max_retries($retries = 3)\n\t{\n\t\t$this->max_retries = $retries;\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Set the caching configuration to use for response caching.\n\t *\n\t * @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>\n\t * @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`.\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function set_cache_config($location, $gzip = true)\n\t{\n\t\t// If location is empty, don't do anything.\n\t\tif (empty($location))\n\t\t{\n\t\t\treturn $this;\n\t\t}\n\n\t\t// If we have an array, we're probably passing in Memcached servers and ports.\n\t\tif (is_array($location))\n\t\t{\n\t\t\t$this->cache_class = 'CacheMC';\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// I would expect locations like `/tmp/cache`, `pdo.mysql://user:pass@hostname:port`, `pdo.sqlite:memory:`, and `apc`.\n\t\t\t$type = strtolower(substr($location, 0, 3));\n\t\t\tswitch ($type)\n\t\t\t{\n\t\t\t\tcase 'apc':\n\t\t\t\t\t$this->cache_class = 'CacheAPC';\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'xca': // First three letters of `xcache`\n\t\t\t\t\t$this->cache_class = 'CacheXCache';\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'pdo':\n\t\t\t\t\t$this->cache_class = 'CachePDO';\n\t\t\t\t\t$location = substr($location, 4);\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\t$this->cache_class = 'CacheFile';\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// Set the remaining cache information.\n\t\t$this->cache_location = $location;\n\t\t$this->cache_compress = $gzip;\n\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Register a callback function to execute whenever a data stream is read from using\n\t * <CFRequest::streaming_read_callback()>.\n\t *\n\t * The user-defined callback function should accept three arguments:\n\t *\n\t * <ul>\n\t * \t<li><code>$curl_handle</code> - <code>resource</code> - Required - The cURL handle resource that represents the in-progress transfer.</li>\n\t * \t<li><code>$file_handle</code> - <code>resource</code> - Required - The file handle resource that represents the file on the local file system.</li>\n\t * \t<li><code>$length</code> - <code>integer</code> - Required - The length in kilobytes of the data chunk that was transferred.</li>\n\t * </ul>\n\t *\n\t * @param string|array|function $callback (Required) The callback function is called by <php:call_user_func()>, so you can pass the following values: <ul>\n\t * \t<li>The name of a global function to execute, passed as a string.</li>\n\t * \t<li>A method to execute, passed as <code>array('ClassName', 'MethodName')</code>.</li>\n\t * \t<li>An anonymous function (PHP 5.3+).</li></ul>\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function register_streaming_read_callback($callback)\n\t{\n\t\t$this->registered_streaming_read_callback = $callback;\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Register a callback function to execute whenever a data stream is written to using\n\t * <CFRequest::streaming_write_callback()>.\n\t *\n\t * The user-defined callback function should accept two arguments:\n\t *\n\t * <ul>\n\t * \t<li><code>$curl_handle</code> - <code>resource</code> - Required - The cURL handle resource that represents the in-progress transfer.</li>\n\t * \t<li><code>$length</code> - <code>integer</code> - Required - The length in kilobytes of the data chunk that was transferred.</li>\n\t * </ul>\n\t *\n\t * @param string|array|function $callback (Required) The callback function is called by <php:call_user_func()>, so you can pass the following values: <ul>\n\t * \t<li>The name of a global function to execute, passed as a string.</li>\n\t * \t<li>A method to execute, passed as <code>array('ClassName', 'MethodName')</code>.</li>\n\t * \t<li>An anonymous function (PHP 5.3+).</li></ul>\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function register_streaming_write_callback($callback)\n\t{\n\t\t$this->registered_streaming_write_callback = $callback;\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Fetches and caches STS credentials. This is meant to be used by the constructor, and is not to be\n\t * manually invoked.\n\t *\n\t * @param CacheCore $cache (Required) The a reference to the cache object that is being used to handle the caching.\n\t * @param array $options (Required) The options that were passed into the constructor.\n\t * @return mixed The data to be cached, or NULL.\n\t */\n\tpublic function cache_sts_credentials($cache, $options)\n\t{\n\t\t$token = new AmazonSTS($options);\n\t\t$response = $token->get_session_token();\n\n\t\tif ($response->isOK())\n\t\t{\n\t\t\t// Update the expiration\n\t\t\t$expiration_time = strtotime((string) $response->body->GetSessionTokenResult->Credentials->Expiration);\n\t\t\t$expiration_duration = round(($expiration_time - time()) * 0.85);\n\t\t\t$cache->expire_in($expiration_duration);\n\n\t\t\t// Return the important data\n\t\t\t$credentials = $response->body->GetSessionTokenResult->Credentials;\n\n\t\t\treturn array(\n\t\t\t\t'key'     => (string) $credentials->AccessKeyId,\n\t\t\t\t'secret'  => (string) $credentials->SecretAccessKey,\n\t\t\t\t'token'   => (string) $credentials->SessionToken,\n\t\t\t\t'expires' => (string) $credentials->Expiration,\n\t\t\t);\n\t\t}\n\n\t\t// @codeCoverageIgnoreStart\n\t\tthrow new STS_Exception('Temporary credentials from the AWS Security '\n\t\t\t. 'Token Service could not be retrieved using the provided long '\n\t\t\t. 'term credentials. It\\'s possible that the provided long term '\n\t\t\t. 'credentials were invalid.');\n\t\t// @codeCoverageIgnoreEnd\n\t}\n\n\t/**\n\t * Fetches and caches EC2 instance profile credentials. This is meant to be used by the constructor, and is not to\n\t * be manually invoked.\n\t *\n\t * @param CacheCore $cache (Required) The a reference to the cache object that is being used to handle the caching.\n\t * @param array $options (Required) The options that were passed into the constructor.\n\t * @return mixed The data to be cached, or NULL.\n\t */\n\tpublic function cache_instance_profile_credentials($cache, $options)\n\t{\n\t\t$instance_profile_url = 'http://169.254.169.254/latest/meta-data/iam/security-credentials/';\n\t\t$connect_timeout = isset($options['instance_profile_timeout']) ? $options['instance_profile_timeout'] : 2;\n\n\t\ttry\n\t\t{\n\t\t\t// Make a call to the EC2 Metadata Service to find the available instance profile\n\t\t\t$request = new RequestCore($instance_profile_url);\n\t\t\t$request->set_curlopts(array(CURLOPT_CONNECTTIMEOUT => $connect_timeout));\n\t\t\t$response = $request->send_request(true);\n\n\t\t\tif ($response->isOK())\n\t\t\t{\n\t\t\t\t// Get the instance profile name\n\t\t\t\t$profile = (string) $response->body;\n\n\t\t\t\t// Make a call to the EC2 Metadata Service to get the instance profile credentials\n\t\t\t\t$request = new RequestCore($instance_profile_url . $profile);\n\t\t\t\t$request->set_curlopts(array(CURLOPT_CONNECTTIMEOUT => $connect_timeout));\n\t\t\t\t$response = $request->send_request(true);\n\n\t\t\t\tif ($response->isOK())\n\t\t\t\t{\n\t\t\t\t\t// Get the credentials\n\t\t\t\t\t$credentials = json_decode($response->body, true);\n\n\t\t\t\t\tif ($credentials['Code'] === 'Success')\n\t\t\t\t\t{\n\t\t\t\t\t\t// Determine the expiration time\n\t\t\t\t\t\t$expiration_time = strtotime((string) $credentials['Expiration']);\n\t\t\t\t\t\t$expiration_duration = round(($expiration_time - time()) * 0.85);\n\t\t\t\t\t\t$cache->expire_in($expiration_duration);\n\n\t\t\t\t\t\t// Return the credential information\n\t\t\t\t\t\treturn array(\n\t\t\t\t\t\t\t'key'     => $credentials['AccessKeyId'],\n\t\t\t\t\t\t\t'secret'  => $credentials['SecretAccessKey'],\n\t\t\t\t\t\t\t'token'   => $credentials['Token'],\n\t\t\t\t\t\t\t'expires' => $credentials['Expiration'],\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcatch (cURL_Exception $e)\n\t\t{\n\t\t\t// The EC2 Metadata Service does not exist or had timed out.\n\t\t\t// An exception will be thrown on the next line.\n\t\t}\n\n\t\t// @codeCoverageIgnoreStart\n\t\tthrow new CFCredentials_Exception('No credentials were provided. The SDK attempted to retrieve Instance '\n\t\t\t. 'Profile credentials from the EC2 Instance Metadata Service, but failed to do so. Instance profile '\n\t\t\t. 'credentials are only accessible on EC2 instances configured with a specific IAM role.');\n\t\t// @codeCoverageIgnoreEnd\n\t}\n\n\n\t/*%******************************************************************************************%*/\n\t// SET CUSTOM CLASSES\n\n\t/**\n\t * Set a custom class for this functionality. Use this method when extending/overriding existing classes\n\t * with new functionality.\n\t *\n\t * The replacement class must extend from <CFUtilities>.\n\t *\n\t * @param string $class (Optional) The name of the new class to use for this functionality.\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function set_utilities_class($class = 'CFUtilities')\n\t{\n\t\t$this->utilities_class = $class;\n\t\t$this->util = new $this->utilities_class();\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Set a custom class for this functionality. Use this method when extending/overriding existing classes\n\t * with new functionality.\n\t *\n\t * The replacement class must extend from <CFRequest>.\n\t *\n\t * @param string $class (Optional) The name of the new class to use for this functionality.\n\t * @param $this A reference to the current instance.\n\t */\n\tpublic function set_request_class($class = 'CFRequest')\n\t{\n\t\t$this->request_class = $class;\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Set a custom class for this functionality. Use this method when extending/overriding existing classes\n\t * with new functionality.\n\t *\n\t * The replacement class must extend from <CFResponse>.\n\t *\n\t * @param string $class (Optional) The name of the new class to use for this functionality.\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function set_response_class($class = 'CFResponse')\n\t{\n\t\t$this->response_class = $class;\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Set a custom class for this functionality. Use this method when extending/overriding existing classes\n\t * with new functionality.\n\t *\n\t * The replacement class must extend from <CFSimpleXML>.\n\t *\n\t * @param string $class (Optional) The name of the new class to use for this functionality.\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function set_parser_class($class = 'CFSimpleXML')\n\t{\n\t\t$this->parser_class = $class;\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Set a custom class for this functionality. Use this method when extending/overriding existing classes\n\t * with new functionality.\n\t *\n\t * The replacement class must extend from <CFBatchRequest>.\n\t *\n\t * @param string $class (Optional) The name of the new class to use for this functionality.\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function set_batch_class($class = 'CFBatchRequest')\n\t{\n\t\t$this->batch_class = $class;\n\t\treturn $this;\n\t}\n\n\n\t/*%******************************************************************************************%*/\n\t// AUTHENTICATION\n\n\t/**\n\t * Default, shared method for authenticating a connection to AWS.\n\t *\n\t * @param string $operation (Required) Indicates the operation to perform.\n\t * @param array $payload (Required) An associative array of parameters for authenticating. See the individual methods for allowed keys.\n\t * @return CFResponse Object containing a parsed HTTP response.\n\t */\n\tpublic function authenticate($operation, $payload)\n\t{\n\t\t$original_payload = $payload;\n\t\t$method_arguments = func_get_args();\n\t\t$curlopts = array();\n\t\t$return_curl_handle = false;\n\n\t\tif (substr($operation, 0, strlen($this->operation_prefix)) !== $this->operation_prefix)\n\t\t{\n\t\t\t$operation = $this->operation_prefix . $operation;\n\t\t}\n\n\t\t// Extract the custom CURLOPT settings from the payload\n\t\tif (is_array($payload) && isset($payload['curlopts']))\n\t\t{\n\t\t\t$curlopts = $payload['curlopts'];\n\t\t\tunset($payload['curlopts']);\n\t\t}\n\n\t\t// Determine whether the response or curl handle should be returned\n\t\tif (is_array($payload) && isset($payload['returnCurlHandle']))\n\t\t{\n\t\t\t$return_curl_handle = isset($payload['returnCurlHandle']) ? $payload['returnCurlHandle'] : false;\n\t\t\tunset($payload['returnCurlHandle']);\n\t\t}\n\n\t\t// Use the caching flow to determine if we need to do a round-trip to the server.\n\t\tif ($this->use_cache_flow)\n\t\t{\n\t\t\t// Generate an identifier specific to this particular set of arguments.\n\t\t\t$cache_id = $this->key . '_' . get_class($this) . '_' . $operation . '_' . sha1(serialize($method_arguments));\n\n\t\t\t// Instantiate the appropriate caching object.\n\t\t\t$this->cache_object = new $this->cache_class($cache_id, $this->cache_location, $this->cache_expires, $this->cache_compress);\n\n\t\t\tif ($this->delete_cache)\n\t\t\t{\n\t\t\t\t$this->use_cache_flow = false;\n\t\t\t\t$this->delete_cache = false;\n\t\t\t\treturn $this->cache_object->delete();\n\t\t\t}\n\n\t\t\t// Invoke the cache callback function to determine whether to pull data from the cache or make a fresh request.\n\t\t\t$data = $this->cache_object->response_manager(array($this, 'cache_callback'), $method_arguments);\n\n\t\t\t// Parse the XML body\n\t\t\t$data = $this->parse_callback($data);\n\n\t\t\t// End!\n\t\t\treturn $data;\n\t\t}\n\n\t\t/*%******************************************************************************************%*/\n\n\t\t// Signer\n\t\t$signer = new $this->auth_class($this->hostname, $operation, $payload, $this->credentials);\n\t\t$signer->key = $this->key;\n\t\t$signer->secret_key = $this->secret_key;\n\t\t$signer->auth_token = $this->auth_token;\n\t\t$signer->api_version = $this->api_version;\n\t\t$signer->utilities_class = $this->utilities_class;\n\t\t$signer->request_class = $this->request_class;\n\t\t$signer->response_class = $this->response_class;\n\t\t$signer->use_ssl = $this->use_ssl;\n\t\t$signer->proxy = $this->proxy;\n\t\t$signer->util = $this->util;\n\t\t$signer->registered_streaming_read_callback = $this->registered_streaming_read_callback;\n\t\t$signer->registered_streaming_write_callback = $this->registered_streaming_write_callback;\n\t\t$request = $signer->authenticate();\n\n\t\t// Update RequestCore settings\n\t\t$request->request_class = $this->request_class;\n\t\t$request->response_class = $this->response_class;\n\t\t$request->ssl_verification = $this->ssl_verification;\n\n\t\t/*%******************************************************************************************%*/\n\n\t\t// Debug mode\n\t\tif ($this->debug_mode)\n\t\t{\n\t\t\t$request->debug_mode = $this->debug_mode;\n\t\t}\n\n\t\t// Set custom CURLOPT settings\n\t\tif (count($curlopts))\n\t\t{\n\t\t\t$request->set_curlopts($curlopts);\n\t\t}\n\n\t\t// Manage the (newer) batch request API or the (older) returnCurlHandle setting.\n\t\tif ($this->use_batch_flow)\n\t\t{\n\t\t\t$handle = $request->prep_request();\n\t\t\t$this->batch_object->add($handle);\n\t\t\t$this->use_batch_flow = false;\n\n\t\t\treturn $handle;\n\t\t}\n\t\telseif ($return_curl_handle)\n\t\t{\n\t\t\treturn $request->prep_request();\n\t\t}\n\n\t\t// Send!\n\t\t$request->send_request();\n\n\t\t// Prepare the response.\n\t\t$headers = $request->get_response_header();\n\t\t$headers['x-aws-stringtosign'] = $signer->string_to_sign;\n\n\t\tif (isset($signer->canonical_request))\n\t\t{\n\t\t\t$headers['x-aws-canonicalrequest'] = $signer->canonical_request;\n\t\t}\n\n\t\t$headers['x-aws-request-headers'] = $request->request_headers;\n\t\t$headers['x-aws-body'] = $signer->querystring;\n\n\t\t$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());\n\n\t\t$response_body = (string) $request->get_response_body();\n\n\t\t// Was it Amazon's fault the request failed? Retry the request until we reach $max_retries.\n\t\tif (\n\t\t\t(integer) $request->get_response_code() === 500 || // Internal Error (presumably transient)\n\t\t\t(integer) $request->get_response_code() === 503)   // Service Unavailable (presumably transient)\n\t\t{\n\t\t\tif ($this->redirects <= $this->max_retries)\n\t\t\t{\n\t\t\t\t// Exponential backoff\n\t\t\t\t$delay = (integer) (pow(4, $this->redirects) * 100000);\n\t\t\t\tusleep($delay);\n\t\t\t\t$this->redirects++;\n\t\t\t\t$data = $this->authenticate($operation, $original_payload);\n\t\t\t}\n\t\t}\n\n\t\t// DynamoDB has additional, custom logic for retrying requests\n\t\telse\n\t\t{\n\t\t\t// If the request to DynamoDB was throttled, we need to retry\n\t\t\t$need_to_retry_dynamodb_request = (\n\t\t\t\t(integer) $request->get_response_code() === 400 &&\n\t\t\t\tstripos($response_body, 'com.amazonaws.dynamodb.') !== false &&\n\t\t\t\tstripos($response_body, 'ProvisionedThroughputExceededException') !== false\n\t\t\t);\n\n\t\t\t// If the CRC32 of the response does not match the expected value, we need to retry\n\t\t\t$response_headers = $request->get_response_header();\n\t\t\tif (!$need_to_retry_dynamodb_request && isset($response_headers['x-amz-crc32']))\n\t\t\t{\n\t\t\t\t$crc32_expected = $response_headers['x-amz-crc32'];\n\t\t\t\t$crc32_actual = hexdec(hash('crc32b', $response_body));\n\t\t\t\t$need_to_retry_dynamodb_request = ($crc32_expected != $crc32_actual);\n\t\t\t}\n\n\t\t\t// Perform retry if necessary using a more aggressive exponential backoff\n\t\t\tif ($need_to_retry_dynamodb_request)\n\t\t\t{\n\t\t\t\tif ($this->redirects === 0)\n\t\t\t\t{\n\t\t\t\t\t$this->redirects++;\n\t\t\t\t\t$data = $this->authenticate($operation, $original_payload);\n\t\t\t\t}\n\t\t\t\telseif ($this->redirects <= max($this->max_retries, 10))\n\t\t\t\t{\n\t\t\t\t\t// Exponential backoff\n\t\t\t\t\t$delay = (integer) (pow(2, ($this->redirects - 1)) * 50000);\n\t\t\t\t\tusleep($delay);\n\t\t\t\t\t$this->redirects++;\n\t\t\t\t\t$data = $this->authenticate($operation, $original_payload);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t$this->redirects = 0;\n\t\treturn $data;\n\t}\n\n\n\t/*%******************************************************************************************%*/\n\t// BATCH REQUEST LAYER\n\n\t/**\n\t * Specifies that the intended request should be queued for a later batch request.\n\t *\n\t * @param CFBatchRequest $queue (Optional) The <CFBatchRequest> instance to use for managing batch requests. If not available, it generates a new instance of <CFBatchRequest>.\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function batch(CFBatchRequest &$queue = null)\n\t{\n\t\tif ($queue)\n\t\t{\n\t\t\t$this->batch_object = $queue;\n\t\t}\n\t\telseif ($this->internal_batch_object)\n\t\t{\n\t\t\t$this->batch_object = &$this->internal_batch_object;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$this->internal_batch_object = new $this->batch_class();\n\t\t\t$this->batch_object = &$this->internal_batch_object;\n\t\t}\n\n\t\t$this->use_batch_flow = true;\n\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Executes the batch request queue by sending all queued requests.\n\t *\n\t * @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.\n\t * @return array An array of <CFResponse> objects.\n\t */\n\tpublic function send($clear_after_send = true)\n\t{\n\t\tif ($this->use_batch_flow)\n\t\t{\n\t\t\t// When we send the request, disable batch flow.\n\t\t\t$this->use_batch_flow = false;\n\n\t\t\t// If we're not caching, simply send the request.\n\t\t\tif (!$this->use_cache_flow)\n\t\t\t{\n\t\t\t\t$response = $this->batch_object->send();\n\t\t\t\t$parsed_data = array_map(array($this, 'parse_callback'), $response);\n\t\t\t\t$parsed_data = new CFArray($parsed_data);\n\n\t\t\t\t// Clear the queue\n\t\t\t\tif ($clear_after_send)\n\t\t\t\t{\n\t\t\t\t\t$this->batch_object->queue = array();\n\t\t\t\t}\n\n\t\t\t\treturn $parsed_data;\n\t\t\t}\n\n\t\t\t// Generate an identifier specific to this particular set of arguments.\n\t\t\t$cache_id = $this->key . '_' . get_class($this) . '_' . sha1(serialize($this->batch_object));\n\n\t\t\t// Instantiate the appropriate caching object.\n\t\t\t$this->cache_object = new $this->cache_class($cache_id, $this->cache_location, $this->cache_expires, $this->cache_compress);\n\n\t\t\tif ($this->delete_cache)\n\t\t\t{\n\t\t\t\t$this->use_cache_flow = false;\n\t\t\t\t$this->delete_cache = false;\n\t\t\t\treturn $this->cache_object->delete();\n\t\t\t}\n\n\t\t\t// Invoke the cache callback function to determine whether to pull data from the cache or make a fresh request.\n\t\t\t$data_set = $this->cache_object->response_manager(array($this, 'cache_callback_batch'), array($this->batch_object));\n\t\t\t$parsed_data = array_map(array($this, 'parse_callback'), $data_set);\n\t\t\t$parsed_data = new CFArray($parsed_data);\n\n\t\t\t// Clear the queue\n\t\t\tif ($clear_after_send)\n\t\t\t{\n\t\t\t\t$this->batch_object->queue = array();\n\t\t\t}\n\n\t\t\t// End!\n\t\t\treturn $parsed_data;\n\t\t}\n\n\t\t// Load the class\n\t\t$null = new CFBatchRequest();\n\t\tunset($null);\n\n\t\tthrow new CFBatchRequest_Exception('You must use $object->batch()->send()');\n\t}\n\n\t/**\n\t * Parses a response body into a PHP object if appropriate.\n\t *\n\t * @param CFResponse|string $response (Required) The <CFResponse> object to parse, or an XML string that would otherwise be a response body.\n\t * @param string $content_type (Optional) The content-type to use when determining how to parse the content.\n\t * @return CFResponse|string A parsed <CFResponse> object, or parsed XML.\n\t */\n\tpublic function parse_callback($response, $headers = null)\n\t{\n\t\t// Bail out\n\t\tif (!$this->parse_the_response) return $response;\n\n\t\t// Shorten this so we have a (mostly) single code path\n\t\tif (isset($response->body))\n\t\t{\n\t\t\tif (is_string($response->body))\n\t\t\t{\n\t\t\t\t$body = $response->body;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\treturn $response;\n\t\t\t}\n\t\t}\n\t\telseif (is_string($response))\n\t\t{\n\t\t\t$body = $response;\n\t\t}\n\t\telse\n\t\t{\n\t\t\treturn $response;\n\t\t}\n\n\t\t// Decompress gzipped content\n\t\tif (isset($headers['content-encoding']))\n\t\t{\n\t\t\tswitch (strtolower(trim($headers['content-encoding'], \"\\x09\\x0A\\x0D\\x20\")))\n\t\t\t{\n\t\t\t\tcase 'gzip':\n\t\t\t\tcase 'x-gzip':\n\t\t\t\t\t$decoder = new CFGzipDecode($body);\n\t\t\t\t\tif ($decoder->parse())\n\t\t\t\t\t{\n\t\t\t\t\t\t$body = $decoder->data;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'deflate':\n\t\t\t\t\tif (($uncompressed = gzuncompress($body)) !== false)\n\t\t\t\t\t{\n\t\t\t\t\t\t$body = $uncompressed;\n\t\t\t\t\t}\n\t\t\t\t\telseif (($uncompressed = gzinflate($body)) !== false)\n\t\t\t\t\t{\n\t\t\t\t\t\t$body = $uncompressed;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// Look for XML cues\n\t\tif (\n\t\t\t(isset($headers['content-type']) && ($headers['content-type'] === 'text/xml' || $headers['content-type'] === 'application/xml')) || // We know it's XML\n\t\t\t(!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\n\t\t)\n\t\t{\n\t\t\t// Strip the default XML namespace to simplify XPath expressions\n\t\t\t$body = str_replace(\"xmlns=\", \"ns=\", $body);\n\n\t\t\ttry {\n\t\t\t\t// Parse the XML body\n\t\t\t\t$body = new $this->parser_class($body);\n\t\t\t}\n\t\t\tcatch (Exception $e)\n\t\t\t{\n\t\t\t\tthrow new Parser_Exception($e->getMessage());\n\t\t\t}\n\t\t}\n\t\t// Look for JSON cues\n\t\telseif (\n\t\t\t(isset($headers['content-type']) && ($headers['content-type'] === 'application/json') || $headers['content-type'] === 'application/x-amz-json-1.0') || // We know it's JSON\n\t\t\t(!isset($headers['content-type']) && $this->util->is_json($body)) // Sniff for JSON\n\t\t)\n\t\t{\n\t\t\t// Normalize JSON to a CFSimpleXML object\n\t\t\t$body = CFJSON::to_xml($body, $this->parser_class);\n\t\t}\n\n\t\t// Put the parsed data back where it goes\n\t\tif (isset($response->body))\n\t\t{\n\t\t\t$response->body = $body;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$response = $body;\n\t\t}\n\n\t\treturn $response;\n\t}\n\n\n\t/*%******************************************************************************************%*/\n\t// CACHING LAYER\n\n\t/**\n\t * Specifies that the resulting <CFResponse> object should be cached according to the settings from\n\t * <set_cache_config()>.\n\t *\n\t * @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\").\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function cache($expires)\n\t{\n\t\t// Die if they haven't used set_cache_config().\n\t\tif (!$this->cache_class)\n\t\t{\n\t\t\tthrow new CFRuntime_Exception('Must call set_cache_config() before using cache()');\n\t\t}\n\n\t\tif (is_string($expires))\n\t\t{\n\t\t\t$expires = strtotime($expires);\n\t\t\t$this->cache_expires = $expires - time();\n\t\t}\n\t\telseif (is_int($expires))\n\t\t{\n\t\t\t$this->cache_expires = $expires;\n\t\t}\n\n\t\t$this->use_cache_flow = true;\n\n\t\treturn $this;\n\t}\n\n\t/**\n\t * The callback function that is executed when the cache doesn't exist or has expired. The response of\n\t * this method is cached. Accepts identical parameters as the <authenticate()> method. Never call this\n\t * method directly -- it is used internally by the caching system.\n\t *\n\t * @param string $operation (Required) Indicates the operation to perform.\n\t * @param array $payload (Required) An associative array of parameters for authenticating. See the individual methods for allowed keys.\n\t * @return CFResponse A parsed HTTP response.\n\t */\n\tpublic function cache_callback($operation, $payload)\n\t{\n\t\t// Disable the cache flow since it's already been handled.\n\t\t$this->use_cache_flow = false;\n\n\t\t// Make the request\n\t\t$response = $this->authenticate($operation, $payload);\n\n\t\t// If this is an XML document, convert it back to a string.\n\t\tif (isset($response->body) && ($response->body instanceof SimpleXMLElement))\n\t\t{\n\t\t\t$response->body = $response->body->asXML();\n\t\t}\n\n\t\treturn $response;\n\t}\n\n\t/**\n\t * Used for caching the results of a batch request. Never call this method directly; it is used\n\t * internally by the caching system.\n\t *\n\t * @param CFBatchRequest $batch (Required) The batch request object to send.\n\t * @return CFResponse A parsed HTTP response.\n\t */\n\tpublic function cache_callback_batch(CFBatchRequest $batch)\n\t{\n\t\treturn $batch->send();\n\t}\n\n\t/**\n\t * Deletes a cached <CFResponse> object using the specified cache storage type.\n\t *\n\t * @return boolean A value of `true` if cached object exists and is successfully deleted, otherwise `false`.\n\t */\n\tpublic function delete_cache()\n\t{\n\t\t$this->use_cache_flow = true;\n\t\t$this->delete_cache = true;\n\n\t\treturn $this;\n\t}\n}\n\n\n/**\n * Contains the functionality for auto-loading service classes.\n */\nclass CFLoader\n{\n\t/*%******************************************************************************************%*/\n\t// AUTO-LOADER\n\n\t/**\n\t * Automatically load classes that aren't included.\n\t *\n\t * @param string $class (Required) The classname to load.\n\t * @return boolean Whether or not the file was successfully loaded.\n\t */\n\tpublic static function autoloader($class)\n\t{\n\t\t$path = dirname(__FILE__) . DIRECTORY_SEPARATOR;\n\n\t\t// Amazon SDK classes\n\t\tif (strstr($class, 'Amazon'))\n\t\t{\n\t\t\tif (file_exists($require_this = $path . 'services' . DIRECTORY_SEPARATOR . str_ireplace('Amazon', '', strtolower($class)) . '.class.php'))\n\t\t\t{\n\t\t\t\trequire_once $require_this;\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\t\t// Utility classes\n\t\telseif (strstr($class, 'CF'))\n\t\t{\n\t\t\tif (file_exists($require_this = $path . 'utilities' . DIRECTORY_SEPARATOR . str_ireplace('CF', '', strtolower($class)) . '.class.php'))\n\t\t\t{\n\t\t\t\trequire_once $require_this;\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\t\t// Load CacheCore\n\t\telseif (strstr($class, 'Cache'))\n\t\t{\n\t\t\tif (file_exists($require_this = $path . 'lib' . DIRECTORY_SEPARATOR . 'cachecore' . DIRECTORY_SEPARATOR . strtolower($class) . '.class.php'))\n\t\t\t{\n\t\t\t\trequire_once $require_this;\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\t\t// Load RequestCore\n\t\telseif (strstr($class, 'RequestCore') || strstr($class, 'ResponseCore'))\n\t\t{\n\t\t\tif (file_exists($require_this = $path . 'lib' . DIRECTORY_SEPARATOR . 'requestcore' . DIRECTORY_SEPARATOR . 'requestcore.class.php'))\n\t\t\t{\n\t\t\t\trequire_once $require_this;\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\t\t// Load Transmogrifier\n\t\telseif (strstr($class, 'Transmogrifier'))\n\t\t{\n\t\t\tif (file_exists($require_this = $path . 'lib' . DIRECTORY_SEPARATOR . 'dom' . DIRECTORY_SEPARATOR . 'Transmogrifier.php'))\n\t\t\t{\n\t\t\t\trequire_once $require_this;\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\t\t// Load Authentication Signers\n\t\telseif (strstr($class, 'Auth'))\n\t\t{\n\t\t\tif (file_exists($require_this = $path . 'authentication' . DIRECTORY_SEPARATOR . str_replace('auth', 'signature_', strtolower($class)) . '.class.php'))\n\t\t\t{\n\t\t\t\trequire_once $require_this;\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\t\t// Load Signer interface\n\t\telseif ($class === 'Signer')\n\t\t{\n\t\t\tif (!interface_exists('Signable', false) &&\n\t\t\t\tfile_exists($require_this = $path . 'authentication' . DIRECTORY_SEPARATOR . 'signable.interface.php'))\n\t\t\t{\n\t\t\t\trequire_once $require_this;\n\t\t\t}\n\n\t\t\tif (file_exists($require_this = $path . 'authentication' . DIRECTORY_SEPARATOR . 'signer.abstract.php'))\n\t\t\t{\n\t\t\t\trequire_once $require_this;\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\t\t// Load Symfony YAML classes\n\t\telseif (strstr($class, 'sfYaml'))\n\t\t{\n\t\t\tif (file_exists($require_this = $path . 'lib' . DIRECTORY_SEPARATOR . 'yaml' . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'sfYaml.php'))\n\t\t\t{\n\t\t\t\trequire_once $require_this;\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\t\treturn false;\n\t}\n}\n\n// Register the autoloader.\nspl_autoload_register(array('CFLoader', 'autoloader'));\n\n\n/*%******************************************************************************************%*/\n// CONFIGURATION\n\n// If config auto-discovery is explicitly disabled, stop here\nif (defined('AWS_DISABLE_CONFIG_AUTO_DISCOVERY')) return;\n\n// Look for include file in the same directory (e.g. `./config.inc.php`).\nif (file_exists(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'config.inc.php'))\n{\n\tinclude_once dirname(__FILE__) . DIRECTORY_SEPARATOR . 'config.inc.php';\n}\n// Fallback to `~/.aws/sdk/config.inc.php`\nelse\n{\n\tif (!isset($_ENV['HOME']) && isset($_SERVER['HOME']))\n\t{\n\t\t$_ENV['HOME'] = $_SERVER['HOME'];\n\t}\n\telseif (!isset($_ENV['HOME']) && !isset($_SERVER['HOME']))\n\t{\n\t\t$os = strtolower(PHP_OS);\n\t\tif (in_array($os, array('windows', 'winnt', 'win32')))\n\t\t{\n\t\t\t$_ENV['HOME'] = false;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$dir = exec('(cd ~ && pwd) 2>&1', $out, $exit);\n\t\t\tif ($exit === 0)\n\t\t\t{\n\t\t\t\t$_ENV['HOME'] = trim($dir);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\terror_log('Failed to determine HOME directory after trying \"' . $dir . '\" (exit code ' . $exit . ')');\n\t\t\t\t$_ENV['HOME'] = false;\n\t\t\t}\n\t\t}\n\n\t\tif (!$_ENV['HOME'])\n\t\t{\n\t\t\tswitch ($os)\n\t\t\t{\n\t\t\t\tcase 'darwin':\n\t\t\t\t\t$_ENV['HOME'] = '/Users/' . get_current_user();\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'windows':\n\t\t\t\tcase 'winnt':\n\t\t\t\tcase 'win32':\n\t\t\t\t\t$_ENV['HOME'] = 'c:' . DIRECTORY_SEPARATOR . 'Documents and Settings' . DIRECTORY_SEPARATOR . get_current_user();\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\t$_ENV['HOME'] = '/home/' . get_current_user();\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t$path = DIRECTORY_SEPARATOR . '.aws' . DIRECTORY_SEPARATOR . 'sdk' . DIRECTORY_SEPARATOR . 'config.inc.php';\n\tif (isset($_ENV['HOME']) && file_exists($_ENV['HOME'] . $path))\n\t{\n\t\tinclude_once $_ENV['HOME'] . $path;\n\t}\n\n\tunset($os, $dir, $out, $exit, $path);\n}\n"
  },
  {
    "path": "services/s3.class.php",
    "content": "<?php\n/*\n * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\").\n * You may not use this file except in compliance with the License.\n * A copy of the License is located at\n *\n *  http://aws.amazon.com/apache2.0\n *\n * or in the \"license\" file accompanying this file. This file is distributed\n * on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either\n * express or implied. See the License for the specific language governing\n * permissions and limitations under the License.\n */\n\n\n/*%******************************************************************************************%*/\n// EXCEPTIONS\n\n/**\n * Default S3 Exception.\n */\nclass S3_Exception extends Exception {}\n\n\n/*%******************************************************************************************%*/\n// MAIN CLASS\n\n/**\n * Amazon S3 is a web service that enables you to store data in the cloud. You can then download the data\n * or use the data with other AWS services, such as Amazon Elastic Cloud Computer (EC2).\n *\n * Amazon Simple Storage Service (Amazon S3) is storage for the Internet. You can use Amazon S3 to store\n * and retrieve any amount of data at any time, from anywhere on the web. You can accomplish these tasks\n * using the AWS Management Console, which is a simple and intuitive web interface.\n *\n * To get the most out of Amazon S3, you need to understand a few simple concepts. Amazon S3 stores data\n * as objects in buckets. An object is comprised of a file and optionally any metadata that describes\n * that file.\n *\n * To store an object in Amazon S3, you upload the file you want to store to a bucket. When you upload a\n * file, you can set permissions on the object as well as any metadata.\n *\n * Buckets are the containers for objects. You can have one or more buckets. For each bucket, you can control\n * access to the bucket (who can create, delete, and list objects in the bucket), view access logs for the\n * bucket and its objects, and choose the geographical region where Amazon S3 will store the bucket and its\n * contents.\n *\n * Visit <http://aws.amazon.com/s3/> for more information.\n *\n * @version 2012.10.02\n * @license See the included NOTICE.md file for more information.\n * @copyright See the included NOTICE.md file for more information.\n * @link http://aws.amazon.com/s3/ Amazon Simple Storage Service\n * @link http://aws.amazon.com/documentation/s3/ Amazon Simple Storage Service documentation\n */\nclass AmazonS3 extends CFRuntime\n{\n\t/*%******************************************************************************************%*/\n\t// REGIONAL ENDPOINTS\n\n\t/**\n\t * Specify the queue URL for the US-Standard (Northern Virginia & Washington State) Region.\n\t */\n\tconst REGION_US_E1 = 's3.amazonaws.com';\n\n\t/**\n\t * Specify the queue URL for the US-Standard (Northern Virginia & Washington State) Region.\n\t */\n\tconst REGION_VIRGINIA = self::REGION_US_E1;\n\n\t/**\n\t * Specify the queue URL for the US-Standard (Northern Virginia & Washington State) Region.\n\t */\n\tconst REGION_US_STANDARD = self::REGION_US_E1;\n\n\t/**\n\t * Specify the queue URL for the US-West 1 (Northern California) Region.\n\t */\n\tconst REGION_US_W1 = 's3-us-west-1.amazonaws.com';\n\n\t/**\n\t * Specify the queue URL for the US-West 1 (Northern California) Region.\n\t */\n\tconst REGION_CALIFORNIA = self::REGION_US_W1;\n\n\t/**\n\t * Specify the queue URL for the US-West 2 (Oregon) Region.\n\t */\n\tconst REGION_US_W2 = 's3-us-west-2.amazonaws.com';\n\n\t/**\n\t * Specify the queue URL for the US-West 2 (Oregon) Region.\n\t */\n\tconst REGION_OREGON = self::REGION_US_W2;\n\n\t/**\n\t * Specify the queue URL for the EU (Ireland) Region.\n\t */\n\tconst REGION_EU_W1 = 's3-eu-west-1.amazonaws.com';\n\n\t/**\n\t * Specify the queue URL for the EU (Ireland) Region.\n\t */\n\tconst REGION_IRELAND = self::REGION_EU_W1;\n\n\t/**\n\t * Specify the queue URL for the Asia Pacific (Singapore) Region.\n\t */\n\tconst REGION_APAC_SE1 = 's3-ap-southeast-1.amazonaws.com';\n\n\t/**\n\t * Specify the queue URL for the Asia Pacific (Singapore) Region.\n\t */\n\tconst REGION_SINGAPORE = self::REGION_APAC_SE1;\n\n\t/**\n\t * Specify the queue URL for the Asia Pacific (Sydney) Region.\n\t */\n\tconst REGION_APAC_SE2 = 's3-ap-southeast-2.amazonaws.com';\n\n\t/**\n\t * Specify the queue URL for the Asia Pacific (Sydney) Region.\n\t */\n\tconst REGION_SYDNEY = self::REGION_APAC_SE2;\n\n\t/**\n\t * Specify the queue URL for the Asia Pacific (Japan) Region.\n\t */\n\tconst REGION_APAC_NE1 = 's3-ap-northeast-1.amazonaws.com';\n\n\t/**\n\t * Specify the queue URL for the Asia Pacific (Japan) Region.\n\t */\n\tconst REGION_TOKYO = self::REGION_APAC_NE1;\n\n\t/**\n\t * Specify the queue URL for the South America (Sao Paulo) Region.\n\t */\n\tconst REGION_SA_E1 = 's3-sa-east-1.amazonaws.com';\n\n\t/**\n\t * Specify the queue URL for the South America (Sao Paulo) Region.\n\t */\n\tconst REGION_SAO_PAULO = self::REGION_SA_E1;\n\n\t/**\n\t * Specify the queue URL for the United States GovCloud Region.\n\t */\n\tconst REGION_US_GOV1 = 's3-us-gov-west-1.amazonaws.com';\n\n\t/**\n\t * Specify the queue URL for the United States GovCloud FIPS 140-2 Region.\n\t */\n\tconst REGION_US_GOV1_FIPS = 's3-fips-us-gov-west-1.amazonaws.com';\n\n\t/**\n\t * The default endpoint.\n\t */\n\tconst DEFAULT_URL = self::REGION_US_E1;\n\n\n\t/*%******************************************************************************************%*/\n\t// REGIONAL WEBSITE ENDPOINTS\n\n\t/**\n\t * Specify the queue URL for the US-Standard (Northern Virginia & Washington State) Website Region.\n\t */\n\tconst REGION_US_E1_WEBSITE = 's3-website-us-east-1.amazonaws.com';\n\n\t/**\n\t * Specify the queue URL for the US-Standard (Northern Virginia & Washington State) Website Region.\n\t */\n\tconst REGION_VIRGINIA_WEBSITE = self::REGION_US_E1_WEBSITE;\n\n\t/**\n\t * Specify the queue URL for the US-Standard (Northern Virginia & Washington State) Website Region.\n\t */\n\tconst REGION_US_STANDARD_WEBSITE = self::REGION_US_E1_WEBSITE;\n\n\t/**\n\t * Specify the queue URL for the US-West 1 (Northern California) Website Region.\n\t */\n\tconst REGION_US_W1_WEBSITE = 's3-website-us-west-1.amazonaws.com';\n\n\t/**\n\t * Specify the queue URL for the US-West 1 (Northern California) Website Region.\n\t */\n\tconst REGION_CALIFORNIA_WEBSITE = self::REGION_US_W1_WEBSITE;\n\n\t/**\n\t * Specify the queue URL for the US-West 2 (Oregon) Website Region.\n\t */\n\tconst REGION_US_W2_WEBSITE = 's3-website-us-west-2.amazonaws.com';\n\n\t/**\n\t * Specify the queue URL for the US-West 2 (Oregon) Website Region.\n\t */\n\tconst REGION_OREGON_WEBSITE = self::REGION_US_W2_WEBSITE;\n\n\t/**\n\t * Specify the queue URL for the EU (Ireland) Website Region.\n\t */\n\tconst REGION_EU_W1_WEBSITE = 's3-website-eu-west-1.amazonaws.com';\n\n\t/**\n\t * Specify the queue URL for the EU (Ireland) Website Region.\n\t */\n\tconst REGION_IRELAND_WEBSITE = self::REGION_EU_W1_WEBSITE;\n\n\t/**\n\t * Specify the queue URL for the Asia Pacific (Singapore) Website Region.\n\t */\n\tconst REGION_APAC_SE1_WEBSITE = 's3-website-ap-southeast-1.amazonaws.com';\n\n\t/**\n\t * Specify the queue URL for the Asia Pacific (Singapore) Website Region.\n\t */\n\tconst REGION_SINGAPORE_WEBSITE = self::REGION_APAC_SE1_WEBSITE;\n\n\t/**\n\t * Specify the queue URL for the Asia Pacific (Sydney) Website Region.\n\t */\n\tconst REGION_APAC_SE2_WEBSITE = 's3-website-ap-southeast-2.amazonaws.com';\n\n\t/**\n\t * Specify the queue URL for the Asia Pacific (Sydney) Website Region.\n\t */\n\tconst REGION_SYDNEY_WEBSITE = self::REGION_APAC_SE2_WEBSITE;\n\n\t/**\n\t * Specify the queue URL for the Asia Pacific (Japan) Website Region.\n\t */\n\tconst REGION_APAC_NE1_WEBSITE = 's3-website-ap-northeast-1.amazonaws.com';\n\n\t/**\n\t * Specify the queue URL for the Asia Pacific (Japan) Website Region.\n\t */\n\tconst REGION_TOKYO_WEBSITE = self::REGION_APAC_NE1_WEBSITE;\n\n\t/**\n\t * Specify the queue URL for the South America (Sao Paulo) Website Region.\n\t */\n\tconst REGION_SA_E1_WEBSITE = 's3-website-sa-east-1.amazonaws.com';\n\n\t/**\n\t * Specify the queue URL for the South America (Sao Paulo) Website Region.\n\t */\n\tconst REGION_SAO_PAULO_WEBSITE = self::REGION_SA_E1_WEBSITE;\n\n\t/**\n\t * Specify the queue URL for the United States GovCloud Website Region.\n\t */\n\tconst REGION_US_GOV1_WEBSITE = 's3-website-us-gov-west-1.amazonaws.com';\n\n\n\t/*%******************************************************************************************%*/\n\t// ACL\n\n\t/**\n\t * ACL: Owner-only read/write.\n\t */\n\tconst ACL_PRIVATE = 'private';\n\n\t/**\n\t * ACL: Owner read/write, public read.\n\t */\n\tconst ACL_PUBLIC = 'public-read';\n\n\t/**\n\t * ACL: Public read/write.\n\t */\n\tconst ACL_OPEN = 'public-read-write';\n\n\t/**\n\t * ACL: Owner read/write, authenticated read.\n\t */\n\tconst ACL_AUTH_READ = 'authenticated-read';\n\n\t/**\n\t * ACL: Bucket owner read.\n\t */\n\tconst ACL_OWNER_READ = 'bucket-owner-read';\n\n\t/**\n\t * ACL: Bucket owner full control.\n\t */\n\tconst ACL_OWNER_FULL_CONTROL = 'bucket-owner-full-control';\n\n\n\t/*%******************************************************************************************%*/\n\t// GRANTS\n\n\t/**\n\t * When applied to a bucket, grants permission to list the bucket. When applied to an object, this\n\t * grants permission to read the object data and/or metadata.\n\t */\n\tconst GRANT_READ = 'READ';\n\n\t/**\n\t * When applied to a bucket, grants permission to create, overwrite, and delete any object in the\n\t * bucket. This permission is not supported for objects.\n\t */\n\tconst GRANT_WRITE = 'WRITE';\n\n\t/**\n\t * Grants permission to read the ACL for the applicable bucket or object. The owner of a bucket or\n\t * object always has this permission implicitly.\n\t */\n\tconst GRANT_READ_ACP = 'READ_ACP';\n\n\t/**\n\t * Gives permission to overwrite the ACP for the applicable bucket or object. The owner of a bucket\n\t * or object always has this permission implicitly. Granting this permission is equivalent to granting\n\t * FULL_CONTROL because the grant recipient can make any changes to the ACP.\n\t */\n\tconst GRANT_WRITE_ACP = 'WRITE_ACP';\n\n\t/**\n\t * Provides READ, WRITE, READ_ACP, and WRITE_ACP permissions. It does not convey additional rights and\n\t * is provided only for convenience.\n\t */\n\tconst GRANT_FULL_CONTROL = 'FULL_CONTROL';\n\n\n\t/*%******************************************************************************************%*/\n\t// USERS\n\n\t/**\n\t * The \"AuthenticatedUsers\" group for access control policies.\n\t */\n\tconst USERS_AUTH = 'http://acs.amazonaws.com/groups/global/AuthenticatedUsers';\n\n\t/**\n\t * The \"AllUsers\" group for access control policies.\n\t */\n\tconst USERS_ALL = 'http://acs.amazonaws.com/groups/global/AllUsers';\n\n\t/**\n\t * The \"LogDelivery\" group for access control policies.\n\t */\n\tconst USERS_LOGGING = 'http://acs.amazonaws.com/groups/s3/LogDelivery';\n\n\n\t/*%******************************************************************************************%*/\n\t// PATTERNS\n\n\t/**\n\t * PCRE: Match all items\n\t */\n\tconst PCRE_ALL = '/.*/i';\n\n\n\t/*%******************************************************************************************%*/\n\t// STORAGE\n\n\t/**\n\t * Standard storage redundancy.\n\t */\n\tconst STORAGE_STANDARD = 'STANDARD';\n\n\t/**\n\t * Reduced storage redundancy.\n\t */\n\tconst STORAGE_REDUCED = 'REDUCED_REDUNDANCY';\n\n\t/**\n\t * Storage in Glacier.\n\t */\n\tconst STORAGE_GLACIER = 'GLACIER';\n\n\n\t/*%******************************************************************************************%*/\n\t// PROPERTIES\n\n\t/**\n\t * The request URL.\n\t */\n\tpublic $request_url;\n\n\t/**\n\t * The virtual host setting.\n\t */\n\tpublic $vhost;\n\n\t/**\n\t * The base XML elements to use for access control policy methods.\n\t */\n\tpublic $base_acp_xml;\n\n\t/**\n\t * The base XML elements to use for creating buckets in regions.\n\t */\n\tpublic $base_location_constraint;\n\n\t/**\n\t * The base XML elements to use for logging methods.\n\t */\n\tpublic $base_logging_xml;\n\n\t/**\n\t * The base XML elements to use for notifications.\n\t */\n\tpublic $base_notification_xml;\n\n\t/**\n\t * The base XML elements to use for versioning.\n\t */\n\tpublic $base_versioning_xml;\n\n\t/**\n\t * The base XML elements to use for completing a multipart upload.\n\t */\n\tpublic $complete_mpu_xml;\n\n\t/**\n\t * The base XML elements to use for website support.\n\t */\n\tpublic $website_config_xml;\n\n\t/**\n\t * The base XML elements to use for multi-object delete support.\n\t */\n\tpublic $multi_object_delete_xml;\n\n\t/**\n\t * The base XML elements to use for object expiration support.\n\t */\n\tpublic $object_expiration_xml;\n\n\t/**\n\t * The base XML elements to use for bucket tagging.\n\t */\n\tpublic $bucket_tagging_xml;\n\n\t/**\n\t * The base XML elements to use for CORS support.\n\t */\n\tpublic $cors_config_xml;\n\n\t/**\n\t * The base XML elements to use for restoration requests.\n\t */\n\tpublic $restore_request_xml;\n\n\t/**\n\t * The DNS vs. Path-style setting.\n\t */\n\tpublic $path_style = false;\n\n\t/**\n\t * The state of whether the prefix change is temporary or permanent.\n\t */\n\tpublic $temporary_prefix = false;\n\n\n\t/*%******************************************************************************************%*/\n\t// CONSTRUCTOR\n\n\t/**\n\t * Constructs a new instance of <AmazonS3>.\n\t *\n\t * @param array $options (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<li><code>credentials</code> - <code>string</code> - Optional - The name of the credential set to use for authentication.</li>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * \t<li><code>token</code> - <code>string</code> - Optional - An AWS session token.</li></ul>\n\t * @return void\n\t */\n\tpublic function __construct(array $options = array())\n\t{\n\t\t$this->vhost = null;\n\t\t$this->api_version = '2006-03-01';\n\t\t$this->hostname = self::DEFAULT_URL;\n\n\t\t$this->base_acp_xml             = '<?xml version=\"1.0\" encoding=\"UTF-8\"?><AccessControlPolicy xmlns=\"http://s3.amazonaws.com/doc/latest/\"/>';\n\t\t$this->base_location_constraint = '<?xml version=\"1.0\" encoding=\"UTF-8\"?><CreateBucketConfiguration xmlns=\"http://s3.amazonaws.com/doc/' . $this->api_version . '/\"><LocationConstraint/></CreateBucketConfiguration>';\n\t\t$this->base_logging_xml         = '<?xml version=\"1.0\" encoding=\"utf-8\"?><BucketLoggingStatus xmlns=\"http://doc.s3.amazonaws.com/' . $this->api_version . '\"/>';\n\t\t$this->base_notification_xml    = '<?xml version=\"1.0\" encoding=\"utf-8\"?><NotificationConfiguration/>';\n\t\t$this->base_versioning_xml      = '<?xml version=\"1.0\" encoding=\"utf-8\"?><VersioningConfiguration xmlns=\"http://s3.amazonaws.com/doc/' . $this->api_version . '/\"/>';\n\t\t$this->complete_mpu_xml         = '<?xml version=\"1.0\" encoding=\"utf-8\"?><CompleteMultipartUpload/>';\n\t\t$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>';\n\t\t$this->multi_object_delete_xml  = '<?xml version=\"1.0\" encoding=\"utf-8\"?><Delete/>';\n\t\t$this->object_expiration_xml    = '<?xml version=\"1.0\" encoding=\"utf-8\"?><LifecycleConfiguration/>';\n\t\t$this->bucket_tagging_xml       = '<?xml version=\"1.0\" encoding=\"utf-8\"?><Tagging><TagSet/></Tagging>';\n\t\t$this->cors_config_xml          = '<?xml version=\"1.0\" encoding=\"utf-8\"?><CORSConfiguration />';\n\t\t$this->restore_request_xml      = '<?xml version=\"1.0\" encoding=\"utf-8\"?><RestoreRequest xmlns=\"http://s3.amazonaws.com/doc/' . $this->api_version . '\"/>';\n\n\t\tparent::__construct($options);\n\t}\n\n\n\t/*%******************************************************************************************%*/\n\t// AUTHENTICATION\n\n\t/**\n\t * Authenticates a connection to Amazon S3. Do not use directly unless implementing custom methods for\n\t * this class.\n\t *\n\t * @param string $operation (Required) The name of the bucket to operate on (S3 Only).\n\t * @param array $payload (Required) An associative array of parameters for authenticating. See inline comments for allowed keys.\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/S3_Authentication.html REST authentication\n\t */\n\tpublic function authenticate($operation, $payload)\n\t{\n\t\t/*\n\t\t * Overriding or extending this class? You can pass the following \"magic\" keys into $opt.\n\t\t *\n\t\t * ## verb, resource, sub_resource and query_string ##\n\t\t * \t<verb> /<resource>?<sub_resource>&<query_string>\n\t\t * \tGET /filename.txt?versions&prefix=abc&max-items=1\n\t\t *\n\t\t * ## versionId, uploadId, partNumber, response-* ##\n\t\t * \tThese don't follow the same rules as above, in that the they needs to be signed, while\n\t\t * \tother query_string values do not.\n\t\t *\n\t\t * ## curlopts ##\n\t\t * \tThese values get passed directly to the cURL methods in RequestCore.\n\t\t *\n\t\t * ## fileUpload, fileDownload, seekTo ##\n\t\t * \tThese are slightly modified and then passed to the cURL methods in RequestCore.\n\t\t *\n\t\t * ## headers ##\n\t\t * \t$opt['headers'] is an array, whose keys are HTTP headers to be sent.\n\t\t *\n\t\t * ## body ##\n\t\t * \tThis is the request body that is sent to the server via PUT/POST.\n\t\t *\n\t\t * ## preauth ##\n\t\t * \tThis is a hook that tells authenticate() to generate a pre-authenticated URL.\n\t\t *\n\t\t * ## returnCurlHandle ##\n\t\t * \tTells authenticate() to return the cURL handle for the request instead of executing it.\n\t\t */\n\n\t\t// Rename variables (to overcome inheritence issues)\n\t\t$bucket = $operation;\n\t\t$opt = $payload;\n\n\t\t// Validate the S3 bucket name\n\t\tif (!$this->validate_bucketname_support($bucket))\n\t\t{\n\t\t\t// @codeCoverageIgnoreStart\n\t\t\tthrow 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.');\n\t\t\t// @codeCoverageIgnoreEnd\n\t\t}\n\n\t\t// Die if $opt isn't set.\n\t\tif (!$opt) return false;\n\n\t\t$method_arguments = func_get_args();\n\n\t\t// Use the caching flow to determine if we need to do a round-trip to the server.\n\t\tif ($this->use_cache_flow)\n\t\t{\n\t\t\t// Generate an identifier specific to this particular set of arguments.\n\t\t\t$cache_id = $this->key . '_' . get_class($this) . '_' . $bucket . '_' . sha1(serialize($method_arguments));\n\n\t\t\t// Instantiate the appropriate caching object.\n\t\t\t$this->cache_object = new $this->cache_class($cache_id, $this->cache_location, $this->cache_expires, $this->cache_compress);\n\n\t\t\tif ($this->delete_cache)\n\t\t\t{\n\t\t\t\t$this->use_cache_flow = false;\n\t\t\t\t$this->delete_cache = false;\n\t\t\t\treturn $this->cache_object->delete();\n\t\t\t}\n\n\t\t\t// Invoke the cache callback function to determine whether to pull data from the cache or make a fresh request.\n\t\t\t$data = $this->cache_object->response_manager(array($this, 'cache_callback'), $method_arguments);\n\n\t\t\tif ($this->parse_the_response)\n\t\t\t{\n\t\t\t\t// Parse the XML body\n\t\t\t\t$data = $this->parse_callback($data);\n\t\t\t}\n\n\t\t\t// End!\n\t\t\treturn $data;\n\t\t}\n\n\t\t// If we haven't already set a resource prefix and the bucket name isn't DNS-valid...\n\t\tif ((!$this->resource_prefix && !$this->validate_bucketname_create($bucket)) || $this->path_style)\n\t\t{\n\t\t\t// Fall back to the older path-style URI\n\t\t\t$this->set_resource_prefix('/' . $bucket);\n\t\t\t$this->temporary_prefix = true;\n\t\t}\n\n\t\t// If the bucket name has periods and we are using SSL, we need to switch to path style URLs\n\t\t$bucket_name_may_cause_ssl_wildcard_failures = false;\n\t\tif ($this->use_ssl && strpos($bucket, '.') !== false)\n\t\t{\n\t\t\t$bucket_name_may_cause_ssl_wildcard_failures = true;\n\t\t}\n\n\t\t// Determine hostname\n\t\t$scheme = $this->use_ssl ? 'https://' : 'http://';\n\t\tif ($bucket_name_may_cause_ssl_wildcard_failures || $this->resource_prefix || $this->path_style)\n\t\t{\n\t\t\t// Use bucket-in-path method\n\t\t\t$hostname = $this->hostname . $this->resource_prefix . (($bucket === '' || $this->resource_prefix === '/' . $bucket) ? '' : ('/' . $bucket));\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$hostname = $this->vhost ? $this->vhost : (($bucket === '') ? $this->hostname : ($bucket . '.') . $this->hostname);\n\t\t}\n\n\t\t// Get the UTC timestamp in RFC 2616 format\n\t\t$date = gmdate(CFUtilities::DATE_FORMAT_RFC2616, time());\n\n\t\t// Storage for request parameters.\n\t\t$resource = '';\n\t\t$sub_resource = '';\n\t\t$querystringparams = array();\n\t\t$signable_querystringparams = array();\n\t\t$string_to_sign = '';\n\t\t$headers = array(\n\t\t\t'Content-MD5' => '',\n\t\t\t'Content-Type' => 'application/x-www-form-urlencoded',\n\t\t\t'Date' => $date\n\t\t);\n\n\t\t/*%******************************************************************************************%*/\n\n\t\t// Do we have an authentication token?\n\t\tif ($this->auth_token)\n\t\t{\n\t\t\t$headers['X-Amz-Security-Token'] = $this->auth_token;\n\t\t}\n\n\t\t// Handle specific resources\n\t\tif (isset($opt['resource']))\n\t\t{\n\t\t\t$resource .= $opt['resource'];\n\t\t}\n\n\t\t// Merge query string values\n\t\tif (isset($opt['query_string']))\n\t\t{\n\t\t\t$querystringparams = array_merge($querystringparams, $opt['query_string']);\n\t\t}\n\t\t$query_string = $this->util->to_query_string($querystringparams);\n\n\t\t// Merge the signable query string values. Must be alphabetical.\n\t\t$signable_list = array(\n\t\t\t'partNumber',\n\t\t\t'response-cache-control',\n\t\t\t'response-content-disposition',\n\t\t\t'response-content-encoding',\n\t\t\t'response-content-language',\n\t\t\t'response-content-type',\n\t\t\t'response-expires',\n\t\t\t'uploadId',\n\t\t\t'versionId'\n\t\t);\n\t\tforeach ($signable_list as $item)\n\t\t{\n\t\t\tif (isset($opt[$item]))\n\t\t\t{\n\t\t\t\t$signable_querystringparams[$item] = $opt[$item];\n\t\t\t}\n\t\t}\n\t\t$signable_query_string = $this->util->to_query_string($signable_querystringparams);\n\n\t\t// Merge the HTTP headers\n\t\tif (isset($opt['headers']))\n\t\t{\n\t\t\t$headers = array_merge($headers, $opt['headers']);\n\t\t}\n\n\t\t// Compile the URI to request\n\t\t$conjunction = '?';\n\t\t$signable_resource = '/' . str_replace('%2F', '/', rawurlencode($resource));\n\t\t$non_signable_resource = '';\n\n\t\tif (isset($opt['sub_resource']))\n\t\t{\n\t\t\t$signable_resource .= $conjunction . rawurlencode($opt['sub_resource']);\n\t\t\t$conjunction = '&';\n\t\t}\n\t\tif ($signable_query_string !== '')\n\t\t{\n\t\t\t$signable_query_string = $conjunction . $signable_query_string;\n\t\t\t$conjunction = '&';\n\t\t}\n\t\tif ($query_string !== '')\n\t\t{\n\t\t\t$non_signable_resource .= $conjunction . $query_string;\n\t\t\t$conjunction = '&';\n\t\t}\n\t\tif (substr($hostname, -1) === substr($signable_resource, 0, 1))\n\t\t{\n\t\t\t$signable_resource = ltrim($signable_resource, '/');\n\t\t}\n\n\t\t$this->request_url = $scheme . $hostname . $signable_resource . $signable_query_string . $non_signable_resource;\n\n\t\tif (isset($opt['location']))\n\t\t{\n\t\t\t$this->request_url = $opt['location'];\n\t\t}\n\n\t\t// Gather information to pass along to other classes.\n\t\t$helpers = array(\n\t\t\t'utilities' => $this->utilities_class,\n\t\t\t'request' => $this->request_class,\n\t\t\t'response' => $this->response_class,\n\t\t);\n\n\t\t// Instantiate the request class\n\t\t$request = new $this->request_class($this->request_url, $this->proxy, $helpers, $this->credentials);\n\n\t\t// Update RequestCore settings\n\t\t$request->request_class = $this->request_class;\n\t\t$request->response_class = $this->response_class;\n\t\t$request->ssl_verification = $this->ssl_verification;\n\n\t\t// Pass along registered stream callbacks\n\t\tif ($this->registered_streaming_read_callback)\n\t\t{\n\t\t\t$request->register_streaming_read_callback($this->registered_streaming_read_callback);\n\t\t}\n\n\t\tif ($this->registered_streaming_write_callback)\n\t\t{\n\t\t\t$request->register_streaming_write_callback($this->registered_streaming_write_callback);\n\t\t}\n\n\t\t// Streaming uploads\n\t\tif (isset($opt['fileUpload']))\n\t\t{\n\t\t\tif (is_resource($opt['fileUpload']))\n\t\t\t{\n\t\t\t\t// Determine the length to read from the stream\n\t\t\t\t$length = null; // From current position until EOF by default, size determined by set_read_stream()\n\n\t\t\t\tif (isset($headers['Content-Length']))\n\t\t\t\t{\n\t\t\t\t\t$length = $headers['Content-Length'];\n\t\t\t\t}\n\t\t\t\telseif (isset($opt['seekTo']))\n\t\t\t\t{\n\t\t\t\t\t// Read from seekTo until EOF by default\n\t\t\t\t\t$stats = fstat($opt['fileUpload']);\n\n\t\t\t\t\tif ($stats && $stats['size'] >= 0)\n\t\t\t\t\t{\n\t\t\t\t\t\t$length = $stats['size'] - (integer) $opt['seekTo'];\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t$request->set_read_stream($opt['fileUpload'], $length);\n\n\t\t\t\tif ($headers['Content-Type'] === 'application/x-www-form-urlencoded')\n\t\t\t\t{\n\t\t\t\t\t$headers['Content-Type'] = 'application/octet-stream';\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$request->set_read_file($opt['fileUpload']);\n\n\t\t\t\t// Determine the length to read from the file\n\t\t\t\t$length = $request->read_stream_size; // The file size by default\n\n\t\t\t\tif (isset($headers['Content-Length']))\n\t\t\t\t{\n\t\t\t\t\t$length = $headers['Content-Length'];\n\t\t\t\t}\n\t\t\t\telseif (isset($opt['seekTo']) && isset($length))\n\t\t\t\t{\n\t\t\t\t\t// Read from seekTo until EOF by default\n\t\t\t\t\t$length -= (integer) $opt['seekTo'];\n\t\t\t\t}\n\n\t\t\t\t$request->set_read_stream_size($length);\n\n\t\t\t\t// Attempt to guess the correct mime-type\n\t\t\t\tif ($headers['Content-Type'] === 'application/x-www-form-urlencoded')\n\t\t\t\t{\n\t\t\t\t\t$extension = explode('.', $opt['fileUpload']);\n\t\t\t\t\t$extension = array_pop($extension);\n\t\t\t\t\t$mime_type = CFMimeTypes::get_mimetype($extension);\n\t\t\t\t\t$headers['Content-Type'] = $mime_type;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t$headers['Content-Length'] = $request->read_stream_size;\n\t\t\t$headers['Content-MD5'] = '';\n\t\t}\n\n\t\t// Handle streaming file offsets\n\t\tif (isset($opt['seekTo']))\n\t\t{\n\t\t\t// Pass the seek position to RequestCore\n\t\t\t$request->set_seek_position((integer) $opt['seekTo']);\n\t\t}\n\n\t\t// Streaming downloads\n\t\tif (isset($opt['fileDownload']))\n\t\t{\n\t\t\tif (is_resource($opt['fileDownload']))\n\t\t\t{\n\t\t\t\t$request->set_write_stream($opt['fileDownload']);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$request->set_write_file($opt['fileDownload']);\n\t\t\t}\n\t\t}\n\n\t\t$curlopts = array();\n\n\t\t// Set custom CURLOPT settings\n\t\tif (isset($opt['curlopts']))\n\t\t{\n\t\t\t$curlopts = $opt['curlopts'];\n\t\t}\n\n\t\t// Debug mode\n\t\tif ($this->debug_mode)\n\t\t{\n\t\t\t$curlopts[CURLOPT_VERBOSE] = true;\n\t\t}\n\n\t\t// Set the curl options.\n\t\tif (count($curlopts))\n\t\t{\n\t\t\t$request->set_curlopts($curlopts);\n\t\t}\n\n\t\t// Do we have a verb?\n\t\tif (isset($opt['verb']))\n\t\t{\n\t\t\t$request->set_method($opt['verb']);\n\t\t\t$string_to_sign .= $opt['verb'] . \"\\n\";\n\t\t}\n\n\t\t// Add headers and content when we have a body\n\t\tif (isset($opt['body']))\n\t\t{\n\t\t\t$request->set_body($opt['body']);\n\t\t\t$headers['Content-Length'] = strlen($opt['body']);\n\n\t\t\tif ($headers['Content-Type'] === 'application/x-www-form-urlencoded')\n\t\t\t{\n\t\t\t\t$headers['Content-Type'] = 'application/octet-stream';\n\t\t\t}\n\n\t\t\tif (!isset($opt['NoContentMD5']) || $opt['NoContentMD5'] !== true)\n\t\t\t{\n\t\t\t\t$headers['Content-MD5'] = $this->util->hex_to_base64(md5($opt['body']));\n\t\t\t}\n\t\t}\n\n\t\t// Handle query-string authentication\n\t\tif (isset($opt['preauth']) && (integer) $opt['preauth'] > 0)\n\t\t{\n\t\t\tunset($headers['Date']);\n\t\t\t$headers['Content-Type'] = '';\n\t\t\t$headers['Expires'] = is_int($opt['preauth']) ? $opt['preauth'] : strtotime($opt['preauth']);\n\t\t}\n\n\t\t// Sort headers\n\t\tuksort($headers, 'strnatcasecmp');\n\n\t\t// Add headers to request and compute the string to sign\n\t\tforeach ($headers as $header_key => $header_value)\n\t\t{\n\t\t\t// Strip linebreaks from header values as they're illegal and can allow for security issues\n\t\t\t$header_value = str_replace(array(\"\\r\", \"\\n\"), '', $header_value);\n\n\t\t\t// Add the header if it has a value\n\t\t\tif ($header_value !== '')\n\t\t\t{\n\t\t\t\t$request->add_header($header_key, $header_value);\n\t\t\t}\n\n\t\t\t// Generate the string to sign\n\t\t\tif (\n\t\t\t\tstrtolower($header_key) === 'content-md5' ||\n\t\t\t\tstrtolower($header_key) === 'content-type' ||\n\t\t\t\tstrtolower($header_key) === 'date' ||\n\t\t\t\t(strtolower($header_key) === 'expires' && isset($opt['preauth']) && (integer) $opt['preauth'] > 0)\n\t\t\t)\n\t\t\t{\n\t\t\t\t$string_to_sign .= $header_value . \"\\n\";\n\t\t\t}\n\t\t\telseif (substr(strtolower($header_key), 0, 6) === 'x-amz-')\n\t\t\t{\n\t\t\t\t$string_to_sign .= strtolower($header_key) . ':' . $header_value . \"\\n\";\n\t\t\t}\n\t\t}\n\n\t\t// Add the signable resource location\n\t\t$string_to_sign .= ($this->resource_prefix ? $this->resource_prefix : '');\n\t\t$string_to_sign .= (($bucket === '' || $this->resource_prefix === '/' . $bucket) ? '' : ('/' . $bucket)) . $signable_resource . urldecode($signable_query_string);\n\n\t\t// Hash the AWS secret key and generate a signature for the request.\n\t\t$signature = base64_encode(hash_hmac('sha1', $string_to_sign, $this->secret_key, true));\n\t\t$request->add_header('Authorization', 'AWS ' . $this->key . ':' . $signature);\n\n\t\t// If we're generating a URL, return the URL to the calling method.\n\t\tif (isset($opt['preauth']) && (integer) $opt['preauth'] > 0)\n\t\t{\n\t\t\t$query_params = array(\n\t\t\t\t'AWSAccessKeyId' => $this->key,\n\t\t\t\t'Expires' => $headers['Expires'],\n\t\t\t\t'Signature' => $signature,\n\t\t\t);\n\n\t\t\t// If using short-term credentials, add the token to the query string\n\t\t\tif ($this->auth_token)\n\t\t\t{\n\t\t\t\t$query_params['x-amz-security-token'] = $this->auth_token;\n\t\t\t}\n\n\t\t\treturn $this->request_url . $conjunction . http_build_query($query_params, '', '&');\n\t\t}\n\t\telseif (isset($opt['preauth']))\n\t\t{\n\t\t\treturn $this->request_url;\n\t\t}\n\n\t\t/*%******************************************************************************************%*/\n\n\t\t// If our changes were temporary, reset them.\n\t\tif ($this->temporary_prefix)\n\t\t{\n\t\t\t$this->temporary_prefix = false;\n\t\t\t$this->resource_prefix = null;\n\t\t}\n\n\t\t// Manage the (newer) batch request API or the (older) returnCurlHandle setting.\n\t\tif ($this->use_batch_flow)\n\t\t{\n\t\t\t$handle = $request->prep_request();\n\t\t\t$this->batch_object->add($handle);\n\t\t\t$this->use_batch_flow = false;\n\n\t\t\treturn $handle;\n\t\t}\n\t\telseif (isset($opt['returnCurlHandle']) && $opt['returnCurlHandle'] === true)\n\t\t{\n\t\t\treturn $request->prep_request();\n\t\t}\n\n\t\t// Send!\n\t\t$request->send_request();\n\n\t\t// Prepare the response\n\t\t$headers = $request->get_response_header();\n\t\t$headers['x-aws-request-url'] = $this->request_url;\n\t\t$headers['x-aws-redirects'] = $this->redirects;\n\t\t$headers['x-aws-stringtosign'] = $string_to_sign;\n\t\t$headers['x-aws-requestheaders'] = $request->request_headers;\n\n\t\t// Did we have a request body?\n\t\tif (isset($opt['body']))\n\t\t{\n\t\t\t$headers['x-aws-requestbody'] = $opt['body'];\n\t\t}\n\n\t\t$data = new $this->response_class($headers, $this->parse_callback($request->get_response_body()), $request->get_response_code());\n\n\t\t// Did Amazon tell us to redirect? Typically happens for multiple rapid requests EU datacenters.\n\t\t// @see: http://docs.amazonwebservices.com/AmazonS3/latest/dev/Redirects.html\n\t\t// @codeCoverageIgnoreStart\n\t\tif ((integer) $request->get_response_code() === 307) // Temporary redirect to new endpoint.\n\t\t{\n\t\t\t$this->redirects++;\n\t\t\t$opt['location'] = $headers['location'];\n\t\t\t$data = $this->authenticate($bucket, $opt);\n\t\t}\n\n\t\t// Was it Amazon's fault the request failed? Retry the request until we reach $max_retries.\n\t\telseif ((integer) $request->get_response_code() === 500 || (integer) $request->get_response_code() === 503)\n\t\t{\n\t\t\tif ($this->redirects <= $this->max_retries)\n\t\t\t{\n\t\t\t\t// Exponential backoff\n\t\t\t\t$delay = (integer) (pow(4, $this->redirects) * 100000);\n\t\t\t\tusleep($delay);\n\t\t\t\t$this->redirects++;\n\t\t\t\t$data = $this->authenticate($bucket, $opt);\n\t\t\t}\n\t\t}\n\t\t// @codeCoverageIgnoreEnd\n\n\t\t// Return!\n\t\t$this->redirects = 0;\n\t\treturn $data;\n\t}\n\n\t/**\n\t * Validates whether or not the specified Amazon S3 bucket name is valid for DNS-style access. This\n\t * method is leveraged by any method that creates buckets.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to validate.\n\t * @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.\n\t */\n\tpublic function validate_bucketname_create($bucket)\n\t{\n\t\t// list_buckets() uses this. Let it pass.\n\t\tif ($bucket === '') return true;\n\n\t\tif (\n\t\t\t($bucket === null || $bucket === false) ||                  // Must not be null or false\n\t\t\tpreg_match('/[^(a-z0-9\\-\\.)]/', $bucket) ||                 // Must be in the lowercase Roman alphabet, period or hyphen\n\t\t\t!preg_match('/^([a-z]|\\d)/', $bucket) ||                    // Must start with a number or letter\n\t\t\t!(strlen($bucket) >= 3 && strlen($bucket) <= 63) ||         // Must be between 3 and 63 characters long\n\t\t\t(strpos($bucket, '..') !== false) ||                        // Bucket names cannot contain two, adjacent periods\n\t\t\t(strpos($bucket, '-.') !== false) ||                        // Bucket names cannot contain dashes next to periods\n\t\t\t(strpos($bucket, '.-') !== false) ||                        // Bucket names cannot contain dashes next to periods\n\t\t\tpreg_match('/(-|\\.)$/', $bucket) ||                         // Bucket names should not end with a dash or period\n\t\t\tpreg_match('/^(?:[0-9]{1,3}\\.){3}[0-9]{1,3}$/', $bucket)    // Must not be formatted as an IP address\n\t\t) return false;\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Validates whether or not the specified Amazon S3 bucket name is valid for path-style access. This\n\t * method is leveraged by any method that reads from buckets.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to validate.\n\t * @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.\n\t */\n\tpublic function validate_bucketname_support($bucket)\n\t{\n\t\t// list_buckets() uses this. Let it pass.\n\t\tif ($bucket === '') return true;\n\n\t\t// Validate\n\t\tif (\n\t\t\t($bucket === null || $bucket === false) ||                  // Must not be null or false\n\t\t\tpreg_match('/[^(a-z0-9_\\-\\.)]/i', $bucket) ||               // Must be in the Roman alphabet, period, hyphen or underscore\n\t\t\t!preg_match('/^([a-z]|\\d)/i', $bucket) ||                   // Must start with a number or letter\n\t\t\t!(strlen($bucket) >= 3 && strlen($bucket) <= 255) ||        // Must be between 3 and 255 characters long\n\t\t\tpreg_match('/^(?:[0-9]{1,3}\\.){3}[0-9]{1,3}$/', $bucket)    // Must not be formatted as an IP address\n\t\t) return false;\n\n\t\treturn true;\n\t}\n\n\t/*%******************************************************************************************%*/\n\t// SETTERS\n\n\t/**\n\t * Sets the region to use for subsequent Amazon S3 operations. This will also reset any prior use of\n\t * <enable_path_style()>.\n\t *\n\t * @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.\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function set_region($region)\n\t{\n\t\t// @codeCoverageIgnoreStart\n\t\t$this->set_hostname($region);\n\n\t\tswitch ($region)\n\t\t{\n\t\t\tcase self::REGION_US_E1: // Northern Virginia\n\t\t\t\t$this->enable_path_style(false);\n\t\t\t\tbreak;\n\n\t\t\tcase self::REGION_EU_W1: // Ireland\n\t\t\t\t$this->enable_path_style(); // Always use path-style access for EU endpoint.\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\t$this->enable_path_style(false);\n\t\t\t\tbreak;\n\n\t\t}\n\t\t// @codeCoverageIgnoreEnd\n\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Sets the virtual host to use in place of the default `bucket.s3.amazonaws.com` domain.\n\t *\n\t * @param string $vhost (Required) The virtual host to use in place of the default `bucket.s3.amazonaws.com` domain.\n\t * @return $this A reference to the current instance.\n\t * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/VirtualHosting.html Virtual Hosting of Buckets\n\t */\n\tpublic function set_vhost($vhost)\n\t{\n\t\t$this->vhost = $vhost;\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Enables the use of the older path-style URI access for all requests.\n\t *\n\t * @param string $style (Optional) Whether or not to enable path-style URI access for all requests. The default value is <code>true</code>.\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function enable_path_style($style = true)\n\t{\n\t\t$this->path_style = $style;\n\t\treturn $this;\n\t}\n\n\n\t/*%******************************************************************************************%*/\n\t// BUCKET METHODS\n\n\t/**\n\t * Creates an Amazon S3 bucket.\n\t *\n\t * Every object stored in Amazon S3 is contained in a bucket. Buckets partition the namespace of\n\t * objects stored in Amazon S3 at the top level. in a bucket, any name can be used for objects.\n\t * However, bucket names must be unique across all of Amazon S3.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to create.\n\t * @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`]\n\t * @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>.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/UsingBucket.html Working with Amazon S3 Buckets\n\t */\n\tpublic function create_bucket($bucket, $region, $acl = self::ACL_PRIVATE, $opt = null)\n\t{\n\t\t// If the bucket contains uppercase letters...\n\t\tif (preg_match('/[A-Z]/', $bucket))\n\t\t{\n\t\t\t// Throw a warning\n\t\t\ttrigger_error('Since DNS-valid bucket names cannot contain uppercase characters, \"' . $bucket . '\" has been automatically converted to \"' . strtolower($bucket) . '\"', E_USER_WARNING);\n\n\t\t\t// Force the bucketname to lowercase\n\t\t\t$bucket = strtolower($bucket);\n\t\t}\n\n\t\t// Validate the S3 bucket name for creation\n\t\tif (!$this->validate_bucketname_create($bucket))\n\t\t{\n\t\t\t// @codeCoverageIgnoreStart\n\t\t\tthrow 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.');\n\t\t\t// @codeCoverageIgnoreEnd\n\t\t}\n\n\t\tif (!$opt) $opt = array();\n\t\t$opt['verb'] = 'PUT';\n\t\t$opt['headers'] = array(\n\t\t\t'Content-Type' => 'application/xml'\n\t\t);\n\n\t\t// Handle Access Control Lists. Can also be passed as an HTTP header.\n\t\tif (isset($acl))\n\t\t{\n\t\t\tif (is_array($acl))\n\t\t\t{\n\t\t\t\t$opt['headers'] = array_merge($opt['headers'], $this->generate_access_policy_headers($acl));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$opt['headers']['x-amz-acl'] = $acl;\n\t\t\t}\n\t\t}\n\n\t\t// Defaults\n\t\t$this->set_region($region); // Also sets path-style\n\t\t$xml = simplexml_load_string($this->base_location_constraint);\n\n\t\tswitch ($region)\n\t\t{\n\t\t\tcase self::REGION_US_E1: // Northern Virginia\n\t\t\t\t$opt['body'] = '';\n\t\t\t\tbreak;\n\n\t\t\tcase self::REGION_EU_W1:    // Ireland\n\t\t\t\t$xml->LocationConstraint = 'EU';\n\t\t\t\t$opt['body'] = $xml->asXML();\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\t$xml->LocationConstraint = str_replace(array('s3-', '.amazonaws.com'), '', $region);\n\t\t\t\t$opt['body'] = $xml->asXML();\n\t\t\t\tbreak;\n\t\t}\n\n\t\t$response = $this->authenticate($bucket, $opt);\n\n\t\t// Make sure we're set back to DNS-style URLs\n\t\t$this->enable_path_style(false);\n\n\t\treturn $response;\n\t}\n\n\t/**\n\t * Gets the region in which the specified Amazon S3 bucket is located.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t */\n\tpublic function get_bucket_region($bucket, $opt = null)\n\t{\n\t\t// Add this to our request\n\t\tif (!$opt) $opt = array();\n\t\t$opt['verb'] = 'GET';\n\t\t$opt['sub_resource'] = 'location';\n\n\t\t// Authenticate to S3\n\t\t$response = $this->authenticate($bucket, $opt);\n\n\t\tif ($response->isOK())\n\t\t{\n\t\t\t// Handle body\n\t\t\t$response->body = (string) $response->body;\n\t\t}\n\n\t\treturn $response;\n\t}\n\n\t/**\n\t * Gets the HTTP headers for the specified Amazon S3 bucket.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t */\n\tpublic function get_bucket_headers($bucket, $opt = null)\n\t{\n\t\tif (!$opt) $opt = array();\n\t\t$opt['verb'] = 'HEAD';\n\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\t/**\n\t * Deletes a bucket from an Amazon S3 account. A bucket must be empty before the bucket itself can be deleted.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param boolean $force (Optional) Whether to force-delete the bucket and all of its contents. The default value is <code>false</code>.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * @return mixed A <CFResponse> object if the bucket was deleted successfully. Returns boolean <code>false</code> if otherwise.\n\t */\n\tpublic function delete_bucket($bucket, $force = false, $opt = null)\n\t{\n\t\t// Set default value\n\t\t$success = true;\n\n\t\tif ($force)\n\t\t{\n\t\t\t// Delete all of the items from the bucket.\n\t\t\t$success = $this->delete_all_object_versions($bucket);\n\t\t}\n\n\t\t// As long as we were successful...\n\t\tif ($success)\n\t\t{\n\t\t\tif (!$opt) $opt = array();\n\t\t\t$opt['verb'] = 'DELETE';\n\n\t\t\treturn $this->authenticate($bucket, $opt);\n\t\t}\n\n\t\t// @codeCoverageIgnoreStart\n\t\treturn false;\n\t\t// @codeCoverageIgnoreEnd\n\t}\n\n\t/**\n\t * Gets a list of all buckets contained in the caller's Amazon S3 account.\n\t *\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t */\n\tpublic function list_buckets($opt = null)\n\t{\n\t\tif (!$opt) $opt = array();\n\t\t$opt['verb'] = 'GET';\n\n\t\treturn $this->authenticate('', $opt);\n\t}\n\n\t/**\n\t * Gets the access control list (ACL) settings for the specified Amazon S3 bucket.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAccessPolicy.html REST Access Control Policy\n\t */\n\tpublic function get_bucket_acl($bucket, $opt = null)\n\t{\n\t\t// Add this to our request\n\t\tif (!$opt) $opt = array();\n\t\t$opt['verb'] = 'GET';\n\t\t$opt['sub_resource'] = 'acl';\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\t/**\n\t * Sets the access control list (ACL) settings for the specified Amazon S3 bucket.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @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>.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAccessPolicy.html REST Access Control Policy\n\t */\n\tpublic function set_bucket_acl($bucket, $acl = self::ACL_PRIVATE, $opt = null)\n\t{\n\t\t// Add this to our request\n\t\tif (!$opt) $opt = array();\n\t\t$opt['verb'] = 'PUT';\n\t\t$opt['sub_resource'] = 'acl';\n\t\t$opt['headers'] = array(\n\t\t\t'Content-Type' => 'application/xml'\n\t\t);\n\n\t\t// Make sure these are defined.\n\t\t// @codeCoverageIgnoreStart\n\t\tif (!$this->credentials->canonical_id || !$this->credentials->canonical_name)\n\t\t{\n\t\t\t// Fetch the data live.\n\t\t\t$canonical = $this->get_canonical_user_id();\n\t\t\t$this->credentials->canonical_id = $canonical['id'];\n\t\t\t$this->credentials->canonical_name = $canonical['display_name'];\n\t\t}\n\t\t// @codeCoverageIgnoreEnd\n\n\t\tif (is_array($acl))\n\t\t{\n\t\t\t$opt['headers'] = array_merge($opt['headers'], $this->generate_access_policy_headers($acl));\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$opt['body'] = '';\n\t\t\t$opt['headers']['x-amz-acl'] = $acl;\n\t\t}\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\n\t/*%******************************************************************************************%*/\n\t// OBJECT METHODS\n\n\t/**\n\t * Creates an Amazon S3 object. After an Amazon S3 bucket is created, objects can be stored in it.\n\t *\n\t * Each standard object can hold up to 5 GB of data. When an object is stored in Amazon S3, the data is streamed\n\t * to multiple storage servers in multiple data centers. This ensures the data remains available in the\n\t * event of internal network or hardware failure.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param string $filename (Required) The file name for the object.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * \t<li><code>encryption</code> - <code>string</code> - Optional - The algorithm to use for encrypting the object. [Allowed values: <code>AES256</code>]</li>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * \t<li><code>seekTo</code> - <code>integer</code> - Optional - The starting position in bytes within the file/stream to upload from.</li>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAccessPolicy.html REST Access Control Policy\n\t */\n\tpublic function create_object($bucket, $filename, $opt = null)\n\t{\n\t\tif (!$opt) $opt = array();\n\n\t\t// Add this to our request\n\t\t$opt['verb'] = 'PUT';\n\t\t$opt['resource'] = $filename;\n\n\t\t// Handle content length. Can also be passed as an HTTP header.\n\t\tif (isset($opt['length']))\n\t\t{\n\t\t\t$opt['headers']['Content-Length'] = $opt['length'];\n\t\t\tunset($opt['length']);\n\t\t}\n\n\t\t// Handle content type. Can also be passed as an HTTP header.\n\t\tif (isset($opt['contentType']))\n\t\t{\n\t\t\t$opt['headers']['Content-Type'] = $opt['contentType'];\n\t\t\tunset($opt['contentType']);\n\t\t}\n\n\t\t// Handle Access Control Lists. Can also be passed as an HTTP header.\n\t\tif (isset($opt['acl']))\n\t\t{\n\t\t\tif (is_array($opt['acl']))\n\t\t\t{\n\t\t\t\t$opt['headers'] = array_merge($opt['headers'], $this->generate_access_policy_headers($opt['acl']));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$opt['headers']['x-amz-acl'] = $opt['acl'];\n\t\t\t}\n\t\t}\n\n\t\t// Handle storage settings. Can also be passed as an HTTP header.\n\t\tif (isset($opt['storage']))\n\t\t{\n\t\t\t$opt['headers']['x-amz-storage-class'] = $opt['storage'];\n\t\t\tunset($opt['storage']);\n\t\t}\n\n\t\t// Handle encryption settings. Can also be passed as an HTTP header.\n\t\tif (isset($opt['encryption']))\n\t\t{\n\t\t\t$opt['headers']['x-amz-server-side-encryption'] = $opt['encryption'];\n\t\t\tunset($opt['encryption']);\n\t\t}\n\n\t\t// URI to redirect to. Can also be passed as an HTTP header.\n\t\tif (isset($opt['redirectTo']))\n\t\t{\n\t\t\t$opt['headers']['x-amz-website-redirect-location'] = $opt['redirectTo'];\n\t\t\tunset($opt['redirectTo']);\n\t\t}\n\n\t\t// Handle meta tags. Can also be passed as an HTTP header.\n\t\tif (isset($opt['meta']))\n\t\t{\n\t\t\tforeach ($opt['meta'] as $meta_key => $meta_value)\n\t\t\t{\n\t\t\t\t// e.g., `My Meta Header` is converted to `x-amz-meta-my-meta-header`.\n\t\t\t\t$opt['headers']['x-amz-meta-' . strtolower(str_replace(' ', '-', $meta_key))] = $meta_value;\n\t\t\t}\n\t\t\tunset($opt['meta']);\n\t\t}\n\n\t\t$opt['headers']['Expect'] = '100-continue';\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\t/**\n\t * Gets the contents of an Amazon S3 object in the specified bucket.\n\t *\n\t * The MD5 value for an object can be retrieved from the ETag HTTP header for any object that was uploaded\n\t * with a normal PUT/POST. This value is incorrect for multipart uploads.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param string $filename (Required) The file name for the object.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t */\n\tpublic function get_object($bucket, $filename, $opt = null)\n\t{\n\t\tif (!$opt) $opt = array();\n\n\t\t// Add this to our request\n\t\t$opt['verb'] = 'GET';\n\t\t$opt['resource'] = $filename;\n\n\t\tif (!isset($opt['headers']) || !is_array($opt['headers']))\n\t\t{\n\t\t\t$opt['headers'] = array();\n\t\t}\n\n\t\tif (isset($opt['lastmodified']))\n\t\t{\n\t\t\t$opt['headers']['If-Modified-Since'] = $opt['lastmodified'];\n\t\t}\n\n\t\tif (isset($opt['etag']))\n\t\t{\n\t\t\t$opt['headers']['If-None-Match'] = $opt['etag'];\n\t\t}\n\n\t\t// Partial content range\n\t\tif (isset($opt['range']))\n\t\t{\n\t\t\t$opt['headers']['Range'] = 'bytes=' . $opt['range'];\n\t\t}\n\n\t\t// GET responses\n\t\tif (isset($opt['response']))\n\t\t{\n\t\t\tforeach ($opt['response'] as $key => $value)\n\t\t\t{\n\t\t\t\t$opt['response-' . $key] = $value;\n\t\t\t\tunset($opt['response'][$key]);\n\t\t\t}\n\t\t}\n\n\t\t// Authenticate to S3\n\t\t$this->parse_the_response = false;\n\t\t$response = $this->authenticate($bucket, $opt);\n\t\t$this->parse_the_response = true;\n\n\t\treturn $response;\n\t}\n\n\t/**\n\t * Gets the HTTP headers for the specified Amazon S3 object.\n\t *\n\t * The MD5 value for an object can be retrieved from the ETag HTTP header for any object that was uploaded\n\t * with a normal PUT/POST. This value is incorrect for multipart uploads.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param string $filename (Required) The file name for the object.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t */\n\tpublic function get_object_headers($bucket, $filename, $opt = null)\n\t{\n\t\t// Add this to our request\n\t\tif (!$opt) $opt = array();\n\t\t$opt['verb'] = 'HEAD';\n\t\t$opt['resource'] = $filename;\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\t/**\n\t * Deletes an Amazon S3 object from the specified bucket.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param string $filename (Required) The file name for the object.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t * @link http://aws.amazon.com/mfa/ Multi-Factor Authentication\n\t */\n\tpublic function delete_object($bucket, $filename, $opt = null)\n\t{\n\t\t// Add this to our request\n\t\tif (!$opt) $opt = array();\n\t\t$opt['verb'] = 'DELETE';\n\t\t$opt['resource'] = $filename;\n\n\t\t// Enable MFA delete?\n\t\t// @codeCoverageIgnoreStart\n\t\tif (isset($opt['MFASerial']) && isset($opt['MFAToken']))\n\t\t{\n\t\t\t$opt['headers'] = array(\n\t\t\t\t'x-amz-mfa' => ($opt['MFASerial'] . ' ' . $opt['MFAToken'])\n\t\t\t);\n\t\t}\n\t\t// @codeCoverageIgnoreEnd\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\t/**\n\t * Deletes one or more specified Amazon S3 objects from the specified bucket.\n\t *\n\t * Since `delete_object()` is designed for deleting a single object, this method is intended to be used\n\t * when there are two or more objects to delete.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<li><code>objects</code> - <code>array</code> - Required - The object references to delete from the bucket. <ul>\n\t * \t\t<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>\n\t * \t\t<li><code>version_id</code> - <code>string</code> - Optional - If the object is versioned, include the version ID to delete.</li>\n\t * \t</ul></li>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t * @link http://aws.amazon.com/mfa/ Multi-Factor Authentication\n\t */\n\tpublic function delete_objects($bucket, $opt = null)\n\t{\n\t\t// Add this to our request\n\t\tif (!$opt) $opt = array();\n\t\t$opt['verb'] = 'POST';\n\t\t$opt['sub_resource'] = 'delete';\n\t\t$opt['body'] = '';\n\n\t\t// Bail out\n\t\tif (!isset($opt['objects']) || !is_array($opt['objects']))\n\t\t{\n\t\t\tthrow new S3_Exception('The ' . __FUNCTION__ . ' method requires the \"objects\" option to be set as an array.');\n\t\t}\n\n\t\t$xml = new SimpleXMLElement($this->multi_object_delete_xml);\n\n\t\t// Add the objects\n\t\tforeach ($opt['objects'] as $object)\n\t\t{\n\t\t\t$xobject = $xml->addChild('Object');\n\t\t\t$node = $xobject->addChild('Key');\n\t\t\t$node[0] = $object['key'];\n\n\t\t\tif (isset($object['version_id']))\n\t\t\t{\n\t\t\t\t$xobject->addChild('VersionId', $object['version_id']);\n\t\t\t}\n\t\t}\n\n\t\t// Quiet mode?\n\t\tif (isset($opt['quiet']))\n\t\t{\n\t\t\t$quiet = 'false';\n\t\t\tif (is_bool($opt['quiet'])) // Boolean\n\t\t\t{\n\t\t\t\t$quiet = $opt['quiet'] ? 'true' : 'false';\n\t\t\t}\n\t\t\telseif (is_string($opt['quiet'])) // String\n\t\t\t{\n\t\t\t\t$quiet = ($opt['quiet'] === 'true') ? 'true' : 'false';\n\t\t\t}\n\n\t\t\t$xml->addChild('Quiet', $quiet);\n\t\t}\n\n\t\t// Enable MFA delete?\n\t\t// @codeCoverageIgnoreStart\n\t\tif (isset($opt['MFASerial']) && isset($opt['MFAToken']))\n\t\t{\n\t\t\t$opt['headers'] = array(\n\t\t\t\t'x-amz-mfa' => ($opt['MFASerial'] . ' ' . $opt['MFAToken'])\n\t\t\t);\n\t\t}\n\t\t// @codeCoverageIgnoreEnd\n\n\t\t$opt['body'] = $xml->asXML();\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\t/**\n\t * Gets a list of all Amazon S3 objects in the specified bucket.\n\t *\n\t * 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.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * \t<li><code>prefix</code> - <code>string</code> - Optional - Restricts the response to contain results that begin only with the specified prefix.</li>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t */\n\tpublic function list_objects($bucket, $opt = null)\n\t{\n\t\tif (!$opt) $opt = array();\n\n\t\t// Add this to our request\n\t\t$opt['verb'] = 'GET';\n\n\t\tforeach (array('delimiter', 'marker', 'max-keys', 'prefix') as $param)\n\t\t{\n\t\t\tif (isset($opt[$param]))\n\t\t\t{\n\t\t\t\t$opt['query_string'][$param] = $opt[$param];\n\t\t\t\tunset($opt[$param]);\n\t\t\t}\n\t\t}\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\t/**\n\t * Copies an Amazon S3 object to a new location, whether in the same Amazon S3 region, bucket, or otherwise.\n\t *\n\t * NOTE: Object redirect locations are not carried over when an object is copied.\n\t *\n\t * @param array $source (Required) The bucket and file name to copy from. The following keys must be set: <ul>\n\t * \t<li><code>bucket</code> - <code>string</code> - Required - Specifies the name of the bucket containing the source object.</li>\n\t * \t<li><code>filename</code> - <code>string</code> - Required - Specifies the file name of the source object to copy.</li></ul>\n\t * @param array $dest (Required) The bucket and file name to copy to. The following keys must be set: <ul>\n\t * \t<li><code>bucket</code> - <code>string</code> - Required - Specifies the name of the bucket to copy the object to.</li>\n\t * \t<li><code>filename</code> - <code>string</code> - Required - Specifies the file name to copy the object to.</li></ul>\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<li><code>encryption</code> - <code>string</code> - Optional - The algorithm to use for encrypting the object. [Allowed values: <code>AES256</code>]</li>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/API/RESTObjectCOPY.html Copying Amazon S3 Objects\n\t */\n\tpublic function copy_object($source, $dest, $opt = null)\n\t{\n\t\tif (!$opt) $opt = array();\n\t\t$batch = array();\n\n\t\t// Add this to our request\n\t\t$opt['verb'] = 'PUT';\n\t\t$opt['resource'] = $dest['filename'];\n\t\t$opt['body'] = '';\n\n\t\t// Handle copy source\n\t\tif (isset($source['bucket']) && isset($source['filename']))\n\t\t{\n\t\t\t$opt['headers']['x-amz-copy-source'] = '/' . $source['bucket'] . '/' . rawurlencode($source['filename'])\n\t\t\t\t. (isset($opt['versionId']) ? ('?' . 'versionId=' . rawurlencode($opt['versionId'])) : ''); // Append the versionId to copy, if available\n\t\t\tunset($opt['versionId']);\n\t\t}\n\n\t\t// Handle metadata directive\n\t\t$opt['headers']['x-amz-metadata-directive'] = 'COPY';\n\t\tif ($source['bucket'] === $dest['bucket'] && $source['filename'] === $dest['filename'])\n\t\t{\n\t\t\t$opt['headers']['x-amz-metadata-directive'] = 'REPLACE';\n\t\t}\n\t\tif (isset($opt['metadataDirective']))\n\t\t{\n\t\t\t$opt['headers']['x-amz-metadata-directive'] = $opt['metadataDirective'];\n\t\t\tunset($opt['metadataDirective']);\n\t\t}\n\n\t\t// Handle Access Control Lists. Can also pass canned ACLs as an HTTP header.\n\t\tif (isset($opt['acl']) && is_array($opt['acl']))\n\t\t{\n\t\t\t$batch[] = $this->set_object_acl($dest['bucket'], $dest['filename'], $opt['acl'], array(\n\t\t\t\t'returnCurlHandle' => true\n\t\t\t));\n\t\t\tunset($opt['acl']);\n\t\t}\n\t\telseif (isset($opt['acl']))\n\t\t{\n\t\t\t$opt['headers']['x-amz-acl'] = $opt['acl'];\n\t\t\tunset($opt['acl']);\n\t\t}\n\n\t\t// Handle storage settings. Can also be passed as an HTTP header.\n\t\tif (isset($opt['storage']))\n\t\t{\n\t\t\t$opt['headers']['x-amz-storage-class'] = $opt['storage'];\n\t\t\tunset($opt['storage']);\n\t\t}\n\n\t\t// Handle encryption settings. Can also be passed as an HTTP header.\n\t\tif (isset($opt['encryption']))\n\t\t{\n\t\t\t$opt['headers']['x-amz-server-side-encryption'] = $opt['encryption'];\n\t\t\tunset($opt['encryption']);\n\t\t}\n\n\t\t// Handle conditional-copy parameters\n\t\tif (isset($opt['ifMatch']))\n\t\t{\n\t\t\t$opt['headers']['x-amz-copy-source-if-match'] = $opt['ifMatch'];\n\t\t\tunset($opt['ifMatch']);\n\t\t}\n\t\tif (isset($opt['ifNoneMatch']))\n\t\t{\n\t\t\t$opt['headers']['x-amz-copy-source-if-none-match'] = $opt['ifNoneMatch'];\n\t\t\tunset($opt['ifNoneMatch']);\n\t\t}\n\t\tif (isset($opt['ifUnmodifiedSince']))\n\t\t{\n\t\t\t$opt['headers']['x-amz-copy-source-if-unmodified-since'] = $opt['ifUnmodifiedSince'];\n\t\t\tunset($opt['ifUnmodifiedSince']);\n\t\t}\n\t\tif (isset($opt['ifModifiedSince']))\n\t\t{\n\t\t\t$opt['headers']['x-amz-copy-source-if-modified-since'] = $opt['ifModifiedSince'];\n\t\t\tunset($opt['ifModifiedSince']);\n\t\t}\n\n\t\t// Handle meta tags. Can also be passed as an HTTP header.\n\t\tif (isset($opt['meta']))\n\t\t{\n\t\t\tforeach ($opt['meta'] as $meta_key => $meta_value)\n\t\t\t{\n\t\t\t\t// e.g., `My Meta Header` is converted to `x-amz-meta-my-meta-header`.\n\t\t\t\t$opt['headers']['x-amz-meta-' . strtolower(str_replace(' ', '-', $meta_key))] = $meta_value;\n\t\t\t}\n\t\t\tunset($opt['meta']);\n\t\t}\n\n\t\t// Authenticate to S3\n\t\t$response = $this->authenticate($dest['bucket'], $opt);\n\n\t\t// Attempt to reset ACLs\n\t\t$http = new CFRequest();\n\t\t$http->send_multi_request($batch);\n\n\t\treturn $response;\n\t}\n\n\t/**\n\t * Updates an Amazon S3 object with new headers or other metadata. To replace the content of the\n\t * specified Amazon S3 object, call <create_object()> with the same bucket and file name parameters.\n\t *\n\t * @param string $bucket (Required) The name of the bucket that contains the source file.\n\t * @param string $filename (Required) The source file name that you want to update.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/API/RESTObjectCOPY.html Copying Amazon S3 Objects\n\t */\n\tpublic function update_object($bucket, $filename, $opt = null)\n\t{\n\t\tif (!$opt) $opt = array();\n\t\t$opt['metadataDirective'] = 'REPLACE';\n\n\t\t// Retrieve the original metadata\n\t\tif ($metadata = $this->get_object_metadata($bucket, $filename))\n\t\t{\n\t\t\tif (isset($metadata['ACL']))\n\t\t\t{\n\t\t\t\t$opt['acl'] = isset($opt['acl']) ? $opt['acl'] : $metadata['ACL'];\n\t\t\t}\n\t\t\tif (isset($metadata['StorageClass']))\n\t\t\t{\n\t\t\t\t$opt['headers']['x-amz-storage-class'] = $metadata['StorageClass'];\n\t\t\t}\n\t\t\tif (isset($metadata['ContentType']))\n\t\t\t{\n\t\t\t\t$opt['headers']['Content-Type'] = $metadata['ContentType'];\n\t\t\t}\n\t\t}\n\n\t\t// Remove a header\n\t\tunset($metadata['Headers']['date']);\n\n\t\t// Merge headers\n\t\t$opt['headers'] = array_merge($opt['headers'], $metadata['Headers']);\n\n\t\t// Authenticate to S3\n\t\treturn $this->copy_object(\n\t\t\tarray('bucket' => $bucket, 'filename' => $filename),\n\t\t\tarray('bucket' => $bucket, 'filename' => $filename),\n\t\t\t$opt\n\t\t);\n\t}\n\n\n\t/*%******************************************************************************************%*/\n\t// ACCESS CONTROL LISTS\n\n\t/**\n\t * Gets the access control list (ACL) settings for the specified Amazon S3 object.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param string $filename (Required) The file name for the object.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAccessPolicy.html REST Access Control Policy\n\t */\n\tpublic function get_object_acl($bucket, $filename, $opt = null)\n\t{\n\t\t// Add this to our request\n\t\tif (!$opt) $opt = array();\n\t\t$opt['verb'] = 'GET';\n\t\t$opt['resource'] = $filename;\n\t\t$opt['sub_resource'] = 'acl';\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\t/**\n\t * Sets the access control list (ACL) settings for the specified Amazon S3 object.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param string $filename (Required) The file name for the object.\n\t * @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>.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAccessPolicy.html REST Access Control Policy\n\t */\n\tpublic function set_object_acl($bucket, $filename, $acl = self::ACL_PRIVATE, $opt = null)\n\t{\n\t\t// Add this to our request\n\t\tif (!$opt) $opt = array();\n\t\t$opt['verb'] = 'PUT';\n\t\t$opt['resource'] = $filename;\n\t\t$opt['sub_resource'] = 'acl';\n\t\t// Retrieve the original metadata\n\t\t$metadata = $this->get_object_metadata($bucket, $filename);\n\t\tif ($metadata && $metadata['ContentType'])\n\t\t{\n\t\t\t$opt['headers']['Content-Type'] = $metadata['ContentType'];\n\t\t}\n\t\tif ($metadata && $metadata['StorageClass'])\n\t\t{\n\t\t\t$opt['headers']['x-amz-storage-class'] = $metadata['StorageClass'];\n\t\t}\n\n\t\t// Make sure these are defined.\n\t\t// @codeCoverageIgnoreStart\n\t\tif (!$this->credentials->canonical_id || !$this->credentials->canonical_name)\n\t\t{\n\t\t\t// Fetch the data live.\n\t\t\t$canonical = $this->get_canonical_user_id();\n\t\t\t$this->credentials->canonical_id = $canonical['id'];\n\t\t\t$this->credentials->canonical_name = $canonical['display_name'];\n\t\t}\n\t\t// @codeCoverageIgnoreEnd\n\n\t\tif (is_array($acl))\n\t\t{\n\t\t\t$opt['headers'] = array_merge($opt['headers'], $this->generate_access_policy_headers($acl));\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$opt['body'] = '';\n\t\t\t$opt['headers']['x-amz-acl'] = $acl;\n\t\t}\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\t/**\n\t * Generates the XML to be used for the Access Control Policy.\n\t *\n\t * @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()>.\n\t * @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()>.\n\t * @param array $users (Optional) An array of associative arrays. Each associative array contains an `id` value and a `permission` value.\n\t * @return string Access Control Policy XML.\n\t * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/ACLOverview.html Access Control Lists\n\t */\n\tpublic function generate_access_policy($canonical_id, $canonical_name, $users)\n\t{\n\t\t$xml = simplexml_load_string($this->base_acp_xml);\n\t\t$owner = $xml->addChild('Owner');\n\t\t$owner->addChild('ID', $canonical_id);\n\t\t$owner->addChild('DisplayName', $canonical_name);\n\t\t$acl = $xml->addChild('AccessControlList');\n\n\t\tforeach ($users as $user)\n\t\t{\n\t\t\t$grant = $acl->addChild('Grant');\n\t\t\t$grantee = $grant->addChild('Grantee');\n\n\t\t\tswitch ($user['id'])\n\t\t\t{\n\t\t\t\t// Authorized Users\n\t\t\t\tcase self::USERS_AUTH:\n\t\t\t\t\t$grantee->addAttribute('xsi:type', 'Group', 'http://www.w3.org/2001/XMLSchema-instance');\n\t\t\t\t\t$grantee->addChild('URI', self::USERS_AUTH);\n\t\t\t\t\tbreak;\n\n\t\t\t\t// All Users\n\t\t\t\tcase self::USERS_ALL:\n\t\t\t\t\t$grantee->addAttribute('xsi:type', 'Group', 'http://www.w3.org/2001/XMLSchema-instance');\n\t\t\t\t\t$grantee->addChild('URI', self::USERS_ALL);\n\t\t\t\t\tbreak;\n\n\t\t\t\t// The Logging User\n\t\t\t\tcase self::USERS_LOGGING:\n\t\t\t\t\t$grantee->addAttribute('xsi:type', 'Group', 'http://www.w3.org/2001/XMLSchema-instance');\n\t\t\t\t\t$grantee->addChild('URI', self::USERS_LOGGING);\n\t\t\t\t\tbreak;\n\n\t\t\t\t// Email Address or Canonical Id\n\t\t\t\tdefault:\n\t\t\t\t\tif (strpos($user['id'], '@'))\n\t\t\t\t\t{\n\t\t\t\t\t\t$grantee->addAttribute('xsi:type', 'AmazonCustomerByEmail', 'http://www.w3.org/2001/XMLSchema-instance');\n\t\t\t\t\t\t$grantee->addChild('EmailAddress', $user['id']);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t// Assume Canonical Id\n\t\t\t\t\t\t$grantee->addAttribute('xsi:type', 'CanonicalUser', 'http://www.w3.org/2001/XMLSchema-instance');\n\t\t\t\t\t\t$grantee->addChild('ID', $user['id']);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t$grant->addChild('Permission', $user['permission']);\n\t\t}\n\n\t\treturn $xml->asXML();\n\t}\n\n\t/**\n\t * Generates the HTTP headers to be used for the Access Control Policy Grants.\n\t *\n\t * @param array $users (Optional) An array of associative arrays. Each associative array contains an `id` value and a `permission` value.\n\t * @return array HTTP headers to be applied to the request.\n\t * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/ACLOverview.html Access Control Lists\n\t */\n\tpublic function generate_access_policy_headers($users)\n\t{\n\t\t$headers = array();\n\n\t\tforeach ($users as $user)\n\t\t{\n\t\t\t// Determine permission. If doesn't exist, create it.\n\t\t\t$permission = 'x-amz-grant-' . str_replace('_', '-', strtolower($user['permission']));\n\t\t\tif (!isset($headers[$permission]))\n\t\t\t{\n\t\t\t\t$headers[$permission] = array();\n\t\t\t}\n\n\t\t\t// Handle the IDs\n\t\t\tswitch ($user['id'])\n\t\t\t{\n\t\t\t\tcase self::USERS_AUTH:    // Authorized Users\n\t\t\t\tcase self::USERS_ALL:     // All Users\n\t\t\t\tcase self::USERS_LOGGING: // The Logging User\n\t\t\t\t\t$headers[$permission][] = 'uri=\"' . $user['id'] . '\"';\n\t\t\t\t\tbreak;\n\n\t\t\t\t// Email Address or Canonical Id\n\t\t\t\tdefault:\n\t\t\t\t\tif (strpos($user['id'], '@'))\n\t\t\t\t\t{\n\t\t\t\t\t\t// Treat as email address\n\t\t\t\t\t\t$headers[$permission][] = 'emailAddress=\"' . $user['id'] . '\"';\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\t// Assume Canonical Id\n\t\t\t\t\t\t$headers[$permission][] = 'id=\"' . $user['id'] . '\"';\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tforeach ($headers as &$permission)\n\t\t{\n\t\t\t$permission = implode(', ', $permission);\n\t\t}\n\n\t\treturn $headers;\n\t}\n\n\n\t/*%******************************************************************************************%*/\n\t// LOGGING METHODS\n\n\t/**\n\t * Gets the access logs associated with the specified Amazon S3 bucket.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use. Pass a `null` value when using the <set_vhost()> method.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/ServerLogs.html Server Access Logging\n\t */\n\tpublic function get_logs($bucket, $opt = null)\n\t{\n\t\t// Add this to our request\n\t\tif (!$opt) $opt = array();\n\t\t$opt['verb'] = 'GET';\n\t\t$opt['sub_resource'] = 'logging';\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\t/**\n\t * Enables access logging for the specified Amazon S3 bucket.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to enable logging for. Pass a `null` value when using the <set_vhost()> method.\n\t * @param string $target_bucket (Required) The name of the bucket to store the logs in.\n\t * @param string $target_prefix (Required) The prefix to give to the log file names.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/LoggingAPI.html Server Access Logging Configuration API\n\t */\n\tpublic function enable_logging($bucket, $target_bucket, $target_prefix, $opt = null)\n\t{\n\t\t// Add this to our request\n\t\tif (!$opt) $opt = array();\n\t\t$opt['verb'] = 'PUT';\n\t\t$opt['sub_resource'] = 'logging';\n\t\t$opt['headers'] = array(\n\t\t\t'Content-Type' => 'application/xml'\n\t\t);\n\n\t\t$xml = simplexml_load_string($this->base_logging_xml);\n\t\t$LoggingEnabled = $xml->addChild('LoggingEnabled');\n\t\t$LoggingEnabled->addChild('TargetBucket', $target_bucket);\n\t\t$LoggingEnabled->addChild('TargetPrefix', $target_prefix);\n\t\t$TargetGrants = $LoggingEnabled->addChild('TargetGrants');\n\n\t\tif (isset($opt['users']) && is_array($opt['users']))\n\t\t{\n\t\t\tforeach ($opt['users'] as $user)\n\t\t\t{\n\t\t\t\t$grant = $TargetGrants->addChild('Grant');\n\t\t\t\t$grantee = $grant->addChild('Grantee');\n\n\t\t\t\tswitch ($user['id'])\n\t\t\t\t{\n\t\t\t\t\t// Authorized Users\n\t\t\t\t\tcase self::USERS_AUTH:\n\t\t\t\t\t\t$grantee->addAttribute('xsi:type', 'Group', 'http://www.w3.org/2001/XMLSchema-instance');\n\t\t\t\t\t\t$grantee->addChild('URI', self::USERS_AUTH);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t// All Users\n\t\t\t\t\tcase self::USERS_ALL:\n\t\t\t\t\t\t$grantee->addAttribute('xsi:type', 'Group', 'http://www.w3.org/2001/XMLSchema-instance');\n\t\t\t\t\t\t$grantee->addChild('URI', self::USERS_ALL);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t// The Logging User\n\t\t\t\t\tcase self::USERS_LOGGING:\n\t\t\t\t\t\t$grantee->addAttribute('xsi:type', 'Group', 'http://www.w3.org/2001/XMLSchema-instance');\n\t\t\t\t\t\t$grantee->addChild('URI', self::USERS_LOGGING);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t// Email Address or Canonical Id\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tif (strpos($user['id'], '@'))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t$grantee->addAttribute('xsi:type', 'AmazonCustomerByEmail', 'http://www.w3.org/2001/XMLSchema-instance');\n\t\t\t\t\t\t\t$grantee->addChild('EmailAddress', $user['id']);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// Assume Canonical Id\n\t\t\t\t\t\t\t$grantee->addAttribute('xsi:type', 'CanonicalUser', 'http://www.w3.org/2001/XMLSchema-instance');\n\t\t\t\t\t\t\t$grantee->addChild('ID', $user['id']);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\t$grant->addChild('Permission', $user['permission']);\n\t\t\t}\n\t\t}\n\n\t\t$opt['body'] = $xml->asXML();\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\t/**\n\t * Disables access logging for the specified Amazon S3 bucket.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use. Pass `null` if using <set_vhost()>.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/LoggingAPI.html Server Access Logging Configuration API\n\t */\n\tpublic function disable_logging($bucket, $opt = null)\n\t{\n\t\t// Add this to our request\n\t\tif (!$opt) $opt = array();\n\t\t$opt['verb'] = 'PUT';\n\t\t$opt['sub_resource'] = 'logging';\n\t\t$opt['headers'] = array(\n\t\t\t'Content-Type' => 'application/xml'\n\t\t);\n\t\t$opt['body'] = $this->base_logging_xml;\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\n\t/*%******************************************************************************************%*/\n\t// CONVENIENCE METHODS\n\n\t/**\n\t * Gets whether or not the specified Amazon S3 bucket exists in Amazon S3. This includes buckets\n\t * that do not belong to the caller.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @return boolean A value of <code>true</code> if the bucket exists, or a value of <code>false</code> if it does not.\n\t */\n\tpublic function if_bucket_exists($bucket)\n\t{\n\t\tif ($this->use_batch_flow)\n\t\t{\n\t\t\tthrow new S3_Exception(__FUNCTION__ . '() cannot be batch requested');\n\t\t}\n\n\t\t$header = $this->get_bucket_headers($bucket);\n\t\treturn (integer) $header->status !== 404;\n\t}\n\n\t/**\n\t * Gets whether or not the specified Amazon S3 object exists in the specified bucket.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param string $filename (Required) The file name for the object.\n\t * @return boolean A value of <code>true</code> if the object exists, or a value of <code>false</code> if it does not.\n\t */\n\tpublic function if_object_exists($bucket, $filename)\n\t{\n\t\tif ($this->use_batch_flow)\n\t\t{\n\t\t\tthrow new S3_Exception(__FUNCTION__ . '() cannot be batch requested');\n\t\t}\n\n\t\t$header = $this->get_object_headers($bucket, $filename);\n\n\t\tif ($header->isOK()) { return true; }\n\t\telseif ($header->status === 404) { return false; }\n\n\t\t// @codeCoverageIgnoreStart\n\t\treturn null;\n\t\t// @codeCoverageIgnoreEnd\n\t}\n\n\t/**\n\t * Gets whether or not the specified Amazon S3 bucket has a bucket policy associated with it.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @return boolean A value of <code>true</code> if a bucket policy exists, or a value of <code>false</code> if one does not.\n\t */\n\tpublic function if_bucket_policy_exists($bucket)\n\t{\n\t\tif ($this->use_batch_flow)\n\t\t{\n\t\t\t// @codeCoverageIgnoreStart\n\t\t\tthrow new S3_Exception(__FUNCTION__ . '() cannot be batch requested');\n\t\t\t// @codeCoverageIgnoreEnd\n\t\t}\n\n\t\t$response = $this->get_bucket_policy($bucket);\n\n\t\tif ($response->isOK()) { return true; }\n\t\telseif ($response->status === 404) { return false; }\n\n\t\t// @codeCoverageIgnoreStart\n\t\treturn null;\n\t\t// @codeCoverageIgnoreEnd\n\t}\n\n\t/**\n\t * Gets the number of Amazon S3 objects in the specified bucket.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @return integer The number of Amazon S3 objects in the bucket.\n\t */\n\tpublic function get_bucket_object_count($bucket)\n\t{\n\t\tif ($this->use_batch_flow)\n\t\t{\n\t\t\t// @codeCoverageIgnoreStart\n\t\t\tthrow new S3_Exception(__FUNCTION__ . '() cannot be batch requested');\n\t\t\t// @codeCoverageIgnoreEnd\n\t\t}\n\n\t\treturn count($this->get_object_list($bucket));\n\t}\n\n\t/**\n\t * Gets the cumulative file size of the contents of the Amazon S3 bucket.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @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.\n\t * @return integer|string The number of bytes as an integer, or the friendly format as a string.\n\t */\n\tpublic function get_bucket_filesize($bucket, $friendly_format = false)\n\t{\n\t\tif ($this->use_batch_flow)\n\t\t{\n\t\t\tthrow new S3_Exception(__FUNCTION__ . '() cannot be batch requested');\n\t\t}\n\n\t\t$filesize = 0;\n\t\t$list = $this->list_objects($bucket);\n\n\t\tforeach ($list->body->Contents as $filename)\n\t\t{\n\t\t\t$filesize += (integer) $filename->Size;\n\t\t}\n\n\t\twhile ((string) $list->body->IsTruncated === 'true')\n\t\t{\n\t\t\t$body = (array) $list->body;\n\t\t\t$list = $this->list_objects($bucket, array(\n\t\t\t\t'marker' => (string) end($body['Contents'])->Key\n\t\t\t));\n\n\t\t\tforeach ($list->body->Contents as $object)\n\t\t\t{\n\t\t\t\t$filesize += (integer) $object->Size;\n\t\t\t}\n\t\t}\n\n\t\tif ($friendly_format)\n\t\t{\n\t\t\t$filesize = $this->util->size_readable($filesize);\n\t\t}\n\n\t\treturn $filesize;\n\t}\n\n\t/**\n\t * Gets the file size of the specified Amazon S3 object.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param string $filename (Required) The file name for the object.\n\t * @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.\n\t * @return integer|string|boolean The number of bytes as an integer, or the friendly format as a string. Returns false if the request failed.\n\t */\n\tpublic function get_object_filesize($bucket, $filename, $friendly_format = false)\n\t{\n\t\tif ($this->use_batch_flow)\n\t\t{\n\t\t\tthrow new S3_Exception(__FUNCTION__ . '() cannot be batch requested');\n\t\t}\n\n\t\t$response = $this->get_object_headers($bucket, $filename);\n\t\tif (!$response->isOK()) {\n\t\t\treturn false;\n\t\t}\n\n\t\t$filesize = (integer) $response->header['content-length'];\n\n\t\tif ($friendly_format)\n\t\t{\n\t\t\t$filesize = $this->util->size_readable($filesize);\n\t\t}\n\n\t\treturn $filesize;\n\t}\n\n\t/**\n\t * Changes the content type for an existing Amazon S3 object.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param string $filename (Required) The file name for the object.\n\t * @param string $contentType (Required) The content-type to apply to the object.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t */\n\tpublic function change_content_type($bucket, $filename, $contentType, $opt = null)\n\t{\n\t\tif (!$opt) $opt = array();\n\n\t\t// Retrieve the original metadata\n\t\t$metadata = $this->get_object_metadata($bucket, $filename);\n\t\tif ($metadata && isset($metadata['ACL']))\n\t\t{\n\t\t\t$opt['acl'] = $metadata['ACL'];\n\t\t}\n\t\tif ($metadata && isset($metadata['StorageClass']))\n\t\t{\n\t\t\t$opt['headers']['x-amz-storage-class'] = $metadata['StorageClass'];\n\t\t}\n\n\t\t// Merge optional parameters\n\t\t$opt = array_merge_recursive(array(\n\t\t\t'headers' => array(\n\t\t\t\t'Content-Type' => $contentType\n\t\t\t),\n\t\t\t'metadataDirective' => 'REPLACE'\n\t\t), $opt);\n\n\t\treturn $this->copy_object(\n\t\t\tarray('bucket' => $bucket, 'filename' => $filename),\n\t\t\tarray('bucket' => $bucket, 'filename' => $filename),\n\t\t\t$opt\n\t\t);\n\t}\n\n\t/**\n\t * Changes the storage redundancy for an existing object.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param string $filename (Required) The file name for the object.\n\t * @param string $storage (Required) The storage setting to apply to the object. [Allowed values: <code>AmazonS3::STORAGE_STANDARD</code>, <code>AmazonS3::STORAGE_REDUCED</code>]\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t */\n\tpublic function change_storage_redundancy($bucket, $filename, $storage, $opt = null)\n\t{\n\t\tif (!$opt) $opt = array();\n\n\t\t// Retrieve the original metadata\n\t\t$metadata = $this->get_object_metadata($bucket, $filename);\n\t\tif ($metadata && isset($metadata['ACL']))\n\t\t{\n\t\t\t$opt['acl'] = $metadata['ACL'];\n\t\t}\n\t\tif ($metadata && isset($metadata['StorageClass']))\n\t\t{\n\t\t\t$opt['headers']['x-amz-storage-class'] = $metadata['StorageClass'];\n\t\t}\n\n\t\t// Merge optional parameters\n\t\t$opt = array_merge(array(\n\t\t\t'storage' => $storage,\n\t\t\t'metadataDirective' => 'COPY',\n\t\t), $opt);\n\n\t\treturn $this->copy_object(\n\t\t\tarray('bucket' => $bucket, 'filename' => $filename),\n\t\t\tarray('bucket' => $bucket, 'filename' => $filename),\n\t\t\t$opt\n\t\t);\n\t}\n\n\t/**\n\t * Gets a simplified list of bucket names on an Amazon S3 account.\n\t *\n\t * @param string $pcre (Optional) A Perl-Compatible Regular Expression (PCRE) to filter the bucket names against.\n\t * @return array The list of matching bucket names. If there are no results, the method will return an empty array.\n\t * @link http://php.net/pcre Regular Expressions (Perl-Compatible)\n\t */\n\tpublic function get_bucket_list($pcre = null)\n\t{\n\t\tif ($this->use_batch_flow)\n\t\t{\n\t\t\tthrow new S3_Exception(__FUNCTION__ . '() cannot be batch requested');\n\t\t}\n\n\t\t// Get a list of buckets.\n\t\t$list = $this->list_buckets();\n\t\tif ($list = $list->body->query('descendant-or-self::Name'))\n\t\t{\n\t\t\t$list = $list->map_string($pcre);\n\t\t\treturn $list;\n\t\t}\n\n\t\t// @codeCoverageIgnoreStart\n\t\treturn array();\n\t\t// @codeCoverageIgnoreEnd\n\t}\n\n\t/**\n\t * Gets a simplified list of Amazon S3 object file names contained in a bucket.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * \t<li><code>max-keys</code> - <code>integer</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 less. A value of zero is treated as if you did not specify max-keys.</li>\n\t * \t<li><code>pcre</code> - <code>string</code> - Optional - A Perl-Compatible Regular Expression (PCRE) to filter the names against. This is applied only AFTER any native Amazon S3 filtering from specified <code>prefix</code>, <code>marker</code>, <code>max-keys</code>, or <code>delimiter</code> values are applied.</li>\n\t * \t<li><code>prefix</code> - <code>string</code> - Optional - Restricts the response to contain results that begin only with the specified prefix.</li>\n\t * \t<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>\n\t * @return array The list of matching object names. If there are no results, the method will return an empty array.\n\t * @link http://php.net/pcre Regular Expressions (Perl-Compatible)\n\t */\n\tpublic function get_object_list($bucket, $opt = null)\n\t{\n\t\tif ($this->use_batch_flow)\n\t\t{\n\t\t\tthrow new S3_Exception(__FUNCTION__ . '() cannot be batch requested');\n\t\t}\n\n\t\tif (!$opt) $opt = array();\n\t\tunset($opt['returnCurlHandle']); // This would cause problems\n\n\t\t// Set some default values\n\t\t$pcre = isset($opt['pcre']) ? $opt['pcre'] : null;\n\t\t$max_keys = (isset($opt['max-keys']) && is_int($opt['max-keys'])) ? $opt['max-keys'] : null;\n\t\t$objects = array();\n\n\t\tif (!$max_keys)\n\t\t{\n\t\t\t// No max-keys specified. Get everything.\n\t\t\tdo\n\t\t\t{\n\t\t\t\t$list = $this->list_objects($bucket, $opt);\n\t\t\t\tif (is_string($list->body))\n\t\t\t\t{\n\t\t\t\t\t$list->body = new CFSimpleXML($list->body);\n\t\t\t\t}\n\t\t\t\tif ($keys = $list->body->query('descendant-or-self::Key')->map_string($pcre))\n\t\t\t\t{\n\t\t\t\t\t$objects = array_merge($objects, $keys);\n\t\t\t\t}\n\n\t\t\t\t$body = (array) $list->body;\n\t\t\t\t$opt = array_merge($opt, array(\n\t\t\t\t\t'marker' => (isset($body['Contents']) && is_array($body['Contents'])) ?\n\t\t\t\t\t\t((string) end($body['Contents'])->Key) :\n\t\t\t\t\t\t((string) $list->body->Contents->Key)\n\t\t\t\t));\n\t\t\t}\n\t\t\twhile ((string) $list->body->IsTruncated === 'true');\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Max-keys specified. Approximate number of loops and make the requests.\n\n\t\t\t$max_keys = $opt['max-keys'];\n\t\t\t$loops = ceil($max_keys / 1000);\n\n\t\t\tdo\n\t\t\t{\n\t\t\t\t$list = $this->list_objects($bucket, $opt);\n\t\t\t\tif (is_string($list->body))\n\t\t\t\t{\n\t\t\t\t\t$list->body = new CFSimpleXML($list->body);\n\t\t\t\t}\n\t\t\t\t$keys = $list->body->query('descendant-or-self::Key')->map_string($pcre);\n\n\t\t\t\tif ($count = count($keys))\n\t\t\t\t{\n\t\t\t\t\t$objects = array_merge($objects, $keys);\n\n\t\t\t\t\tif ($count < 1000)\n\t\t\t\t\t{\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif ($max_keys > 1000)\n\t\t\t\t{\n\t\t\t\t\t$max_keys -= 1000;\n\t\t\t\t}\n\n\t\t\t\t$body = (array) $list->body;\n\t\t\t\t$opt = array_merge($opt, array(\n\t\t\t\t\t'max-keys' => $max_keys,\n\t\t\t\t\t'marker' => (isset($body['Contents']) && is_array($body['Contents'])) ?\n\t\t\t\t\t\t((string) end($body['Contents'])->Key) :\n\t\t\t\t\t\t((string) $list->body->Contents->Key)\n\t\t\t\t));\n\t\t\t}\n\t\t\twhile (--$loops);\n\t\t}\n\n\t\treturn $objects;\n\t}\n\n\t/**\n\t * Deletes all Amazon S3 objects inside the specified bucket.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param string $pcre (Optional) A Perl-Compatible Regular Expression (PCRE) to filter the names against. The default value is <PCRE_ALL>.\n\t * @return boolean A value of <code>true</code> means that all objects were successfully deleted. A value of <code>false</code> means that at least one object failed to delete.\n\t * @link http://php.net/pcre Regular Expressions (Perl-Compatible)\n\t */\n\tpublic function delete_all_objects($bucket, $pcre = self::PCRE_ALL)\n\t{\n\t\t// Collect all matches\n\t\t$list = $this->get_object_list($bucket, array('pcre' => $pcre));\n\n\t\t// As long as we have at least one match...\n\t\tif (count($list) > 0)\n\t\t{\n\t\t\t$objects = array();\n\n\t\t\tforeach ($list as $object)\n\t\t\t{\n\t\t\t\t$objects[] = array('key' => $object);\n\t\t\t}\n\n\t\t\t$batch = new CFBatchRequest();\n\t\t\t$batch->use_credentials($this->credentials);\n\n\t\t\tforeach (array_chunk($objects, 1000) as $object_set)\n\t\t\t{\n\t\t\t\t$this->batch($batch)->delete_objects($bucket, array(\n\t\t\t\t\t'objects' => $object_set\n\t\t\t\t));\n\t\t\t}\n\n\t\t\t$responses = $this->batch($batch)->send();\n\t\t\t$is_ok = true;\n\n\t\t\tforeach ($responses as $response)\n\t\t\t{\n\t\t\t\tif (!$response->isOK() || isset($response->body->Error))\n\t\t\t\t{\n\t\t\t\t\t$is_ok = false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn $is_ok;\n\t\t}\n\n\t\t// If there are no matches, return true\n\t\treturn true;\n\t}\n\n\t/**\n\t * Deletes all of the versions of all Amazon S3 objects inside the specified bucket.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param string $pcre (Optional) A Perl-Compatible Regular Expression (PCRE) to filter the names against. The default value is <PCRE_ALL>.\n\t * @return boolean A value of <code>true</code> means that all object versions were successfully deleted. A value of <code>false</code> means that at least one object/version failed to delete.\n\t * @link http://php.net/pcre Regular Expressions (Perl-Compatible)\n\t */\n\tpublic function delete_all_object_versions($bucket, $pcre = null)\n\t{\n\t\t// Instantiate\n\t\t$versions = $this->list_bucket_object_versions($bucket);\n\n\t\t// Gather all nodes together into a single array\n\t\tif ($versions->body->DeleteMarker() && $versions->body->Version())\n\t\t{\n\t\t\t$markers = array_merge($versions->body->DeleteMarker()->getArrayCopy(), $versions->body->Version()->getArrayCopy());\n\t\t}\n\t\telseif ($versions->body->DeleteMarker())\n\t\t{\n\t\t\t$markers = $versions->body->DeleteMarker()->getArrayCopy();\n\t\t}\n\t\telseif ($versions->body->Version())\n\t\t{\n\t\t\t$markers = $versions->body->Version()->getArrayCopy();\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$markers = array();\n\t\t}\n\n\t\twhile ((string) $versions->body->IsTruncated === 'true')\n\t\t{\n\t\t\t$versions = $this->list_bucket_object_versions($bucket, array(\n\t\t\t\t'key-marker' => (string) $versions->body->NextKeyMarker\n\t\t\t));\n\n\t\t\t// Gather all nodes together into a single array\n\t\t\tif ($versions->body->DeleteMarker() && $versions->body->Version())\n\t\t\t{\n\t\t\t\t$markers = array_merge($markers, $versions->body->DeleteMarker()->getArrayCopy(), $versions->body->Version()->getArrayCopy());\n\t\t\t}\n\t\t\telseif ($versions->body->DeleteMarker())\n\t\t\t{\n\t\t\t\t$markers = array_merge($markers, $versions->body->DeleteMarker()->getArrayCopy());\n\t\t\t}\n\t\t\telseif ($versions->body->Version())\n\t\t\t{\n\t\t\t\t$markers = array_merge($markers, $versions->body->Version()->getArrayCopy());\n\t\t\t}\n\t\t}\n\n\t\t$objects = array();\n\n\t\t// Loop through markers\n\t\tforeach ($markers as $marker)\n\t\t{\n\t\t\tif ($pcre)\n\t\t\t{\n\t\t\t\tif (preg_match($pcre, (string) $marker->Key))\n\t\t\t\t{\n\t\t\t\t\t$xx = array('key' => (string) $marker->Key);\n\t\t\t\t\tif ((string) $marker->VersionId !== 'null')\n\t\t\t\t\t{\n\t\t\t\t\t\t$xx['version_id'] = (string) $marker->VersionId;\n\t\t\t\t\t}\n\t\t\t\t\t$objects[] = $xx;\n\t\t\t\t\tunset($xx);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$xx = array('key' => (string) $marker->Key);\n\t\t\t\tif ((string) $marker->VersionId !== 'null')\n\t\t\t\t{\n\t\t\t\t\t$xx['version_id'] = (string) $marker->VersionId;\n\t\t\t\t}\n\t\t\t\t$objects[] = $xx;\n\t\t\t\tunset($xx);\n\t\t\t}\n\t\t}\n\n\t\t$batch = new CFBatchRequest();\n\t\t$batch->use_credentials($this->credentials);\n\n\t\tforeach (array_chunk($objects, 1000) as $object_set)\n\t\t{\n\t\t\t$this->batch($batch)->delete_objects($bucket, array(\n\t\t\t\t'objects' => $object_set\n\t\t\t));\n\t\t}\n\n\t\t$responses = $this->batch($batch)->send();\n\t\t$is_ok = true;\n\n\t\tforeach ($responses as $response)\n\t\t{\n\t\t\tif (!$response->isOK() || isset($response->body->Error))\n\t\t\t{\n\t\t\t\t$is_ok = false;\n\t\t\t}\n\t\t}\n\n\t\treturn $is_ok;\n\t}\n\n\t/**\n\t * Gets the collective metadata for the given Amazon S3 object.\n\t *\n\t * The MD5 value for an object can be retrieved from the ETag HTTP header for any object that was uploaded\n\t * with a normal PUT/POST. This value is incorrect for multipart uploads.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param string $filename (Required) The file name for the Amazon S3 object.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * @return mixed If the object exists, the method returns the collective metadata for the Amazon S3 object. If the object does not exist, the method returns boolean <code>false</code>.\n\t */\n\tpublic function get_object_metadata($bucket, $filename, $opt = null)\n\t{\n\t\t$batch = new CFBatchRequest();\n\t\t$this->batch($batch)->get_object_acl($bucket, $filename); // Get ACL info\n\t\t$this->batch($batch)->get_object_headers($bucket, $filename); // Get content-type\n\t\t$this->batch($batch)->list_objects($bucket, array( // Get other metadata\n\t\t\t'max-keys' => 1,\n\t\t\t'prefix' => $filename\n\t\t));\n\t\t$response = $this->batch($batch)->send();\n\n\t\t// Fail if any requests were unsuccessful\n\t\tif (!$response->areOK())\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\t$data = array(\n\t\t\t'ACL'          => array(),\n\t\t\t'ContentType'  => null,\n\t\t\t'ETag'         => null,\n\t\t\t'Headers'      => null,\n\t\t\t'Key'          => null,\n\t\t\t'LastModified' => null,\n\t\t\t'Owner'        => array(),\n\t\t\t'Size'         => null,\n\t\t\t'StorageClass' => null,\n\t\t);\n\n\t\t// Add the content type\n\t\t$data['ContentType'] = (string) $response[1]->header['content-type'];\n\n\t\t// Add the other metadata (including storage type)\n\t\t$contents = json_decode(json_encode($response[2]->body->query('descendant-or-self::Contents')->first()), true);\n\t\t$data = array_merge($data, (is_array($contents) ? $contents : array()));\n\n\t\t// Add ACL info\n\t\t$grants = $response[0]->body->query('descendant-or-self::Grant');\n\t\t$max = count($grants);\n\n\t\t// Add raw header info\n\t\t$data['Headers'] = $response[1]->header;\n\t\tforeach (array('_info', 'x-amz-id-2', 'x-amz-request-id', 'cneonction', 'server', 'content-length', 'content-type', 'etag') as $header)\n\t\t{\n\t\t\tunset($data['Headers'][$header]);\n\t\t}\n\t\tksort($data['Headers']);\n\n\t\tif (count($grants) > 0)\n\t\t{\n\t\t\tforeach ($grants as $grant)\n\t\t\t{\n\t\t\t\t$dgrant = array(\n\t\t\t\t\t'id' => (string) $this->util->try_these(array('ID', 'URI'), $grant->Grantee),\n\t\t\t\t\t'permission' => (string) $grant->Permission\n\t\t\t\t);\n\n\t\t\t\t$data['ACL'][] = $dgrant;\n\t\t\t}\n\t\t}\n\n\t\treturn $data;\n\t}\n\n\n\t/*%******************************************************************************************%*/\n\t// URLS\n\n\t/**\n\t * Gets the web-accessible URL for the Amazon S3 object or generates a time-limited signed request for\n\t * a private file.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param string $filename (Required) The file name for the Amazon S3 object.\n\t * @param integer|string $preauth (Optional) Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with <php:strtotime()>.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<li><code>https</code> - <code>boolean</code> - Optional - Set to <code>true</code> if you would like the URL be in https mode. Otherwise, the default behavior is always to use http regardless of your SSL settings.</li>\n\t * \t<li><code>method</code> - <code>string</code> - Optional - The HTTP method to use for the request. Defaults to a value of <code>GET</code>.</li>\n\t * \t<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>\n\t * \t<li><code>torrent</code> - <code>boolean</code> - Optional - A value of <code>true</code> will return a URL to a torrent of the Amazon S3 object. A value of <code>false</code> will return a non-torrent URL. Defaults to <code>false</code>.</li>\n\t * \t<li><code>versionId</code> - <code>string</code> - Optional - The version of the object. Version IDs are returned in the <code>x-amz-version-id</code> header of any previous object-related request.</li>\n\t * \t<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>\n\t * @return string The file URL, with authentication and/or torrent parameters if requested.\n\t * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/S3_QSAuth.html Using Query String Authentication\n\t */\n\tpublic function get_object_url($bucket, $filename, $preauth = 0, $opt = null)\n\t{\n\t\t// Add this to our request\n\t\tif (!$opt) $opt = array();\n\t\t$opt['verb'] = isset($opt['method']) ? $opt['method'] : 'GET';\n\t\t$opt['resource'] = $filename;\n\t\t$opt['preauth'] = $preauth;\n\n\t\tif (isset($opt['torrent']) && $opt['torrent'])\n\t\t{\n\t\t\t$opt['sub_resource'] = 'torrent';\n\t\t\tunset($opt['torrent']);\n\t\t}\n\n\t\t// GET responses\n\t\tif (isset($opt['response']))\n\t\t{\n\t\t\tforeach ($opt['response'] as $key => $value)\n\t\t\t{\n\t\t\t\t$opt['response-' . $key] = $value;\n\t\t\t\tunset($opt['response'][$key]);\n\t\t\t}\n\t\t}\n\n\t\t// Determine whether or not to use SSL\n\t\t$use_ssl = isset($opt['https']) ? (bool) $opt['https'] : false;\n\t\tunset($opt['https']);\n\t\t$current_use_ssl_setting = $this->use_ssl;\n\n\t\t// Authenticate to S3\n\t\t$this->use_ssl = $use_ssl;\n\t\t$response = $this->authenticate($bucket, $opt);\n\t\t$this->use_ssl = $current_use_ssl_setting;\n\n\t\treturn $response;\n\t}\n\n\t/**\n\t * Gets the web-accessible URL to a torrent of the Amazon S3 object. The Amazon S3 object's access\n\t * control list settings (ACL) MUST be set to <ACL_PUBLIC> for a valid URL to be returned.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param string $filename (Required) The file name for the object.\n\t * @param integer|string $preauth (Optional) Specifies that a presigned URL for this request should be returned. May be passed as a number of seconds since UNIX Epoch, or any string compatible with <php:strtotime()>.\n\t * @return string The torrent URL, with authentication parameters if requested.\n\t * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/index.html?S3TorrentRetrieve.html Using BitTorrent to Retrieve Objects Stored in Amazon S3\n\t */\n\tpublic function get_torrent_url($bucket, $filename, $preauth = 0)\n\t{\n\t\treturn $this->get_object_url($bucket, $filename, $preauth, array(\n\t\t\t'torrent' => true\n\t\t));\n\t}\n\n\n\t/*%******************************************************************************************%*/\n\t// VERSIONING\n\n\t/**\n\t * Enables versioning support for the specified Amazon S3 bucket.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<li><code>MFASerial</code> - string (Optional) The serial number on the back of the Gemalto device. <code>MFASerial</code>, <code>MFAToken</code> and <code>MFAStatus</code> must all be set for MFA to work.</li>\n\t * \t<li><code>MFAToken</code> - string (Optional) The current token displayed on the Gemalto device. <code>MFASerial</code>, <code>MFAToken</code> and <code>MFAStatus</code> must all be set for MFA to work.</li>\n\t * \t<li><code>MFAStatus</code> - string (Optional) The MFA Delete status. Can be <code>Enabled</code> or <code>Disabled</code>. <code>MFASerial</code>, <code>MFAToken</code> and <code>MFAStatus</code> must all be set for MFA to work.</li>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t * @link http://aws.amazon.com/mfa/ Multi-Factor Authentication\n\t */\n\tpublic function enable_versioning($bucket, $opt = null)\n\t{\n\t\tif (!$opt) $opt = array();\n\n\t\t// Add this to our request\n\t\t$opt['verb'] = 'PUT';\n\t\t$opt['sub_resource'] = 'versioning';\n\t\t$opt['headers'] = array(\n\t\t\t'Content-Type' => 'application/xml'\n\t\t);\n\n\t\t$xml = simplexml_load_string($this->base_versioning_xml);\n\t\t$xml->addChild('Status', 'Enabled');\n\n\t\t// Enable MFA delete?\n\t\t// @codeCoverageIgnoreStart\n\t\tif (isset($opt['MFASerial']) && isset($opt['MFAToken']) && isset($opt['MFAStatus']))\n\t\t{\n\t\t\t$xml->addChild('MfaDelete', $opt['MFAStatus']);\n\t\t\t$opt['headers']['x-amz-mfa'] = ($opt['MFASerial'] . ' ' . $opt['MFAToken']);\n\t\t}\n\t\t// @codeCoverageIgnoreEnd\n\n\t\t$opt['body'] = $xml->asXML();\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\t/**\n\t * Disables versioning support for the specified Amazon S3 bucket.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<li><code>MFASerial</code> - <code>string</code> - Optional - The serial number on the back of the Gemalto device. <code>MFASerial</code>, <code>MFAToken</code> and <code>MFAStatus</code> must all be set for MFA to work.</li>\n\t * \t<li><code>MFAToken</code> - <code>string</code> - Optional - The current token displayed on the Gemalto device. <code>MFASerial</code>, <code>MFAToken</code> and <code>MFAStatus</code> must all be set for MFA to work.</li>\n\t * \t<li><code>MFAStatus</code> - <code>string</code> - Optional - The MFA Delete status. Can be <code>Enabled</code> or <code>Disabled</code>. <code>MFASerial</code>, <code>MFAToken</code> and <code>MFAStatus</code> must all be set for MFA to work.</li>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t * @link http://aws.amazon.com/mfa/ Multi-Factor Authentication\n\t */\n\tpublic function disable_versioning($bucket, $opt = null)\n\t{\n\t\tif (!$opt) $opt = array();\n\n\t\t// Add this to our request\n\t\t$opt['verb'] = 'PUT';\n\t\t$opt['sub_resource'] = 'versioning';\n\t\t$opt['headers'] = array(\n\t\t\t'Content-Type' => 'application/xml'\n\t\t);\n\n\t\t$xml = simplexml_load_string($this->base_versioning_xml);\n\t\t$xml->addChild('Status', 'Suspended');\n\n\t\t// Enable MFA delete?\n\t\t// @codeCoverageIgnoreStart\n\t\tif (isset($opt['MFASerial']) && isset($opt['MFAToken']) && isset($opt['MFAStatus']))\n\t\t{\n\t\t\t$xml->addChild('MfaDelete', $opt['MFAStatus']);\n\t\t\t$opt['headers']['x-amz-mfa'] = ($opt['MFASerial'] . ' ' . $opt['MFAToken']);\n\t\t}\n\t\t// @codeCoverageIgnoreEnd\n\n\t\t$opt['body'] = $xml->asXML();\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\t/**\n\t * Gets an Amazon S3 bucket's versioning status.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t */\n\tpublic function get_versioning_status($bucket, $opt = null)\n\t{\n\t\tif (!$opt) $opt = array();\n\t\t$opt['verb'] = 'GET';\n\t\t$opt['sub_resource'] = 'versioning';\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\t/**\n\t * Gets a list of all the versions of Amazon S3 objects in the specified bucket.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<li><code>delimiter</code> - <code>string</code> - Optional - Unicode string parameter. Keys that contain the same string between the prefix and the first occurrence of the delimiter will be rolled up into a single result element in the CommonPrefixes collection.</li>\n\t * \t<li><code>key-marker</code> - <code>string</code> - Optional - Restricts the response to contain results that only occur alphabetically after the value of the <code>key-marker</code>.</li>\n\t * \t<li><code>max-keys</code> - <code>string</code> - Optional - Limits the number of results returned in response to your query. Will return no more than this number of results, but possibly less.</li>\n\t * \t<li><code>prefix</code> - <code>string</code> - Optional - Restricts the response to only contain results that begin with the specified prefix.</li>\n\t * \t<li><code>version-id-marker</code> - <code>string</code> - Optional - Restricts the response to contain results that only occur alphabetically after the value of the <code>version-id-marker</code>.</li>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t */\n\tpublic function list_bucket_object_versions($bucket, $opt = null)\n\t{\n\t\tif (!$opt) $opt = array();\n\t\t$opt['verb'] = 'GET';\n\t\t$opt['sub_resource'] = 'versions';\n\n\t\tforeach (array('delimiter', 'key-marker', 'max-keys', 'prefix', 'version-id-marker') as $param)\n\t\t{\n\t\t\tif (isset($opt[$param]))\n\t\t\t{\n\t\t\t\t$opt['query_string'][$param] = $opt[$param];\n\t\t\t\tunset($opt[$param]);\n\t\t\t}\n\t\t}\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\n\t/*%******************************************************************************************%*/\n\t// BUCKET POLICIES\n\n\t/**\n\t * Sets the policy sub-resource for the specified Amazon S3 bucket. The specified policy replaces any\n\t * policy the bucket already has.\n\t *\n\t * To perform this operation, the caller must be authorized to set a policy for the bucket and have\n\t * PutPolicy permissions. If the caller does not have PutPolicy permissions for the bucket, Amazon S3\n\t * returns a `403 Access Denied` error. If the caller has the correct permissions but has not been\n\t * authorized by the bucket owner, Amazon S3 returns a `405 Method Not Allowed` error.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param CFPolicy $policy (Required) The JSON policy to use.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/AccessPolicyLanguage.html Appendix: The Access Policy Language\n\t */\n\tpublic function set_bucket_policy($bucket, CFPolicy $policy, $opt = null)\n\t{\n\t\tif (!$opt) $opt = array();\n\t\t$opt['verb'] = 'PUT';\n\t\t$opt['sub_resource'] = 'policy';\n\t\t$opt['body'] = $policy->get_json();\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\t/**\n\t * Gets the policy of the specified Amazon S3 bucket.\n\t *\n\t * To use this operation, the caller must have GetPolicy permissions for the specified bucket and must be\n\t * the bucket owner. If the caller does not have GetPolicy permissions, this method will generate a\n\t * `403 Access Denied` error. If the caller has the correct permissions but is not the bucket owner, this\n\t * method will generate a `405 Method Not Allowed` error. If the bucket does not have a policy defined for\n\t * it, this method will generate a `404 Policy Not Found` error.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t */\n\tpublic function get_bucket_policy($bucket, $opt = null)\n\t{\n\t\tif (!$opt) $opt = array();\n\t\t$opt['verb'] = 'GET';\n\t\t$opt['sub_resource'] = 'policy';\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\t/**\n\t * Deletes the bucket policy for the specified Amazon S3 bucket. To delete the policy, the caller must\n\t * be the bucket owner and have `DeletePolicy` permissions for the specified bucket.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response. If you do not have `DeletePolicy` permissions, Amazon S3 returns a `403 Access Denied` error. If you have the correct permissions, but are not the bucket owner, Amazon S3 returns a `405 Method Not Allowed` error. If the bucket doesn't have a policy, Amazon S3 returns a `204 No Content` error.\n\t */\n\tpublic function delete_bucket_policy($bucket, $opt = null)\n\t{\n\t\tif (!$opt) $opt = array();\n\t\t$opt['verb'] = 'DELETE';\n\t\t$opt['sub_resource'] = 'policy';\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\n\t/*%******************************************************************************************%*/\n\t// BUCKET NOTIFICATIONS\n\n\t/**\n\t * Enables notifications of specified events for an Amazon S3 bucket. Currently, the\n\t * `s3:ReducedRedundancyLostObject` event is the only event supported for notifications. The\n\t * `s3:ReducedRedundancyLostObject` event is triggered when Amazon S3 detects that it has lost all\n\t * copies of an Amazon S3 object and can no longer service requests for that object.\n\t *\n\t * If the bucket owner and Amazon SNS topic owner are the same, the bucket owner has permission to\n\t * publish notifications to the topic by default. Otherwise, the owner of the topic must create a\n\t * policy to enable the bucket owner to publish to the topic.\n\t *\n\t * By default, only the bucket owner can configure notifications on a bucket. However, bucket owners\n\t * can use bucket policies to grant permission to other users to set this configuration with the\n\t * `s3:PutBucketNotification` permission.\n\t *\n\t * After a PUT operation is called to configure notifications on a bucket, Amazon S3 publishes a test\n\t * notification to ensure that the topic exists and that the bucket owner has permission to publish\n\t * to the specified topic. If the notification is successfully published to the SNS topic, the PUT\n\t * operation updates the bucket configuration and returns the 200 OK responses with a\n\t * `x-amz-sns-test-message-id` header containing the message ID of the test notification sent to topic.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to create bucket notifications for.\n\t * @param string $topic_arn (Required) The SNS topic ARN to send notifications to.\n\t * @param string $event (Required) The event type to listen for.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/NotificationHowTo.html Setting Up Notification of Bucket Events\n\t */\n\tpublic function create_bucket_notification($bucket, $topic_arn, $event, $opt = null)\n\t{\n\t\tif (!$opt) $opt = array();\n\t\t$opt['verb'] = 'PUT';\n\t\t$opt['sub_resource'] = 'notification';\n\t\t$opt['headers'] = array(\n\t\t\t'Content-Type' => 'application/xml'\n\t\t);\n\n\t\t$xml = simplexml_load_string($this->base_notification_xml);\n\t\t$topic_config = $xml->addChild('TopicConfiguration');\n\t\t$topic_config->addChild('Topic', $topic_arn);\n\t\t$topic_config->addChild('Event', $event);\n\n\t\t$opt['body'] = $xml->asXML();\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\t/**\n\t * Gets the notification configuration of a bucket. Currently, the `s3:ReducedRedundancyLostObject` event\n\t * is the only event supported for notifications. The `s3:ReducedRedundancyLostObject` event is triggered\n\t * when Amazon S3 detects that it has lost all replicas of a Reduced Redundancy Storage object and can no\n\t * longer service requests for that object.\n\t *\n\t * If notifications are not enabled on the bucket, the operation returns an empty\n\t * `NotificatonConfiguration` element.\n\t *\n\t * By default, you must be the bucket owner to read the notification configuration of a bucket. However,\n\t * the bucket owner can use a bucket policy to grant permission to other users to read this configuration\n\t * with the `s3:GetBucketNotification` permission.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/NotificationHowTo.html Setting Up Notification of Bucket Events\n\t */\n\tpublic function get_bucket_notifications($bucket, $opt = null)\n\t{\n\t\tif (!$opt) $opt = array();\n\t\t$opt['verb'] = 'GET';\n\t\t$opt['sub_resource'] = 'notification';\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\t/**\n\t * Empties the list of SNS topics to send notifications to.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/NotificationHowTo.html Setting Up Notification of Bucket Events\n\t */\n\tpublic function delete_bucket_notification($bucket, $opt = null)\n\t{\n\t\tif (!$opt) $opt = array();\n\t\t$opt['verb'] = 'PUT';\n\t\t$opt['sub_resource'] = 'notification';\n\t\t$opt['body'] = $this->base_notification_xml;\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\n\t/*%******************************************************************************************%*/\n\t// MULTIPART UPLOAD\n\n\t/**\n\t * Calculates the correct values for sequentially reading a file for multipart upload. This method should\n\t * be used in conjunction with <upload_part()>.\n\t *\n\t * @param integer $filesize (Required) The size in bytes of the entire file.\n\t * @param integer $part_size (Required) The size in bytes of the part of the file to send.\n\t * @return array An array containing key-value pairs. The keys are `seekTo` and `length`.\n\t */\n\tpublic function get_multipart_counts($filesize, $part_size)\n\t{\n\t\t$i = 0;\n\t\t$sizecount = $filesize;\n\t\t$values = array();\n\n\t\twhile ($sizecount > 0)\n\t\t{\n\t\t\t$sizecount -= $part_size;\n\t\t\t$values[] = array(\n\t\t\t\t'seekTo' => ($part_size * $i),\n\t\t\t\t'length' => (($sizecount > 0) ? $part_size : ($sizecount + $part_size)),\n\t\t\t);\n\t\t\t$i++;\n\t\t}\n\n\t\treturn $values;\n\t}\n\n\t/**\n\t * Initiates a multipart upload and returns an `UploadId`.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param string $filename (Required) The file name for the object.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<li><code>contentType</code> - <code>string</code> - Optional - The type of content that is being sent. The default value is <code>application/octet-stream</code>.</li>\n\t * \t<li><code>encryption</code> - <code>string</code> - Optional - The algorithm to use for encrypting the object. [Allowed values: <code>AES256</code>]</li>\n\t * \t<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>\n\t * \t<li><code>meta</code> - <code>array</code> - Optional - An associative array of key-value pairs. Any header starting with <code>x-amz-meta-:</code> 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>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAccessPolicy.html REST Access Control Policy\n\t */\n\tpublic function initiate_multipart_upload($bucket, $filename, $opt = null)\n\t{\n\t\tif (!$opt) $opt = array();\n\n\t\t// Add this to our request\n\t\t$opt['verb'] = 'POST';\n\t\t$opt['resource'] = $filename;\n\t\t$opt['sub_resource'] = 'uploads';\n\t\t$opt['body'] = '';\n\n\t\t// Handle content type. Can also be passed as an HTTP header.\n\t\tif (isset($opt['contentType']))\n\t\t{\n\t\t\t$opt['headers']['Content-Type'] = $opt['contentType'];\n\t\t\tunset($opt['contentType']);\n\t\t}\n\n\t\t// Set a default content type.\n\t\tif (!isset($opt['headers']['Content-Type']))\n\t\t{\n\t\t\t$opt['headers']['Content-Type'] = 'application/octet-stream';\n\t\t}\n\n\t\t// Handle Access Control Lists. Can also be passed as an HTTP header.\n\t\tif (isset($opt['acl']))\n\t\t{\n\t\t\tif (is_array($opt['acl']))\n\t\t\t{\n\t\t\t\t$opt['headers'] = array_merge($opt['headers'], $this->generate_access_policy_headers($opt['acl']));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$opt['headers']['x-amz-acl'] = $opt['acl'];\n\t\t\t}\n\t\t}\n\n\t\t// Handle storage settings. Can also be passed as an HTTP header.\n\t\tif (isset($opt['storage']))\n\t\t{\n\t\t\t$opt['headers']['x-amz-storage-class'] = $opt['storage'];\n\t\t\tunset($opt['storage']);\n\t\t}\n\n\t\t// Handle encryption settings. Can also be passed as an HTTP header.\n\t\tif (isset($opt['encryption']))\n\t\t{\n\t\t\t$opt['headers']['x-amz-server-side-encryption'] = $opt['encryption'];\n\t\t\tunset($opt['encryption']);\n\t\t}\n\n\t\t// Handle meta tags. Can also be passed as an HTTP header.\n\t\tif (isset($opt['meta']))\n\t\t{\n\t\t\tforeach ($opt['meta'] as $meta_key => $meta_value)\n\t\t\t{\n\t\t\t\t// e.g., `My Meta Header` is converted to `x-amz-meta-my-meta-header`.\n\t\t\t\t$opt['headers']['x-amz-meta-' . strtolower(str_replace(' ', '-', $meta_key))] = $meta_value;\n\t\t\t}\n\t\t\tunset($opt['meta']);\n\t\t}\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\t/**\n\t * Uploads a single part of a multipart upload. The part size cannot be smaller than 5 MB\n\t * or larger than 5 TB. A multipart upload can have no more than 10,000 parts.\n\t *\n\t * Amazon S3 charges for storage as well as requests to the service. Smaller part sizes (and more\n\t * requests) allow for faster failures and better upload reliability. Larger part sizes (and fewer\n\t * requests) costs slightly less but has lower upload reliability.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param string $filename (Required) The file name for the object.\n\t * @param string $upload_id (Required) The upload ID identifying the multipart upload whose parts are being listed. The upload ID is retrieved from a call to <initiate_multipart_upload()>.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<li><code>fileUpload</code> - <code>string|resource</code> - Required - The URL/path for the file to upload or an open resource.</li>\n\t * \t<li><code>partNumber</code> - <code>integer</code> - Required - The part number order of the multipart upload.</li>\n\t * \t<li><code>expect</code> - <code>string</code> - Optional - Specifies that the SDK not send the request body until it receives an acknowledgement. If the message is rejected based on the headers, the body of the message is not sent. For more information, see <a href=\"http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.20\">RFC 2616, section 14.20</a>. The value can also be passed to the <code>header</code> option as <code>Expect</code>. [Allowed values: <code>100-continue</code>]</li>\n\t * \t<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>\n\t * \t<li><code>length</code> - <code>integer</code> - Optional - The size of the part 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>\n\t * \t<li><code>md5</code> - <code>string</code> - Optional - The base64 encoded 128-bit MD5 digest of the part data. This header can be used as a message integrity check to verify that the part data is the same data that was originally sent. Although it is optional, we recommend using this mechanism as an end-to-end integrity check. For more information, see <a href=\"http://www.ietf.org/rfc/rfc1864.txt\">RFC 1864</a>. The value can also be passed to the <code>header</code> option as <code>Content-MD5</code>.</li>\n\t * \t<li><code>seekTo</code> - <code>integer</code> - Optional - The starting position in bytes for the piece of the file/stream to upload.</li>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t */\n\tpublic function upload_part($bucket, $filename, $upload_id, $opt = null)\n\t{\n\t\tif (!$opt) $opt = array();\n\n\t\t// Add this to our request\n\t\t$opt['verb'] = 'PUT';\n\t\t$opt['resource'] = $filename;\n\t\t$opt['uploadId'] = $upload_id;\n\n\t\tif (!isset($opt['fileUpload']) || !isset($opt['partNumber']))\n\t\t{\n\t\t\tthrow new S3_Exception('The `fileUpload` and `partNumber` options are both required in ' . __FUNCTION__ . '().');\n\t\t}\n\n\t\t// Handle expectation. Can also be passed as an HTTP header.\n\t\tif (isset($opt['expect']))\n\t\t{\n\t\t\t$opt['headers']['Expect'] = $opt['expect'];\n\t\t\tunset($opt['expect']);\n\t\t}\n\n\t\t// Handle content length. Can also be passed as an HTTP header.\n\t\tif (isset($opt['length']))\n\t\t{\n\t\t\t$opt['headers']['Content-Length'] = $opt['length'];\n\t\t\tunset($opt['length']);\n\t\t}\n\n\t\t// Handle content md5. Can also be passed as an HTTP header.\n\t\tif (isset($opt['md5']))\n\t\t{\n\t\t\t$opt['headers']['Content-MD5'] = $opt['md5'];\n\t\t\tunset($opt['md5']);\n\t\t}\n\n\t\t$opt['headers']['Expect'] = '100-continue';\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\t/**\n\t * Lists the completed parts of an in-progress multipart upload.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param string $filename (Required) The file name for the object.\n\t * @param string $upload_id (Required) The upload ID identifying the multipart upload whose parts are being listed. The upload ID is retrieved from a call to <initiate_multipart_upload()>.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<li><code>max-parts</code> - <code>integer</code> - Optional - The maximum number of parts to return in the response body.</li>\n\t * \t<li><code>part-number-marker</code> - <code>string</code> - Optional - Restricts the response to contain results that only occur numerically after the value of the <code>part-number-marker</code>.</li>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t */\n\tpublic function list_parts($bucket, $filename, $upload_id, $opt = null)\n\t{\n\t\tif (!$opt) $opt = array();\n\n\t\t// Add this to our request\n\t\t$opt['verb'] = 'GET';\n\t\t$opt['resource'] = $filename;\n\t\t$opt['uploadId'] = $upload_id;\n\t\t$opt['query_string'] = array();\n\n\t\tforeach (array('max-parts', 'part-number-marker') as $param)\n\t\t{\n\t\t\tif (isset($opt[$param]))\n\t\t\t{\n\t\t\t\t$opt['query_string'][$param] = $opt[$param];\n\t\t\t\tunset($opt[$param]);\n\t\t\t}\n\t\t}\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\t/**\n\t * Aborts an in-progress multipart upload. This operation cannot be reversed.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param string $filename (Required) The file name for the object.\n\t * @param string $upload_id (Required) The upload ID identifying the multipart upload whose parts are being listed. The upload ID is retrieved from a call to <initiate_multipart_upload()>.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t */\n\tpublic function abort_multipart_upload($bucket, $filename, $upload_id, $opt = null)\n\t{\n\t\tif (!$opt) $opt = array();\n\n\t\t// Add this to our request\n\t\t$opt['verb'] = 'DELETE';\n\t\t$opt['resource'] = $filename;\n\t\t$opt['uploadId'] = $upload_id;\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\t/**\n\t * Completes an in-progress multipart upload. A multipart upload is completed by describing the part\n\t * numbers and corresponding ETag values in order, and submitting that data to Amazon S3 as an XML document.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param string $filename (Required) The file name for the object.\n\t * @param string $upload_id (Required) The upload ID identifying the multipart upload whose parts are being listed. The upload ID is retrieved from a call to <initiate_multipart_upload()>.\n\t * @param string|array|SimpleXMLElement|CFResponse $parts (Required) The completion XML document. This document can be provided in multiple ways; as a string of XML, as a <php:SimpleXMLElement> object representing the XML document, as an indexed array of associative arrays where the keys are <code>PartNumber</code> and <code>ETag</code>, or as a <CFResponse> object returned by <list_parts()>.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t */\n\tpublic function complete_multipart_upload($bucket, $filename, $upload_id, $parts, $opt = null)\n\t{\n\t\tif (!$opt) $opt = array();\n\n\t\t// Add this to our request\n\t\t$opt['verb'] = 'POST';\n\t\t$opt['resource'] = $filename;\n\t\t$opt['uploadId'] = $upload_id;\n\t\t$opt['headers'] = array(\n\t\t\t'Content-Type' => 'application/xml'\n\t\t);\n\n\t\t// Disable Content-MD5 calculation for this operation\n\t\t$opt['NoContentMD5'] = true;\n\n\t\tif (is_string($parts))\n\t\t{\n\t\t\t// Assume it's the intended XML.\n\t\t\t$opt['body'] = $parts;\n\t\t}\n\t\telseif ($parts instanceof SimpleXMLElement)\n\t\t{\n\t\t\t// Assume it's a SimpleXMLElement object representing the XML.\n\t\t\t$opt['body'] = $parts->asXML();\n\t\t}\n\t\telseif (is_array($parts) || $parts instanceof CFResponse)\n\t\t{\n\t\t\t$xml = simplexml_load_string($this->complete_mpu_xml);\n\n\t\t\tif (is_array($parts))\n\t\t\t{\n\t\t\t\t// Generate the appropriate XML.\n\t\t\t\tforeach ($parts as $node)\n\t\t\t\t{\n\t\t\t\t\t$part = $xml->addChild('Part');\n\t\t\t\t\t$part->addChild('PartNumber', $node['PartNumber']);\n\t\t\t\t\t$part->addChild('ETag', $node['ETag']);\n\t\t\t\t}\n\n\t\t\t}\n\t\t\telseif ($parts instanceof CFResponse)\n\t\t\t{\n\t\t\t\t// Assume it's a response from list_parts().\n\t\t\t\tforeach ($parts->body->Part as $node)\n\t\t\t\t{\n\t\t\t\t\t$part = $xml->addChild('Part');\n\t\t\t\t\t$part->addChild('PartNumber', (string) $node->PartNumber);\n\t\t\t\t\t$part->addChild('ETag', (string) $node->ETag);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t$opt['body'] = $xml->asXML();\n\t\t}\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\t/**\n\t * Lists the in-progress multipart uploads.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<li><code>key-marker</code> - <code>string</code> - Optional - Restricts the response to contain results that only occur alphabetically after the value of the <code>key-marker</code>. If used in conjunction with <code>upload-id-marker</code>, the results will be filtered to include keys whose upload ID is alphabetically after the value of <code>upload-id-marker</code>.</li>\n\t * \t<li><code>upload-id-marker</code> - <code>string</code> - Optional - Restricts the response to contain results that only occur alphabetically after the value of the <code>upload-id-marker</code>. Must be used in conjunction with <code>key-marker</code>.</li>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t */\n\tpublic function list_multipart_uploads($bucket, $opt = null)\n\t{\n\t\tif (!$opt) $opt = array();\n\n\t\t// Add this to our request\n\t\t$opt['verb'] = 'GET';\n\t\t$opt['sub_resource'] = 'uploads';\n\n\t\tforeach (array('key-marker', 'max-uploads', 'upload-id-marker') as $param)\n\t\t{\n\t\t\tif (isset($opt[$param]))\n\t\t\t{\n\t\t\t\t$opt['query_string'][$param] = $opt[$param];\n\t\t\t\tunset($opt[$param]);\n\t\t\t}\n\t\t}\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\t/**\n\t * Since Amazon S3's standard <copy_object()> operation only supports copying objects that are smaller than\n\t * 5 GB, the ability to copy large objects (greater than 5 GB) requires the use of \"Multipart Copy\".\n\t *\n\t * Copying large objects requires the developer to initiate a new multipart \"upload\", copy pieces of the\n\t * large object (specifying a range of bytes up to 5 GB from the large source file), then complete the\n\t * multipart \"upload\".\n\t *\n\t * NOTE: <strong>This is a synchronous operation</strong>, not an <em>asynchronous</em> operation, which means\n\t * that Amazon S3 will not return a response for this operation until the copy has completed across the Amazon\n\t * S3 server fleet. Copying objects within a single region will complete more quickly than copying objects\n\t * <em>across</em> regions. The synchronous nature of this operation is different from other services where\n\t * responses are typically returned immediately, even if the operation itself has not yet been completed on\n\t * the server-side.\n\t *\n\t * @param array $source (Required) The bucket and file name to copy from. The following keys must be set: <ul>\n\t * \t<li><code>bucket</code> - <code>string</code> - Required - Specifies the name of the bucket containing the source object.</li>\n\t * \t<li><code>filename</code> - <code>string</code> - Required - Specifies the file name of the source object to copy.</li></ul>\n\t * @param array $dest (Required) The bucket and file name to copy to. The following keys must be set: <ul>\n\t * \t<li><code>bucket</code> - <code>string</code> - Required - Specifies the name of the bucket to copy the object to.</li>\n\t * \t<li><code>filename</code> - <code>string</code> - Required - Specifies the file name to copy the object to.</li></ul>\n\t * @param string $upload_id (Required) The upload ID identifying the multipart upload whose parts are being listed. The upload ID is retrieved from a call to <initiate_multipart_upload()>.\n\t * @param integer $part_number (Required) A part number uniquely identifies a part and defines its position within the destination object. When you complete a multipart upload, a complete object is created by concatenating parts in ascending order based on part number. If you copy a new part using the same part number as a previously copied/uploaded part, the previously written part is overwritten.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * \t<li><code>range</code> - <code>string</code> - Optional - The range of bytes to copy from the object. Specify this parameter when copying partial bits. The specified range must be notated with a hyphen (e.g., 0-10485759). Defaults to the byte range of the complete Amazon S3 object.</li>\n\t * \t<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>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t */\n\tpublic function copy_part($source, $dest, $upload_id, $part_number, $opt = null)\n\t{\n\t\tif (!$opt) $opt = array();\n\n\t\t// Add this to our request\n\t\t$opt['verb'] = 'PUT';\n\t\t$opt['resource'] = $dest['filename'];\n\t\t$opt['uploadId'] = $upload_id;\n\t\t$opt['partNumber'] = $part_number;\n\n\t\t// Handle copy source\n\t\tif (isset($source['bucket']) && isset($source['filename']))\n\t\t{\n\t\t\t$opt['headers']['x-amz-copy-source'] = '/' . $source['bucket'] . '/' . rawurlencode($source['filename'])\n\t\t\t\t. (isset($opt['versionId']) ? ('?' . 'versionId=' . rawurlencode($opt['versionId'])) : ''); // Append the versionId to copy, if available\n\t\t\tunset($opt['versionId']);\n\t\t}\n\n\t\t// Handle conditional-copy parameters\n\t\tif (isset($opt['ifMatch']))\n\t\t{\n\t\t\t$opt['headers']['x-amz-copy-source-if-match'] = $opt['ifMatch'];\n\t\t\tunset($opt['ifMatch']);\n\t\t}\n\t\tif (isset($opt['ifNoneMatch']))\n\t\t{\n\t\t\t$opt['headers']['x-amz-copy-source-if-none-match'] = $opt['ifNoneMatch'];\n\t\t\tunset($opt['ifNoneMatch']);\n\t\t}\n\t\tif (isset($opt['ifUnmodifiedSince']))\n\t\t{\n\t\t\t$opt['headers']['x-amz-copy-source-if-unmodified-since'] = $opt['ifUnmodifiedSince'];\n\t\t\tunset($opt['ifUnmodifiedSince']);\n\t\t}\n\t\tif (isset($opt['ifModifiedSince']))\n\t\t{\n\t\t\t$opt['headers']['x-amz-copy-source-if-modified-since'] = $opt['ifModifiedSince'];\n\t\t\tunset($opt['ifModifiedSince']);\n\t\t}\n\n\t\t// Partial content range\n\t\tif (isset($opt['range']))\n\t\t{\n\t\t\t$opt['headers']['x-amz-copy-source-range'] = 'bytes=' . $opt['range'];\n\t\t}\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($dest['bucket'], $opt);\n\t}\n\n\t/**\n\t * Creates an Amazon S3 object using the multipart upload APIs. It is analogous to <create_object()>.\n\t *\n\t * While each individual part of a multipart upload can hold up to 5 GB of data, this method limits the\n\t * part size to a maximum of 500 MB. The combined size of all parts can not exceed 5 TB of data. When an\n\t * object is stored in Amazon S3, the data is streamed to multiple storage servers in multiple data\n\t * centers. This ensures the data remains available in the event of internal network or hardware failure.\n\t *\n\t * Amazon S3 charges for storage as well as requests to the service. Smaller part sizes (and more\n\t * requests) allow for faster failures and better upload reliability. Larger part sizes (and fewer\n\t * requests) costs slightly less but has lower upload reliability.\n\t *\n\t * In certain cases with large objects, it's possible for this method to attempt to open more file system\n\t * connections than allowed by the OS. In this case, either\n\t * <a href=\"https://forums.aws.amazon.com/thread.jspa?threadID=70216\">increase the number of connections\n\t * allowed</a> or increase the value of the <code>partSize</code> parameter to use a larger part size.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param string $filename (Required) The file name for the object.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<li><code>fileUpload</code> - <code>string|resource</code> - Required - The URL/path for the file to upload, or an open resource.</li>\n\t * \t<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 <code>ACL_PRIVATE</code>.</li>\n\t * \t<li><code>contentType</code> - <code>string</code> - Optional - The type of content that is being sent in the body. The default value is <code>application/octet-stream</code>.</li>\n\t * \t<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>\n\t * \t<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>\n\t * \t<li><code>limit</code> - <code>integer</code> - Optional - The maximum number of concurrent uploads done by cURL. Gets passed to <code>CFBatchRequest</code>.</li>\n\t * \t<li><code>meta</code> - <code>array</code> - Optional - An associative array of key-value pairs. Any header starting with <code>x-amz-meta-:</code> 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>\n\t * \t<li><code>partSize</code> - <code>integer</code> - Optional - The size of an individual part. The size may not be smaller than 5 MB or larger than 500 MB. The default value is 50 MB.</li>\n\t * \t<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>\n\t * \t<li><code>seekTo</code> - <code>integer</code> - Optional - The starting position in bytes for the first piece of the file/stream to upload.</li>\n\t * \t<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>\n\t * \t<li><code>uploadId</code> - <code>string</code> - Optional - An upload ID identifying an existing multipart upload to use. If this option is not set, one will be created automatically.</li>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAccessPolicy.html REST Access Control Policy\n\t */\n\tpublic function create_mpu_object($bucket, $filename, $opt = null)\n\t{\n\t\tif ($this->use_batch_flow)\n\t\t{\n\t\t\tthrow new S3_Exception(__FUNCTION__ . '() cannot be batch requested');\n\t\t}\n\n\t\tif (!$opt) $opt = array();\n\n\t\t// Handle content length. Can also be passed as an HTTP header.\n\t\tif (isset($opt['length']))\n\t\t{\n\t\t\t$opt['headers']['Content-Length'] = $opt['length'];\n\t\t\tunset($opt['length']);\n\t\t}\n\n\t\t// URI to redirect to. Can also be passed as an HTTP header.\n\t\tif (isset($opt['redirectTo']))\n\t\t{\n\t\t\t$opt['headers']['x-amz-website-redirect-location'] = $opt['redirectTo'];\n\t\t\tunset($opt['redirectTo']);\n\t\t}\n\n\t\tif (!isset($opt['fileUpload']))\n\t\t{\n\t\t\tthrow new S3_Exception('The `fileUpload` option is required in ' . __FUNCTION__ . '().');\n\t\t}\n\t\telseif (is_resource($opt['fileUpload']))\n\t\t{\n\t\t\t$opt['limit'] = 1; // We can only read from this one resource.\n\t\t\t$upload_position = isset($opt['seekTo']) ? (integer) $opt['seekTo'] : ftell($opt['fileUpload']);\n\t\t\t$upload_filesize = isset($opt['headers']['Content-Length']) ? (integer) $opt['headers']['Content-Length'] : null;\n\n\t\t\tif (!isset($upload_filesize) && $upload_position !== false)\n\t\t\t{\n\t\t\t\t$stats = fstat($opt['fileUpload']);\n\n\t\t\t\tif ($stats && $stats['size'] >= 0)\n\t\t\t\t{\n\t\t\t\t\t$upload_filesize = $stats['size'] - $upload_position;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$upload_position = isset($opt['seekTo']) ? (integer) $opt['seekTo'] : 0;\n\n\t\t\tif (isset($opt['headers']['Content-Length']))\n\t\t\t{\n\t\t\t\t$upload_filesize = (integer) $opt['headers']['Content-Length'];\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$upload_filesize = filesize($opt['fileUpload']);\n\n\t\t\t\tif ($upload_filesize !== false)\n\t\t\t\t{\n\t\t\t\t\t$upload_filesize -= $upload_position;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif ($upload_position === false || !isset($upload_filesize) || $upload_filesize === false || $upload_filesize < 0)\n\t\t{\n\t\t\tthrow new S3_Exception('The size of `fileUpload` cannot be determined in ' . __FUNCTION__ . '().');\n\t\t}\n\n\t\t// Handle part size\n\t\tif (isset($opt['partSize']))\n\t\t{\n\t\t\t// If less that 5 MB...\n\t\t\tif ((integer) $opt['partSize'] < 5242880)\n\t\t\t{\n\t\t\t\t$opt['partSize'] = 5242880; // 5 MB\n\t\t\t}\n\t\t\t// If more than 500 MB...\n\t\t\telseif ((integer) $opt['partSize'] > 524288000)\n\t\t\t{\n\t\t\t\t$opt['partSize'] = 524288000; // 500 MB\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$opt['partSize'] = 52428800; // 50 MB\n\t\t}\n\n\t\t// If the upload size is smaller than the piece size, failover to create_object().\n\t\tif ($upload_filesize < $opt['partSize'] && !isset($opt['uploadId']))\n\t\t{\n\t\t\treturn $this->create_object($bucket, $filename, $opt);\n\t\t}\n\n\t\t// Initiate multipart upload\n\t\tif (isset($opt['uploadId']))\n\t\t{\n\t\t\t$upload_id = $opt['uploadId'];\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// Compose options for initiate_multipart_upload().\n\t\t\t$_opt = array();\n\t\t\tforeach (array('contentType', 'acl', 'storage', 'headers', 'meta') as $param)\n\t\t\t{\n\t\t\t\tif (isset($opt[$param]))\n\t\t\t\t{\n\t\t\t\t\t$_opt[$param] = $opt[$param];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t$upload = $this->initiate_multipart_upload($bucket, $filename, $_opt);\n\t\t\tif (!$upload->isOK())\n\t\t\t{\n\t\t\t\treturn $upload;\n\t\t\t}\n\n\t\t\t// Fetch the UploadId\n\t\t\t$upload_id = (string) $upload->body->UploadId;\n\t\t}\n\n\t\t// Get the list of pieces\n\t\t$pieces = $this->get_multipart_counts($upload_filesize, (integer) $opt['partSize']);\n\n\t\t// Queue batch requests\n\t\t$batch = new CFBatchRequest(isset($opt['limit']) ? (integer) $opt['limit'] : null);\n\t\tforeach ($pieces as $i => $piece)\n\t\t{\n\t\t\t$this->batch($batch)->upload_part($bucket, $filename, $upload_id, array(\n\t\t\t\t'expect' => '100-continue',\n\t\t\t\t'fileUpload' => $opt['fileUpload'],\n\t\t\t\t'partNumber' => ($i + 1),\n\t\t\t\t'seekTo' => $upload_position + (integer) $piece['seekTo'],\n\t\t\t\t'length' => (integer) $piece['length'],\n\t\t\t));\n\t\t}\n\n\t\t// Send batch requests\n\t\t$batch_responses = $this->batch($batch)->send();\n\t\tif (!$batch_responses->areOK())\n\t\t{\n\t\t\treturn $batch_responses;\n\t\t}\n\n\t\t// Compose completion XML\n\t\t$parts = array();\n\t\tforeach ($batch_responses as $i => $response)\n\t\t{\n\t\t\t$parts[] = array('PartNumber' => ($i + 1), 'ETag' => $response->header['etag']);\n\t\t}\n\n\t\treturn $this->complete_multipart_upload($bucket, $filename, $upload_id, $parts);\n\t}\n\n\t/**\n\t * Aborts all multipart uploads initiated before the specified date. This operation cannot be reversed.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param string|integer $when (Optional) The time and date to use for comparison. Accepts any value that <php:strtotime()> understands.\n\t * @return CFArray A <CFArray> containing a series of 0 or more <CFResponse> objects, containing a parsed HTTP response.\n\t */\n\tpublic function abort_multipart_uploads_by_date($bucket, $when = null)\n\t{\n\t\tif ($this->use_batch_flow)\n\t\t{\n\t\t\t// @codeCoverageIgnoreStart\n\t\t\tthrow new S3_Exception(__FUNCTION__ . '() cannot be batch requested');\n\t\t\t// @codeCoverageIgnoreEnd\n\t\t}\n\n\t\t$when = $when ? $when : time();\n\t\t$handles = array();\n\t\t$data = $this->list_multipart_uploads($bucket)->body;\n\t\t$when = is_int($when) ? $when : strtotime($when);\n\n\t\tif (!($data instanceof CFSimpleXML))\n\t\t{\n\t\t\treturn false;\n\t\t}\n\n\t\t$list = $data->query('descendant-or-self::Upload/Initiated');\n\n\t\tif (count($list) > 0)\n\t\t{\n\t\t\tforeach ($list as $node)\n\t\t\t{\n\t\t\t\tif (strtotime((string) $node) < $when)\n\t\t\t\t{\n\t\t\t\t\t$q = new CFBatchRequest();\n\t\t\t\t\t$parent = $node->parent();\n\n\t\t\t\t\t$upload_id = $parent\n\t\t\t\t\t\t->query('descendant-or-self::UploadId')\n\t\t\t\t\t\t->first()\n\t\t\t\t\t\t->to_string();\n\n\t\t\t\t\t$filename = $parent\n\t\t\t\t\t\t->query('descendant-or-self::Key')\n\t\t\t\t\t\t->first()\n\t\t\t\t\t\t->to_string();\n\n\t\t\t\t\t$handles[] = $this->abort_multipart_upload($bucket, $filename, $upload_id, array(\n\t\t\t\t\t\t'returnCurlHandle' => true\n\t\t\t\t\t));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t$http = new CFRequest();\n\t\t\t$responses = $http->send_multi_request($handles);\n\n\t\t\tif (is_array($responses) && count($responses) > 0)\n\t\t\t{\n\t\t\t\treturn new CFArray($responses);\n\t\t\t}\n\t\t}\n\n\t\treturn new CFArray();\n\t}\n\n\n\t/*%******************************************************************************************%*/\n\t// WEBSITE CONFIGURATION\n\n\t/**\n\t * Enables and configures an Amazon S3 website using the corresponding bucket as the content source.\n\t * The website will have one default domain name associated with it, which is the bucket name. If you\n\t * attempt to configure an Amazon S3 website for a bucket whose name is not compatible with DNS,\n\t * Amazon S3 returns an <code>InvalidBucketName</code> error. For more information on bucket names and DNS,\n\t * refer to <a href=\"http://docs.amazonwebservices.com/AmazonS3/latest/dev/BucketRestrictions.html\">Bucket Restrictions and Limitations.</a>\n\t *\n\t * To visit the bucket as a website a new endpoint is created in the following pattern:\n\t * <code>http://&lt;bucketName&gt;.s3-website-&lt;region&gt;.amazonaws.com</code>. This is a sample URL\n\t * for a bucket called <code>example-bucket</code> in the <code>us-east-1</code> region.\n\t * (e.g., <code>http://example-bucket.s3-website-us-east-1.amazonaws.com</code>)\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<li><code>indexDocument</code> - <code>string</code> - Optional - The file path to use as the root document. The default value is <code>index.html</code>.</li>\n\t * \t<li><code>errorDocument</code> - <code>string</code> - Optional - The file path to use as the error document. The default value is <code>error.html</code>.</li>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t */\n\tpublic function create_website_config($bucket, $opt = null)\n\t{\n\t\tif (!$opt) $opt = array();\n\t\t$opt['verb'] = 'PUT';\n\t\t$opt['sub_resource'] = 'website';\n\n\t\t$xml = simplexml_load_string($this->website_config_xml);\n\t\tif (isset($opt['indexDocument']))\n\t\t{\n\t\t\t$xml->IndexDocument->Suffix = $opt['indexDocument'];\n\t\t}\n\t\tif (isset($opt['errorDocument']))\n\t\t{\n\t\t\t$xml->ErrorDocument->Key = $opt['errorDocument'];\n\t\t}\n\n\t\t$opt['body'] = $xml->asXML();\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\t/**\n\t * Retrieves the website configuration for a bucket. The contents of this response are identical to the\n\t * content submitted by the user during the website creation operation. If a website configuration has\n\t * never been set, Amazon S3 will return a 404 error.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t */\n\tpublic function get_website_config($bucket, $opt = null)\n\t{\n\t\tif (!$opt) $opt = array();\n\t\t$opt['verb'] = 'GET';\n\t\t$opt['sub_resource'] = 'website';\n\t\t$opt['headers'] = array(\n\t\t\t'Content-Type' => 'application/xml'\n\t\t);\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\t/**\n\t * Removes the website configuration for a bucket.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t */\n\tpublic function delete_website_config($bucket, $opt = null)\n\t{\n\t\tif (!$opt) $opt = array();\n\t\t$opt['verb'] = 'DELETE';\n\t\t$opt['sub_resource'] = 'website';\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\n\t/*%******************************************************************************************%*/\n\t// OBJECT LIFECYCLE\n\n\t/**\n\t * Enables the ability to specify a configuration that relates to the object's _lifecycle_.\n\t *\n\t * **NOTE:** In cases where the lifecycle configuration dictates that an object should be deleted, Amazon S3\n\t * guarantees that the object will be deleted when the expiration time is passed.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<li><code>rules</code> - <code>string</code> - Required - The object expiration rule-sets to apply to the bucket. <ul>\n\t * \t\t<li><code>x</code> - <code>array</code> - Required - This represents a simple array index. <ul>\n\t * \t\t\t<li><code>id</code> - <code>string</code> - Optional - Unique identifier for the rule. The value cannot be longer than 255 characters.</li>\n\t * \t\t\t<li><code>prefix</code> - <code>string</code> - Required - The Amazon S3 object prefix which targets the file(s) for expiration.</li>\n\t * \t\t\t<li><code>expiration</code> - <code>array</code> - Optional - The container for the unit of measurement by which the expiration time is calculated. At least one action (either <code>transition</code> or <code>expiration</code>) is required within one lifecycle rule. <ul>\n\t *     \t\t\t<li><code>date</code> - <code>string</code> - Conditionally Required - The timestamp for when the targetted objects are to be moved or expired from the bucket. Should be in ISO 8601 Format. HH:MM:SS will be enforced as midnight GMT/UTC.</li>\n\t * \t\t\t\t<li><code>days</code> - <code>integer</code> - Conditionally Required - The number of days until the targetted objects are to be moved or expired from the bucket. Must be a positive integer.</li>\n\t * \t\t\t</ul></li>\n\t * \t\t\t<li><code>transition</code> - <code>array</code> - Optional - The container for the element that describes a transition action. At least one action (either <code>transition</code> or <code>expiration</code>) is required within one lifecycle rule. <ul>\n\t *     \t\t\t<li><code>date</code> - <code>string</code> - Conditionally Required - The timestamp for when the targetted objects are to be moved or expired from the bucket. Should be in ISO 8601 Format. HH:MM:SS will be enforced as midnight GMT/UTC.</li>\n\t * \t\t\t\t<li><code>days</code> - <code>integer</code> - Conditionally Required - The number of days until the targetted objects are to be moved or expired from the bucket. Must be a positive integer.</li>\n\t *     \t\t\t<li><code>storage</code> - <code>string</code> - Required - The storage setting of an object. [Allowed values: <code>AmazonS3::STORAGE_STANDARD</code>, <code>AmazonS3::STORAGE_REDUCED</code>, <code>STORAGE_GLACIER</code>]. The default value is <code>STORAGE_STANDARD</code>.</li>\n\t * \t\t\t</ul></li>\n\t * \t\t\t<li><code>enabled</code> - <code>boolean</code> - Optional - Whether or not to enable this rule-set. A value of <code>true</code> enables the rule-set. A value of <code>false</code> disables the rule-set. The default value is <code>true</code>.</li>\n\t * \t\t</ul></li>\n\t * \t</ul></li>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t */\n\tpublic function create_lifecycle_config($bucket, $opt = null)\n\t{\n\t\tif (!$opt) $opt = array();\n\t\t$opt['verb'] = 'PUT';\n\t\t$opt['sub_resource'] = 'lifecycle';\n\t\t$opt['headers'] = array(\n\t\t\t'Content-Type' => 'application/xml'\n\t\t);\n\n\t\t$xml = simplexml_load_string($this->object_expiration_xml, $this->parser_class);\n\n\t\tif (isset($opt['rules']) && is_array($opt['rules']) && count($opt['rules']))\n\t\t{\n\t\t\tforeach ($opt['rules'] as $rule)\n\t\t\t{\n\t\t\t\t$xrule = $xml->addChild('Rule');\n\n\t\t\t\t// ID\n\t\t\t\tif (isset($rule['id']))\n\t\t\t\t{\n\t\t\t\t\tif (strlen($rule['id']) > 255)\n\t\t\t\t\t{\n\t\t\t\t\t\tthrow new S3_Exception('The \"id\" for a rule must not be more than 255 characters in the ' . __FUNCTION__ . ' method.');\n\t\t\t\t\t}\n\n\t\t\t\t\t$xrule->addChild('ID', $rule['id']);\n\t\t\t\t}\n\n\t\t\t\t// Prefix\n\t\t\t\tif (isset($rule['prefix']))\n\t\t\t\t{\n\t\t\t\t\t$xrule->addChild('Prefix', $rule['prefix']);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tthrow new S3_Exception('Each rule requires a \"prefix\" in the ' . __FUNCTION__ . ' method.');\n\t\t\t\t}\n\n\t\t\t\t// Status\n\t\t\t\t$enabled = 'Enabled';\n\t\t\t\tif (isset($rule['enabled']))\n\t\t\t\t{\n\t\t\t\t\tif (is_bool($rule['enabled'])) // Boolean\n\t\t\t\t\t{\n\t\t\t\t\t\t$enabled = $rule['enabled'] ? 'Enabled' : 'Disabled';\n\t\t\t\t\t}\n\t\t\t\t\telseif (is_string($rule['enabled'])) // String\n\t\t\t\t\t{\n\t\t\t\t\t\t$enabled = (strtolower($rule['enabled']) === 'true') ? 'Enabled' : 'Disabled';\n\t\t\t\t\t}\n\n\t\t\t\t\t$xrule->addChild('Status', $enabled);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t$xrule->addChild('Status', 'Enabled');\n\t\t\t\t}\n\n\t\t\t\t// Expiration\n\t\t\t\tif (isset($rule['expiration']))\n\t\t\t\t{\n\t\t\t\t\t$xexpiration = $xrule->addChild('Expiration');\n\n\t\t\t\t\tif (isset($rule['expiration']['date']))\n\t\t\t\t\t{\n\t\t\t\t\t\t$xexpiration->addChild('Date', $rule['expiration']['date']);\n\t\t\t\t\t}\n\t\t\t\t\telseif (isset($rule['expiration']['days']))\n\t\t\t\t\t{\n\t\t\t\t\t\t$xexpiration->addChild('Days', $rule['expiration']['days']);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Transition\n\t\t\t\tif (isset($rule['transition']))\n\t\t\t\t{\n\t\t\t\t\t$xtransition = $xrule->addChild('Transition');\n\n\t\t\t\t\tif (isset($rule['transition']['date']))\n\t\t\t\t\t{\n\t\t\t\t\t\t$xtransition->addChild('Date', $rule['transition']['date']);\n\t\t\t\t\t}\n\t\t\t\t\telseif (isset($rule['transition']['days']))\n\t\t\t\t\t{\n\t\t\t\t\t\t$xtransition->addChild('Days', $rule['transition']['days']);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (isset($rule['transition']['storage']))\n\t\t\t\t\t{\n\t\t\t\t\t\t$xtransition->addChild('StorageClass', $rule['transition']['storage']);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (!isset($rule['expiration']) && !isset($rule['transition']))\n\t\t\t\t{\n\t\t\t\t\tthrow new S3_Exception('Each rule requires a either a \"transition\" or \"expiration\" entry in the ' . __FUNCTION__ . ' method.');\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t$opt['body'] = $xml->asXML();\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\t/**\n\t * Retrieves the configuration that relates to the object's _lifecycle_.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t */\n\tpublic function get_lifecycle_config($bucket, $opt = null)\n\t{\n\t\tif (!$opt) $opt = array();\n\t\t$opt['verb'] = 'GET';\n\t\t$opt['sub_resource'] = 'lifecycle';\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\t/**\n\t * Deletes the configuration that relates to the object's _lifecycle_.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t */\n\tpublic function delete_lifecycle_config($bucket, $opt = null)\n\t{\n\t\tif (!$opt) $opt = array();\n\t\t$opt['verb'] = 'DELETE';\n\t\t$opt['sub_resource'] = 'lifecycle';\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\t/**\n\t * Restore an object archived in Amazon Glacier back to Amazon S3.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param string $filename (Required) The file name for the object.\n\t * @param integer $days (Required) The number of days until the targeted objects are to be moved or expired from the bucket. Must be a positive integer.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t */\n\tpublic function restore_archived_object($bucket, $filename, $days, $opt = null)\n\t{\n\t\tif (!$opt) $opt = array();\n\t\t$opt['verb'] = 'POST';\n\t\t$opt['resource'] = $filename;\n\t\t$opt['sub_resource'] = 'restore';\n\t\t$opt['headers'] = array(\n\t\t\t'Content-Type' => 'application/xml'\n\t\t);\n\n\t\t$xml = simplexml_load_string($this->restore_request_xml, $this->parser_class);\n\t\t$xml->addChild('Days', (integer) $days);\n\n\t\t$opt['body'] = $xml->asXML();\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\t/**\n\t * Alias of {@see AmazonS3::create_lifecycle_config()}.\n\t *\n\t * @deprecated\n\t */\n\tpublic function create_object_expiration_config($bucket, $opt = null)\n\t{\n\t\treturn $this->create_lifecycle_config($bucket, $opt);\n\t}\n\n\t/**\n\t * Alias of {@see AmazonS3::get_lifecycle_config()}.\n\t *\n\t * @deprecated\n\t */\n\tpublic function get_object_expiration_config($bucket, $opt = null)\n\t{\n\t\treturn $this->get_lifecycle_config($bucket, $opt);\n\t}\n\n\t/**\n\t * Alias of {@see AmazonS3::delete_lifecycle_config()}.\n\t *\n\t * @deprecated\n\t */\n\tpublic function delete_object_expiration_config($bucket, $opt = null)\n\t{\n\t\treturn $this->delete_lifecycle_config($bucket, $opt);\n\t}\n\n\n\t/*%******************************************************************************************%*/\n\t// BUCKET TAGS\n\n\t/**\n\t * Apply a set of tags to the specified bucket. Bucket Tags simplify the task of associating Amazon S3\n\t * costs with specific buckets.\n\t *\n\t * This operation requires permission to perform <code>s3:PutBucketTagging</code> actions. By default,\n\t * the bucket owner is permitted to perform these actions, and can grant permission to other users.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<li><code>tags</code> - <code>array</code> - Required - An associative array of custom key-value pairs. <ul>\n\t * \t\t<li><code>[custom-key]</code> - <code>string</code> - Optional - A custom key-value pair to tag the bucket with.</li>\n\t * \t</ul></li>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t */\n\tpublic function create_bucket_tags($bucket, $opt = null)\n\t{\n\t\tif (!$opt) $opt = array();\n\t\t$opt['verb'] = 'PUT';\n\t\t$opt['sub_resource'] = 'tagging';\n\n\t\t$xml = simplexml_load_string($this->bucket_tagging_xml);\n\t\tif (isset($opt['tags']) && is_array($opt['tags']))\n\t\t{\n\t\t\tforeach ($opt['tags'] as $key => $value)\n\t\t\t{\n\t\t\t\t$xtag = $xml->TagSet->addChild('Tag');\n\t\t\t\t$xtag->addChild('Key', $key);\n\t\t\t\t$xtag->addChild('Value', $value);\n\t\t\t}\n\t\t}\n\n\t\t$opt['body'] = $xml->asXML();\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\t/**\n\t * Retrieve all associated tags for the specified bucket.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t */\n\tpublic function get_bucket_tags($bucket, $opt = null)\n\t{\n\t\tif (!$opt) $opt = array();\n\t\t$opt['verb'] = 'GET';\n\t\t$opt['sub_resource'] = 'tagging';\n\t\t$opt['headers'] = array(\n\t\t\t'Content-Type' => 'application/xml'\n\t\t);\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\t/**\n\t * Delete all associated tags from the specified bucket.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t */\n\tpublic function delete_bucket_tags($bucket, $opt = null)\n\t{\n\t\tif (!$opt) $opt = array();\n\t\t$opt['verb'] = 'DELETE';\n\t\t$opt['sub_resource'] = 'tagging';\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\n\t/*%******************************************************************************************%*/\n\t// CROSS-ORIGIN RESOURCE SHARING (CORS)\n\n\t/**\n\t * Create a new CORS configuration.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<li><code>cors_rule</code> - <code>array</code> - Required - One or more rule-sets. <ul>\n\t * \t\t<li><code>x</code> - <code>array</code> - Required - This represents a simple array index. <ul>\n\t * \t\t\t<li><code>allowed_header</code> - <code>array</code> - Required - Used in response to a preflight request to indicate which HTTP headers can be used when making the actual request.</li>\n\t * \t\t\t<li><code>allowed_method</code> - <code>array</code> - Required - An array of HTTP methods to allow. There must be at least one method set. [Allowed values: `GET`, `PUT`, `HEAD`, `POST`, `DELETE`]</li>\n\t * \t\t\t<li><code>allowed_origin</code> - <code>array</code> - Required - An array of hostnames to allow. This could be `*` to indicate it is open to all domains. If one of them contains the string `*`, then there can be exactly one.</li>\n\t * \t\t\t<li><code>expose_header</code> - <code>string</code> - Optional - Enable the browser to read this header.</li>\n\t * \t\t\t<li><code>id</code> - <code>string</code> - Optional - Unique identifier for the rule. The value cannot be longer than 255 characters.</li>\n\t * \t\t\t<li><code>max_age</code> - <code>integer</code> - Optional - Alter the client's caching behavior for the pre-flight request.</li>\n\t * \t\t</ul></li>\n\t * \t</ul></li>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t */\n\tpublic function create_cors_config($bucket, $opt = null)\n\t{\n\t\tif (!$opt) $opt = array();\n\t\t$opt['verb'] = 'PUT';\n\t\t$opt['sub_resource'] = 'cors';\n\t\t$opt['headers'] = array(\n\t\t\t'Content-Type' => 'application/xml'\n\t\t);\n\n\t\t$xml = simplexml_load_string($this->cors_config_xml, $this->parser_class);\n\n\t\tif (isset($opt['cors_rule']) && is_array($opt['cors_rule']))\n\t\t{\n\t\t\tforeach ($opt['cors_rule'] as $rule_set)\n\t\t\t{\n\t\t\t\t// New rule node\n\t\t\t\t$xrule = $xml->addChild('CORSRule');\n\n\t\t\t\t// ID node\n\t\t\t\tif (isset($rule_set['id']))\n\t\t\t\t{\n\t\t\t\t\t$xrule->addChild('ID', $rule_set['id']);\n\t\t\t\t}\n\n\t\t\t\t// ExposeHeader node\n\t\t\t\tif (isset($rule_set['expose_header']))\n\t\t\t\t{\n\t\t\t\t\t$xrule->addChild('ExposeHeader', $rule_set['expose_header']);\n\t\t\t\t}\n\n\t\t\t\t// MaxAgeSeconds node\n\t\t\t\tif (isset($rule_set['max_age']))\n\t\t\t\t{\n\t\t\t\t\t$xrule->addChild('MaxAgeSeconds', $rule_set['max_age']);\n\t\t\t\t}\n\n\t\t\t\t// AllowedHeader node\n\t\t\t\tif (isset($rule_set['allowed_header']))\n\t\t\t\t{\n\t\t\t\t\tif (!is_array($rule_set['allowed_header']))\n\t\t\t\t\t{\n\t\t\t\t\t\t$rule_set['allowed_header'] = array($rule_set['allowed_header']);\n\t\t\t\t\t}\n\n\t\t\t\t\tforeach ($rule_set['allowed_header'] as $method)\n\t\t\t\t\t{\n\t\t\t\t\t\t$xrule->addChild('AllowedHeader', $method);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// AllowedMethod node\n\t\t\t\tif (isset($rule_set['allowed_method']))\n\t\t\t\t{\n\t\t\t\t\tif (!is_array($rule_set['allowed_method']))\n\t\t\t\t\t{\n\t\t\t\t\t\t$rule_set['allowed_method'] = array($rule_set['allowed_method']);\n\t\t\t\t\t}\n\n\t\t\t\t\tforeach ($rule_set['allowed_method'] as $method)\n\t\t\t\t\t{\n\t\t\t\t\t\t$xrule->addChild('AllowedMethod', $method);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// AllowedOrigin node\n\t\t\t\tif (isset($rule_set['allowed_origin']))\n\t\t\t\t{\n\t\t\t\t\tif (!is_array($rule_set['allowed_origin']))\n\t\t\t\t\t{\n\t\t\t\t\t\t$rule_set['allowed_origin'] = array($rule_set['allowed_origin']);\n\t\t\t\t\t}\n\n\t\t\t\t\tforeach ($rule_set['allowed_origin'] as $method)\n\t\t\t\t\t{\n\t\t\t\t\t\t$xrule->addChild('AllowedOrigin', $method);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t$opt['body'] = $xml->asXML();\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\t/**\n\t * Retrieves the CORS configuration.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t */\n\tpublic function get_cors_config($bucket, $opt = null)\n\t{\n\t\tif (!$opt) $opt = array();\n\t\t$opt['verb'] = 'GET';\n\t\t$opt['sub_resource'] = 'cors';\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\t/**\n\t * Deletes the CORS configuration.\n\t *\n\t * @param string $bucket (Required) The name of the bucket to use.\n\t * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>\n\t * \t<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>\n\t * \t<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>\n\t * @return CFResponse A <CFResponse> object containing a parsed HTTP response.\n\t */\n\tpublic function delete_cors_config($bucket, $opt = null)\n\t{\n\t\tif (!$opt) $opt = array();\n\t\t$opt['verb'] = 'DELETE';\n\t\t$opt['sub_resource'] = 'cors';\n\n\t\t// Authenticate to S3\n\t\treturn $this->authenticate($bucket, $opt);\n\t}\n\n\n\t/*%******************************************************************************************%*/\n\t// MISCELLANEOUS\n\n\t/**\n\t * Gets the canonical user ID and display name from the Amazon S3 server.\n\t *\n\t * @return array An associative array containing the `id` and `display_name` values.\n\t */\n\tpublic function get_canonical_user_id()\n\t{\n\t\tif ($this->use_batch_flow)\n\t\t{\n\t\t\tthrow new S3_Exception(__FUNCTION__ . '() cannot be batch requested');\n\t\t}\n\n\t\t$id = $this->list_buckets();\n\n\t\treturn array(\n\t\t\t'id' => (string) $id->body->Owner->ID,\n\t\t\t'display_name' => (string) $id->body->Owner->DisplayName\n\t\t);\n\t}\n\n\t/**\n\t * Loads and registers the S3StreamWrapper class as a stream wrapper.\n\t *\n\t * @param string $protocol (Optional) The name of the protocol to register.\n\t * @return boolean Whether or not the registration succeeded.\n\t */\n\tpublic function register_stream_wrapper($protocol = 's3')\n\t{\n\t\trequire_once dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'extensions'\n\t\t\t. DIRECTORY_SEPARATOR . 's3streamwrapper.class.php';\n\n\t\treturn S3StreamWrapper::register($this, $protocol);\n\t}\n}\n"
  },
  {
    "path": "utilities/array.class.php",
    "content": "<?php\n/*\n * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\").\n * You may not use this file except in compliance with the License.\n * A copy of the License is located at\n *\n *  http://aws.amazon.com/apache2.0\n *\n * or in the \"license\" file accompanying this file. This file is distributed\n * on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either\n * express or implied. See the License for the specific language governing\n * permissions and limitations under the License.\n */\n\n\n/*%******************************************************************************************%*/\n// CLASS\n\n/**\n * The <CFArray> object extends PHP's built-in <php:ArrayObject> object by providing convenience methods for\n * rapidly manipulating array data. Specifically, the `CFArray` object is intended for working with\n * <CFResponse> and <CFSimpleXML> objects that are returned by AWS services.\n *\n * @version 2012.01.17\n * @license See the included NOTICE.md file for more information.\n * @copyright See the included NOTICE.md file for more information.\n * @link http://aws.amazon.com/php/ PHP Developer Center\n * @link http://php.net/ArrayObject ArrayObject\n */\nclass CFArray extends ArrayObject\n{\n\t/**\n\t * Constructs a new instance of <CFArray>.\n\t *\n\t * @param mixed $input (Optional) The input parameter accepts an array or an Object. The default value is an empty array.\n\t * @param integer $flags (Optional) Flags to control the behavior of the ArrayObject object. Defaults to <STD_PROP_LIST>.\n\t * @param string $iterator_class (Optional) Specify the class that will be used for iteration of the <php:ArrayObject> object. <php:ArrayIterator> is the default class used.\n\t * @return mixed Either an array of matches, or a single <CFSimpleXML> element.\n\t */\n\tpublic function __construct($input = array(), $flags = self::STD_PROP_LIST, $iterator_class = 'ArrayIterator')\n\t{\n\t\t// Provide a default value\n\t\t$input = $input ? $input : array();\n\n\t\ttry {\n\t\t\treturn parent::__construct($input, $flags, $iterator_class);\n\t\t}\n\t\tcatch (InvalidArgumentException $e)\n\t\t{\n\t\t\tthrow new CFArray_Exception($e->getMessage());\n\t\t}\n\t}\n\n\t/**\n\t * Alternate approach to constructing a new instance. Supports chaining.\n\t *\n\t * @param mixed $input (Optional) The input parameter accepts an array or an Object. The default value is an empty array.\n\t * @param integer $flags (Optional) Flags to control the behavior of the ArrayObject object. Defaults to <STD_PROP_LIST>.\n\t * @param string $iterator_class (Optional) Specify the class that will be used for iteration of the <php:ArrayObject> object. <php:ArrayIterator> is the default class used.\n\t * @return mixed Either an array of matches, or a single <CFSimpleXML> element.\n\t */\n\tpublic static function init($input = array(), $flags = self::STD_PROP_LIST, $iterator_class = 'ArrayIterator')\n\t{\n\t\tif (version_compare(PHP_VERSION, '5.3.0', '<'))\n\t\t{\n\t\t\tthrow new Exception('PHP 5.3 or newer is required to instantiate a new class with CLASS::init().');\n\t\t}\n\n\t\t$self = get_called_class();\n\t\treturn new $self($input, $flags, $iterator_class);\n\t}\n\n\t/**\n\t * Handles how the object is rendered when cast as a string.\n\t *\n\t * @return string The word \"Array\".\n\t */\n\tpublic function __toString()\n\t{\n\t\treturn 'Array';\n\t}\n\n\n\t/*%******************************************************************************************%*/\n\t// REFORMATTING\n\n\t/**\n\t * Maps each element in the <CFArray> object as an integer.\n\t *\n\t * @return array The contents of the <CFArray> object mapped as integers.\n\t */\n\tpublic function map_integer()\n\t{\n\t\treturn array_map('intval', $this->getArrayCopy());\n\t}\n\n\t/**\n\t * Maps each element in the CFArray object as a string.\n\t *\n\t * @param string $pcre (Optional) A Perl-Compatible Regular Expression (PCRE) to filter the names against.\n\t * @return array The contents of the <CFArray> object mapped as strings. If there are no results, the method will return an empty array.\n\t */\n\tpublic function map_string($pcre = null)\n\t{\n\t\t$list = array_map('strval', $this->getArrayCopy());\n\t\t$dlist = array();\n\n\t\tif ($pcre)\n\t\t{\n\t\t\tforeach ($list as $item)\n\t\t\t{\n\t\t\t\t$dlist[] = preg_match($pcre, $item) ? $item : null;\n\t\t\t}\n\n\t\t\t$list = array_values(array_filter($dlist));\n\t\t}\n\n\t\treturn $list;\n\t}\n\n\n\t/*%******************************************************************************************%*/\n\t// CONFIRMATION\n\n\t/**\n\t * Verifies that _all_ responses were successful. A single failed request will cause <areOK()> to return false. Equivalent to <CFResponse::isOK()>, except it applies to all responses.\n\t *\n\t * @return boolean Whether _all_ requests were successful or not.\n\t */\n\tpublic function areOK()\n\t{\n\t\t$dlist = array();\n\t\t$list = $this->getArrayCopy();\n\n\t\tforeach ($list as $response)\n\t\t{\n\t\t\tif ($response instanceof CFResponse)\n\t\t\t{\n\t\t\t\t$dlist[] = $response->isOK();\n\t\t\t}\n\t\t}\n\n\t\treturn (array_search(false, $dlist, true) !== false) ? false : true;\n\t}\n\n\n\t/*%******************************************************************************************%*/\n\t// ITERATING AND EXECUTING\n\n\t/**\n\t * Iterates over a <CFArray> object, and executes a function for each matched element.\n\t *\n\t * The callback function takes three parameters: <ul>\n\t * \t<li><code>$item</code> - <code>mixed</code> - Optional - The individual node in the array.</li>\n\t * \t<li><code>$key</code> - <code>mixed</code> - Optional - The key for the array node.</li>\n\t * \t<li><code>$bind</code> - <code>mixed</code> - Optional - The variable that was passed into the $bind parameter.</li></ul>\n\t *\n\t * @param string|function $callback (Required) The callback function to execute. PHP 5.3 or newer can use an anonymous function.\n\t * @param mixed $bind (Optional) A variable from the calling scope to pass-by-reference into the local scope of the callback function.\n\t * @return CFArray The original <CFArray> object.\n\t */\n\tpublic function each($callback, &$bind = null)\n\t{\n\t\t$items = $this->getArrayCopy();\n\n\t\tforeach ($items as $key => &$item)\n\t\t{\n\t\t\t$callback($item, $key, $bind);\n\t\t}\n\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Passes each element in the current <CFArray> object through a function, and produces a new <CFArray> object containing the return values.\n\t *\n\t * The callback function takes three parameters: <ul>\n\t * \t<li><code>$item</code> - <code>mixed</code> - Optional - The individual node in the array.</li>\n\t * \t<li><code>$key</code> - <code>mixed</code> - Optional - The key for the array node.</li>\n\t * \t<li><code>$bind</code> - <code>mixed</code> - Optional - The variable that was passed into the $bind parameter.</li></ul>\n\t *\n\t * @param string|function $callback (Required) The callback function to execute. PHP 5.3 or newer can use an anonymous function.\n\t * @param mixed $bind (Optional) A variable from the calling scope to pass-by-reference into the local scope of the callback function.\n\t * @return CFArray A new <CFArray> object containing the return values.\n\t */\n\tpublic function map($callback, &$bind = null)\n\t{\n\t\t$items = $this->getArrayCopy();\n\t\t$collect = array();\n\n\t\tforeach ($items as $key => &$item)\n\t\t{\n\t\t\t$collect[] = $callback($item, $key, $bind);\n\t\t}\n\n\t\treturn new CFArray($collect);\n\t}\n\n\t/**\n\t * Filters the list of nodes by passing each value in the current <CFArray> object through a function. The node will be removed if the function returns `false`.\n\t *\n\t * The callback function takes three parameters: <ul>\n\t * \t<li><code>$item</code> - <code>mixed</code> - Optional - The individual node in the array.</li>\n\t * \t<li><code>$key</code> - <code>mixed</code> - Optional - The key for the array node.</li>\n\t * \t<li><code>$bind</code> - <code>mixed</code> - Optional - The variable that was passed into the $bind parameter.</li></ul>\n\t *\n\t * @param string|function $callback (Required) The callback function to execute. PHP 5.3 or newer can use an anonymous function.\n\t * @param mixed $bind (Optional) A variable from the calling scope to pass-by-reference into the local scope of the callback function.\n\t * @return CFArray A new <CFArray> object containing the return values.\n\t */\n\tpublic function filter($callback, &$bind = null)\n\t{\n\t\t$items = $this->getArrayCopy();\n\t\t$collect = array();\n\n\t\tforeach ($items as $key => &$item)\n\t\t{\n\t\t\tif ($callback($item, $key, $bind) !== false)\n\t\t\t{\n\t\t\t\t$collect[] = $item;\n\t\t\t}\n\t\t}\n\n\t\treturn new CFArray($collect);\n\t}\n\n\t/**\n\t * Alias for <filter()>. This functionality was incorrectly named _reduce_ in earlier versions of the SDK.\n\t *\n\t * @param string|function $callback (Required) The callback function to execute. PHP 5.3 or newer can use an anonymous function.\n\t * @param mixed $bind (Optional) A variable from the calling scope to pass-by-reference into the local scope of the callback function.\n\t * @return CFArray A new <CFArray> object containing the return values.\n\t */\n\tpublic function reduce($callback, &$bind = null)\n\t{\n\t\treturn $this->filter($callback, $bind);\n\t}\n\n\n\t/*%******************************************************************************************%*/\n\t// TRAVERSAL\n\n\t/**\n\t * Gets the first result in the array.\n\t *\n\t * @return mixed The first result in the <CFArray> object. Returns `false` if there are no items in the array.\n\t */\n\tpublic function first()\n\t{\n\t\t$items = $this->getArrayCopy();\n\t\treturn count($items) ? $items[0] : false;\n\t}\n\n\t/**\n\t * Gets the last result in the array.\n\t *\n\t * @return mixed The last result in the <CFArray> object. Returns `false` if there are no items in the array.\n\t */\n\tpublic function last()\n\t{\n\t\t$items = $this->getArrayCopy();\n\t\treturn count($items) ? end($items) : false;\n\t}\n\n\t/**\n\t * Removes all `null` values from an array.\n\t *\n\t * @return CFArray A new <CFArray> object containing the non-null values.\n\t */\n\tpublic function compress()\n\t{\n\t\treturn new CFArray(array_filter($this->getArrayCopy()));\n\t}\n\n\t/**\n\t * Reindexes the array, starting from zero.\n\t *\n\t * @return CFArray A new <CFArray> object with indexes starting at zero.\n\t */\n\tpublic function reindex()\n\t{\n\t\treturn new CFArray(array_values($this->getArrayCopy()));\n\t}\n\n\n\t/*%******************************************************************************************%*/\n\t// ALTERNATE FORMATS\n\n\t/**\n\t * Gets the current XML node as a JSON string.\n\t *\n\t * @return string The current XML node as a JSON string.\n\t */\n\tpublic function to_json()\n\t{\n\t\treturn json_encode($this->getArrayCopy());\n\t}\n\n\t/**\n\t * Gets the current XML node as a YAML string.\n\t *\n\t * @return string The current XML node as a YAML string.\n\t */\n\tpublic function to_yaml()\n\t{\n\t\treturn sfYaml::dump($this->getArrayCopy(), 5);\n\t}\n}\n\nclass CFArray_Exception extends Exception {}\n"
  },
  {
    "path": "utilities/batchrequest.class.php",
    "content": "<?php\n/*\n * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\").\n * You may not use this file except in compliance with the License.\n * A copy of the License is located at\n *\n *  http://aws.amazon.com/apache2.0\n *\n * or in the \"license\" file accompanying this file. This file is distributed\n * on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either\n * express or implied. See the License for the specific language governing\n * permissions and limitations under the License.\n */\n\n\n/*%******************************************************************************************%*/\n// EXCEPTIONS\n\n/**\n * Default CFBatchRequest Exception.\n */\nclass CFBatchRequest_Exception extends Exception {}\n\n\n/*%******************************************************************************************%*/\n// CLASS\n\n/**\n * Simplifies the flow involved with managing and executing a batch request queue. Batch requesting is the\n * ability to queue up a series of requests and execute them all in parallel. This allows for faster\n * application performance when a lot of requests are involved.\n *\n * @version 2011.12.02\n * @license See the included NOTICE.md file for more information.\n * @copyright See the included NOTICE.md file for more information.\n * @link http://aws.amazon.com/php/ PHP Developer Center\n */\nclass CFBatchRequest extends CFRuntime\n{\n\t/**\n\t * Stores the cURL handles that are to be processed.\n\t */\n\tpublic $queue;\n\n\t/**\n\t * Stores the size of the request window.\n\t */\n\tpublic $limit;\n\n\t/**\n\t * The proxy to use for connecting.\n\t */\n\tpublic $proxy = null;\n\n\t/**\n\t * The helpers to use when connecting.\n\t */\n\tpublic $helpers = null;\n\n\t/**\n\t * The active credential set.\n\t */\n\tpublic $credentials;\n\n\n\t/*%******************************************************************************************%*/\n\t// CONSTRUCTOR\n\n\t/**\n\t * Constructs a new instance of this class.\n\t *\n\t * @param integer $limit (Optional) The size of the request window. Defaults to unlimited.\n\t * @return boolean `false` if no valid values are set, otherwise `true`.\n\t */\n\tpublic function __construct($limit = null)\n\t{\n\t\t$this->queue = array();\n\t\t$this->limit = $limit ? $limit : -1;\n\t\t$this->credentials = new CFCredential(array());\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Sets the AWS credentials to use for the batch request.\n\t *\n\t * @param CFCredential $credentials (Required) The credentials to use for signing and making requests.\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function use_credentials(CFCredential $credentials)\n\t{\n\t\t$this->credentials = $credentials;\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Adds a new cURL handle to the request queue.\n\t *\n\t * @param resource $handle (Required) A cURL resource to add to the queue.\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function add($handle)\n\t{\n\t\t$this->queue[] = $handle;\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Executes the batch request queue.\n\t *\n\t * @param array $opt (DO NOT USE) Enabled for compatibility with the method this overrides, although any values passed will be ignored.\n\t * @return array An indexed array of <CFResponse> objects.\n\t */\n\tpublic function send($opt = null)\n\t{\n\t\t$http = new $this->request_class(null, $this->proxy, null, $this->credentials);\n\n\t\t// Make the request\n\t\t$response = $http->send_multi_request($this->queue, array(\n\t\t\t'limit' => $this->limit\n\t\t));\n\n\t\treturn $response;\n\t}\n}\n"
  },
  {
    "path": "utilities/complextype.class.php",
    "content": "<?php\n/*\n * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\").\n * You may not use this file except in compliance with the License.\n * A copy of the License is located at\n *\n *  http://aws.amazon.com/apache2.0\n *\n * or in the \"license\" file accompanying this file. This file is distributed\n * on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either\n * express or implied. See the License for the specific language governing\n * permissions and limitations under the License.\n */\n\n\n/*%******************************************************************************************%*/\n// CLASS\n\n/**\n * Contains utility methods used for converting array, JSON, and YAML data types into query string keys.\n *\n * @version 2010.11.11\n * @license See the included NOTICE.md file for more information.\n * @copyright See the included NOTICE.md file for more information.\n * @link http://aws.amazon.com/php/ PHP Developer Center\n */\nclass CFComplexType\n{\n\t/**\n\t * Takes a JSON object, as a string, to convert to query string keys.\n\t *\n\t * @param string $json (Required) A JSON object. The JSON string should use canonical rules (e.g., double quotes, quoted keys) as is required by PHP's <php:json_encode()> function.\n\t * @param string $member (Optional) The name of the \"member\" property that AWS uses for lists in certain services. Defaults to an empty string.\n\t * @param string $default_key (Optional) The default key to use when the value for `$data` is a string. Defaults to an empty string.\n\t * @return array The option group parameters to merge into another method's `$opt` parameter.\n\t */\n\tpublic static function json($json, $member = '', $default_key = '')\n\t{\n\t\treturn self::option_group(json_decode($json, true), $member, $default_key);\n\t}\n\n\t/**\n\t * Takes a YAML object, as a string, to convert to query string keys.\n\t *\n\t * @param string $yaml (Required) A YAML object.\n\t * @param string $member (Optional) The name of the \"member\" property that AWS uses for lists in certain services. Defaults to an empty string.\n\t * @param string $default_key (Optional) The default key to use when the value for `$data` is a string. Defaults to an empty string.\n\t * @return array The option group parameters to merge into another method's `$opt` parameter.\n\t */\n\tpublic static function yaml($yaml, $member = '', $default_key = '')\n\t{\n\t\treturn self::option_group(sfYaml::load($yaml), $member, $default_key);\n\t}\n\n\t/**\n\t * Takes an associative array to convert to query string keys.\n\t *\n\t * @param array $map (Required) An associative array.\n\t * @param string $member (Optional) The name of the \"member\" property that AWS uses for lists in certain services. Defaults to an empty string.\n\t * @param string $default_key (Optional) The default key to use when the value for `$data` is a string. Defaults to an empty string.\n\t * @return array The option group parameters to merge into another method's `$opt` parameter.\n\t */\n\tpublic static function map($map, $member = '', $default_key = '')\n\t{\n\t\treturn self::option_group($map, $member, $default_key);\n\t}\n\n\t/**\n\t * A protected method that is used by <json()>, <yaml()> and <map()>.\n\t *\n\t * @param string|array $data (Required) The data to iterate over.\n\t * @param string $member (Optional) The name of the \"member\" property that AWS uses for lists in certain services. Defaults to an empty string.\n\t * @param string $key (Optional) The default key to use when the value for `$data` is a string. Defaults to an empty string.\n\t * @param array $out (Optional) INTERNAL ONLY. The array that contains the calculated values up to this point.\n\t * @return array The option group parameters to merge into another method's `$opt` parameter.\n\t */\n\tpublic static function option_group($data, $member = '', $key = '', &$out = array())\n\t{\n\t\t$reset = $key;\n\n\t\tif (is_array($data))\n\t\t{\n\t\t\tforeach ($data as $k => $v)\n\t\t\t{\n\t\t\t\t// Avoid 0-based indexes.\n\t\t\t\tif (is_int($k))\n\t\t\t\t{\n\t\t\t\t\t$k = $k + 1;\n\n\t\t\t\t\tif ($member !== '')\n\t\t\t\t\t{\n\t\t\t\t\t\t$key .= '.' . $member;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t$key .= ($key === '' ? $k : '.' . $k);\n\n\t\t\t\tif (is_array($v))\n\t\t\t\t{\n\t\t\t\t\tself::option_group($v, $member, $key, $out);\n\t\t\t\t}\n\t\t\t\telseif ($v instanceof CFStepConfig)\n\t\t\t\t{\n\t\t\t\t\tself::option_group($v->get_config(), $member, $key, $out);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\t$out[$key] = $v;\n\t\t\t\t}\n\n\t\t\t\t$key = $reset;\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$out[$key] = $data;\n\t\t}\n\n\t\treturn $out;\n\t}\n}\n"
  },
  {
    "path": "utilities/credential.class.php",
    "content": "<?php\n/*\n * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\").\n * You may not use this file except in compliance with the License.\n * A copy of the License is located at\n *\n *  http://aws.amazon.com/apache2.0\n *\n * or in the \"license\" file accompanying this file. This file is distributed\n * on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either\n * express or implied. See the License for the specific language governing\n * permissions and limitations under the License.\n */\n\n\n/*%******************************************************************************************%*/\n// CLASS\n\n/**\n * The <CFCredential> class represents an individual credential set.\n *\n * @version 2011.11.15\n * @license See the included NOTICE.md file for more information.\n * @copyright See the included NOTICE.md file for more information.\n * @link http://aws.amazon.com/php/ PHP Developer Center\n */\nclass CFCredential implements ArrayAccess\n{\n\t/**\n\t * Stores the internal <php:ArrayObject> representation of the collection.\n\t */\n\tprivate $collection;\n\n\t/**\n\t* Default getter. Enables syntax such as $object->method->chained_method();. Also supports\n\t* $object->key. Matching methods are prioritized over matching keys.\n\t*\n\t* @param string $name (Required) The name of the method to execute or key to retrieve.\n\t* @return mixed The results of calling the function <code>$name()</code>, or the value of the key <code>$object[$name]</code>.\n\t*/\n\tpublic function __get($name)\n\t{\n\t\treturn $this[$name];\n\t}\n\n\t/**\n\t* Default setter.\n\t*\n\t* @param string $name (Required) The name of the method to execute.\n\t* @param string $value (Required) The value to pass to the method.\n\t* @return mixed The results of calling the function, <code>$name</code>.\n\t*/\n\tpublic function __set($name, $value)\n\t{\n\t\t$this[$name] = $value;\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Create a clone of the object.\n\t *\n\t * @return CFCredential A clone of the current instance.\n\t */\n\tpublic function __clone()\n\t{\n\t\t$this->collection = clone $this->collection;\n\t}\n\n\n\t/*%******************************************************************************************%*/\n\t// CONSTRUCTOR\n\n\t/**\n\t * Constructs a new instance of the <CFCredential> class.\n\t */\n\tpublic function __construct($value = array())\n\t{\n\t\t$this->collection = new ArrayObject($value, ArrayObject::ARRAY_AS_PROPS);\n\t}\n\n\t/**\n\t * Check whether or not a specific offset exists.\n\t *\n\t * @param integer $offset (Required) The location in the collection to verify the existence of.\n\t * @return boolean A value of <code>true</code> indicates that the collection offset exists. A value of <code>false</code> indicates that it does not.\n\t */\n\tpublic function offsetExists($offset)\n\t{\n\t\treturn $this->collection->offsetExists($offset);\n\t}\n\n\t/**\n\t * Get the value for a specific offset.\n\t *\n\t * @param integer $offset (Required) The location in the collection to retrieve the value for.\n\t * @return mixed The value of the collection offset. <code>NULL</code> is returned if the offset does not exist.\n\t */\n\tpublic function offsetGet($offset)\n\t{\n\t\tif ($this->collection->offsetExists($offset))\n\t\t{\n\t\t\treturn $this->collection->offsetGet($offset);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Set the value for a specific offset.\n\t *\n\t * @param integer $offset (Required) The location in the collection to set a new value for.\n\t * @param mixed $value (Required) The new value for the collection location.\n\t * @return CFCredential A reference to the current collection.\n\t */\n\tpublic function offsetSet($offset, $value)\n\t{\n\t\t$this->collection->offsetSet($offset, $value);\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Unset the value for a specific offset.\n\t *\n\t * @param integer $offset (Required) The location in the collection to unset.\n\t * @return CFCredential A reference to the current collection.\n\t */\n\tpublic function offsetUnset($offset)\n\t{\n\t\t$this->collection->offsetUnset($offset);\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Merge another instance of <CFCredential> onto this one.\n\t *\n\t * @param CFCredential $credential (Required) Another instance of <CFCredential>.\n\t * @return CFCredential A reference to the current collection.\n\t */\n\tpublic function merge(CFCredential $credential)\n\t{\n\t\t$merged = array_merge($this->to_array(), $credential->to_array());\n\t\t$this->collection->exchangeArray($merged);\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Retrieves the data as a standard array.\n\t *\n\t * @return array The data as an array.\n\t */\n\tpublic function to_array()\n\t{\n\t\treturn $this->collection->getArrayCopy();\n\t}\n}\n"
  },
  {
    "path": "utilities/credentials.class.php",
    "content": "<?php\n/*\n * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\").\n * You may not use this file except in compliance with the License.\n * A copy of the License is located at\n *\n *  http://aws.amazon.com/apache2.0\n *\n * or in the \"license\" file accompanying this file. This file is distributed\n * on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either\n * express or implied. See the License for the specific language governing\n * permissions and limitations under the License.\n */\n\n\n/*%******************************************************************************************%*/\n// CLASS\n\n/**\n * The <CFCredentials> class enables developers to easily switch between multiple sets of credentials.\n *\n * @version 2012.07.13\n * @license See the included NOTICE.md file for more information.\n * @copyright See the included NOTICE.md file for more information.\n * @link http://aws.amazon.com/php/ PHP Developer Center\n */\nclass CFCredentials\n{\n\t/**\n\t * The key used to specify the default credential set\n\t */\n\tconst DEFAULT_KEY = '@default';\n\n\t/**\n\t * The key used to identify inherited credentials\n\t */\n\tconst INHERIT_KEY = '@inherit';\n\n\t/**\n\t * Stores the credentials\n\t */\n\tprotected static $credentials = array();\n\n\t/**\n\t * Prevents this class from being constructed\n\t */\n\tfinal private function __construct() {}\n\n\t/**\n\t * Stores the credentials for re-use.\n\t *\n\t * @param array $credential_sets (Required) The named credential sets that should be made available to the application.\n\t * @return void\n\t */\n\tpublic static function set(array $credential_sets)\n\t{\n\t\t// Make sure a default credential set is specified or can be inferred\n\t\tif (count($credential_sets) === 1)\n\t\t{\n\t\t\t$credential_sets[self::DEFAULT_KEY] = reset($credential_sets);\n\t\t}\n\n\t\t// Resolve any @inherit tags\n\t\tforeach ($credential_sets as $credential_name => &$credential_set)\n\t\t{\n\t\t\tif (is_array($credential_set))\n\t\t\t{\n\t\t\t\tforeach ($credential_set as $credential_key => &$credential_value)\n\t\t\t\t{\n\t\t\t\t\tif ($credential_key === self::INHERIT_KEY)\n\t\t\t\t\t{\n\t\t\t\t\t\tif (!isset($credential_sets[$credential_value]))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthrow new CFCredentials_Exception('The credential set, \"' . $credential_value . '\", does not exist and cannot be inherited.');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t$credential_set = array_merge($credential_sets[$credential_value], $credential_set);\n\t\t\t\t\t\tunset($credential_set[self::INHERIT_KEY]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Normalize the value of the @default credential set\n\t\tif (isset($credential_sets[self::DEFAULT_KEY]))\n\t\t{\n\t\t\t$default = $credential_sets[self::DEFAULT_KEY];\n\t\t\tif (is_string($default))\n\t\t\t{\n\t\t\t\tif (!isset($credential_sets[$default]))\n\t\t\t\t{\n\t\t\t\t\tthrow new CFCredentials_Exception('The credential set, \"' . $default . '\", does not exist and cannot be used as the default credential set.');\n\t\t\t\t}\n\n\t\t\t\t$credential_sets[self::DEFAULT_KEY] = $credential_sets[$default];\n\t\t\t}\n\t\t}\n\n\t\t// Store the credentials\n\t\tself::$credentials = $credential_sets;\n\t}\n\n\t/**\n\t * Retrieves the requested credentials from the internal credential store.\n\t *\n\t * @param string $credential_set (Optional) The name of the credential set to retrieve. The default value is set in DEFAULT_KEY.\n\t * @return stdClass A stdClass object where the properties represent the keys that were provided.\n\t */\n\tpublic static function get($credential_name = self::DEFAULT_KEY)\n\t{\n\t\t// Make sure the credential set exists\n\t\tif (!isset(self::$credentials[$credential_name]))\n\t\t{\n\t\t\tthrow new CFCredentials_Exception('The credential set, \"' . $credential_name . '\", does not exist and cannot be retrieved.');\n\t\t}\n\n\t\t// Return the credential set as an object\n\t\treturn new CFCredential(self::$credentials[$credential_name]);\n\t}\n\n\t/**\n\t * Retrieves a list of all available credential set names.\n\t *\n\t * @return CFArray A list of all available credential set names.\n\t */\n\tpublic static function list_sets()\n\t{\n\t\treturn new CFArray(array_keys(self::$credentials));\n\t}\n}\n\nclass CFCredentials_Exception extends Exception {}\n"
  },
  {
    "path": "utilities/gzipdecode.class.php",
    "content": "<?php\n/*\n * Copyright 2011-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\").\n * You may not use this file except in compliance with the License.\n * A copy of the License is located at\n *\n *  http://aws.amazon.com/apache2.0\n *\n * or in the \"license\" file accompanying this file. This file is distributed\n * on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either\n * express or implied. See the License for the specific language governing\n * permissions and limitations under the License.\n */\n\n/**\n * SimplePie\n *\n * A PHP-Based RSS and Atom Feed Framework.\n * Takes the hard work out of managing a complete RSS/Atom solution.\n *\n * Copyright (c) 2004-2010, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without modification, are\n * permitted provided that the following conditions are met:\n *\n * \t* Redistributions of source code must retain the above copyright notice, this list of\n * \t  conditions and the following disclaimer.\n *\n * \t* Redistributions in binary form must reproduce the above copyright notice, this list\n * \t  of conditions and the following disclaimer in the documentation and/or other materials\n * \t  provided with the distribution.\n *\n * \t* Neither the name of the SimplePie Team nor the names of its contributors may be used\n * \t  to endorse or promote products derived from this software without specific prior\n * \t  written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS\n * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY\n * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS\n * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\n * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n *\n * @package SimplePie\n * @version 1.3-dev\n * @copyright 2004-2010 Ryan Parman, Geoffrey Sneddon, Ryan McCue\n * @author Ryan Parman\n * @author Geoffrey Sneddon\n * @author Ryan McCue\n * @link http://simplepie.org/ SimplePie\n * @license http://www.opensource.org/licenses/bsd-license.php BSD License\n * @todo phpDoc comments\n */\n\n\n/*%******************************************************************************************%*/\n// CLASS\n\n/**\n * Handles a variety of primary and edge cases around gzip/deflate decoding in PHP.\n *\n * @version 2011.02.21\n * @license See the included NOTICE.md file for more information.\n * @copyright See the included NOTICE.md file for more information.\n * @link http://aws.amazon.com/php/ PHP Developer Center\n * @link https://github.com/simplepie/simplepie/blob/master/SimplePie/gzdecode.php SimplePie_gzdecode\n */\nclass CFGzipDecode\n{\n\t/**\n\t * Compressed data\n\t *\n\t * @access private\n\t * @see gzdecode::$data\n\t */\n\tpublic $compressed_data;\n\n\t/**\n\t * Size of compressed data\n\t *\n\t * @access private\n\t */\n\tpublic $compressed_size;\n\n\t/**\n\t * Minimum size of a valid gzip string\n\t *\n\t * @access private\n\t */\n\tpublic $min_compressed_size = 18;\n\n\t/**\n\t * Current position of pointer\n\t *\n\t * @access private\n\t */\n\tpublic $position = 0;\n\n\t/**\n\t * Flags (FLG)\n\t *\n\t * @access private\n\t */\n\tpublic $flags;\n\n\t/**\n\t * Uncompressed data\n\t *\n\t * @access public\n\t * @see gzdecode::$compressed_data\n\t */\n\tpublic $data;\n\n\t/**\n\t * Modified time\n\t *\n\t * @access public\n\t */\n\tpublic $MTIME;\n\n\t/**\n\t * Extra Flags\n\t *\n\t * @access public\n\t */\n\tpublic $XFL;\n\n\t/**\n\t * Operating System\n\t *\n\t * @access public\n\t */\n\tpublic $OS;\n\n\t/**\n\t * Subfield ID 1\n\t *\n\t * @access public\n\t * @see gzdecode::$extra_field\n\t * @see gzdecode::$SI2\n\t */\n\tpublic $SI1;\n\n\t/**\n\t * Subfield ID 2\n\t *\n\t * @access public\n\t * @see gzdecode::$extra_field\n\t * @see gzdecode::$SI1\n\t */\n\tpublic $SI2;\n\n\t/**\n\t * Extra field content\n\t *\n\t * @access public\n\t * @see gzdecode::$SI1\n\t * @see gzdecode::$SI2\n\t */\n\tpublic $extra_field;\n\n\t/**\n\t * Original filename\n\t *\n\t * @access public\n\t */\n\tpublic $filename;\n\n\t/**\n\t * Human readable comment\n\t *\n\t * @access public\n\t */\n\tpublic $comment;\n\n\t/**\n\t * Don't allow anything to be set\n\t *\n\t * @access public\n\t */\n\tpublic function __set($name, $value)\n\t{\n\t\ttrigger_error(\"Cannot write property $name\", E_USER_ERROR);\n\t}\n\n\t/**\n\t * Set the compressed string and related properties\n\t *\n\t * @access public\n\t */\n\tpublic function __construct($data)\n\t{\n\t\t$this->compressed_data = $data;\n\t\t$this->compressed_size = strlen($data);\n\t}\n\n\t/**\n\t * Decode the GZIP stream\n\t *\n\t * @access public\n\t */\n\tpublic function parse()\n\t{\n\t\tif ($this->compressed_size >= $this->min_compressed_size)\n\t\t{\n\t\t\t// Check ID1, ID2, and CM\n\t\t\tif (substr($this->compressed_data, 0, 3) !== \"\\x1F\\x8B\\x08\")\n\t\t\t{\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// Get the FLG (FLaGs)\n\t\t\t$this->flags = ord($this->compressed_data[3]);\n\n\t\t\t// FLG bits above (1 << 4) are reserved\n\t\t\tif ($this->flags > 0x1F)\n\t\t\t{\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// Advance the pointer after the above\n\t\t\t$this->position += 4;\n\n\t\t\t// MTIME\n\t\t\t$mtime = substr($this->compressed_data, $this->position, 4);\n\t\t\t// Reverse the string if we're on a big-endian arch because l is the only signed long and is machine endianness\n\t\t\tif (current(unpack('S', \"\\x00\\x01\")) === 1)\n\t\t\t{\n\t\t\t\t$mtime = strrev($mtime);\n\t\t\t}\n\t\t\t$this->MTIME = current(unpack('l', $mtime));\n\t\t\t$this->position += 4;\n\n\t\t\t// Get the XFL (eXtra FLags)\n\t\t\t$this->XFL = ord($this->compressed_data[$this->position++]);\n\n\t\t\t// Get the OS (Operating System)\n\t\t\t$this->OS = ord($this->compressed_data[$this->position++]);\n\n\t\t\t// Parse the FEXTRA\n\t\t\tif ($this->flags & 4)\n\t\t\t{\n\t\t\t\t// Read subfield IDs\n\t\t\t\t$this->SI1 = $this->compressed_data[$this->position++];\n\t\t\t\t$this->SI2 = $this->compressed_data[$this->position++];\n\n\t\t\t\t// SI2 set to zero is reserved for future use\n\t\t\t\tif ($this->SI2 === \"\\x00\")\n\t\t\t\t{\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\t// Get the length of the extra field\n\t\t\t\t$len = current(unpack('v', substr($this->compressed_data, $this->position, 2)));\n\t\t\t\t$position += 2;\n\n\t\t\t\t// Check the length of the string is still valid\n\t\t\t\t$this->min_compressed_size += $len + 4;\n\t\t\t\tif ($this->compressed_size >= $this->min_compressed_size)\n\t\t\t\t{\n\t\t\t\t\t// Set the extra field to the given data\n\t\t\t\t\t$this->extra_field = substr($this->compressed_data, $this->position, $len);\n\t\t\t\t\t$this->position += $len;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Parse the FNAME\n\t\t\tif ($this->flags & 8)\n\t\t\t{\n\t\t\t\t// Get the length of the filename\n\t\t\t\t$len = strcspn($this->compressed_data, \"\\x00\", $this->position);\n\n\t\t\t\t// Check the length of the string is still valid\n\t\t\t\t$this->min_compressed_size += $len + 1;\n\t\t\t\tif ($this->compressed_size >= $this->min_compressed_size)\n\t\t\t\t{\n\t\t\t\t\t// Set the original filename to the given string\n\t\t\t\t\t$this->filename = substr($this->compressed_data, $this->position, $len);\n\t\t\t\t\t$this->position += $len + 1;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Parse the FCOMMENT\n\t\t\tif ($this->flags & 16)\n\t\t\t{\n\t\t\t\t// Get the length of the comment\n\t\t\t\t$len = strcspn($this->compressed_data, \"\\x00\", $this->position);\n\n\t\t\t\t// Check the length of the string is still valid\n\t\t\t\t$this->min_compressed_size += $len + 1;\n\t\t\t\tif ($this->compressed_size >= $this->min_compressed_size)\n\t\t\t\t{\n\t\t\t\t\t// Set the original comment to the given string\n\t\t\t\t\t$this->comment = substr($this->compressed_data, $this->position, $len);\n\t\t\t\t\t$this->position += $len + 1;\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Parse the FHCRC\n\t\t\tif ($this->flags & 2)\n\t\t\t{\n\t\t\t\t// Check the length of the string is still valid\n\t\t\t\t$this->min_compressed_size += $len + 2;\n\t\t\t\tif ($this->compressed_size >= $this->min_compressed_size)\n\t\t\t\t{\n\t\t\t\t\t// Read the CRC\n\t\t\t\t\t$crc = current(unpack('v', substr($this->compressed_data, $this->position, 2)));\n\n\t\t\t\t\t// Check the CRC matches\n\t\t\t\t\tif ((crc32(substr($this->compressed_data, 0, $this->position)) & 0xFFFF) === $crc)\n\t\t\t\t\t{\n\t\t\t\t\t\t$this->position += 2;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Decompress the actual data\n\t\t\tif (($this->data = gzinflate(substr($this->compressed_data, $this->position, -8))) === false)\n\t\t\t{\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$this->position = $this->compressed_size - 8;\n\t\t\t}\n\n\t\t\t// Check CRC of data\n\t\t\t$crc = current(unpack('V', substr($this->compressed_data, $this->position, 4)));\n\t\t\t$this->position += 4;\n\t\t\t/*if (extension_loaded('hash') && sprintf('%u', current(unpack('V', hash('crc32b', $this->data)))) !== sprintf('%u', $crc))\n\t\t\t{\n\t\t\t\treturn false;\n\t\t\t}*/\n\n\t\t\t// Check ISIZE of data\n\t\t\t$isize = current(unpack('V', substr($this->compressed_data, $this->position, 4)));\n\t\t\t$this->position += 4;\n\t\t\tif (sprintf('%u', strlen($this->data) & 0xFFFFFFFF) !== sprintf('%u', $isize))\n\t\t\t{\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// Wow, against all odds, we've actually got a valid gzip string\n\t\t\treturn true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\treturn false;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "utilities/info.class.php",
    "content": "<?php\n/*\n * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\").\n * You may not use this file except in compliance with the License.\n * A copy of the License is located at\n *\n *  http://aws.amazon.com/apache2.0\n *\n * or in the \"license\" file accompanying this file. This file is distributed\n * on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either\n * express or implied. See the License for the specific language governing\n * permissions and limitations under the License.\n */\n\n\n/*%******************************************************************************************%*/\n// CLASS\n\n/**\n * Contains a series of methods that provide information about the SDK.\n *\n * @version 2010.10.01\n * @license See the included NOTICE.md file for more information.\n * @copyright See the included NOTICE.md file for more information.\n * @link http://aws.amazon.com/php/ PHP Developer Center\n */\nclass CFInfo\n{\n\t/**\n\t * Gets information about the web service APIs that the SDK supports.\n\t *\n\t * @return array An associative array containing service classes and API versions.\n\t */\n\tpublic static function api_support()\n\t{\n\t\t$existing_classes = get_declared_classes();\n\n\t\tforeach (glob(dirname(dirname(__FILE__)) . '/services/*.class.php') as $file)\n\t\t{\n\t\t\tinclude $file;\n\t\t}\n\n\t\t$with_sdk_classes = get_declared_classes();\n\t\t$new_classes = array_diff($with_sdk_classes, $existing_classes);\n\t\t$filtered_classes = array();\n\t\t$collect = array();\n\n\t\tforeach ($new_classes as $class)\n\t\t{\n\t\t\tif (strpos($class, 'Amazon') !== false)\n\t\t\t{\n\t\t\t\t$filtered_classes[] = $class;\n\t\t\t}\n\t\t}\n\n\t\t$filtered_classes = array_values($filtered_classes);\n\n\t\tforeach ($filtered_classes as $class)\n\t\t{\n\t\t\t$obj = new $class();\n\t\t\t$collect[get_class($obj)] = $obj->api_version;\n\t\t\tunset($obj);\n\t\t}\n\n\t\treturn $collect;\n\t}\n}\n"
  },
  {
    "path": "utilities/json.class.php",
    "content": "<?php\n/*\n * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\").\n * You may not use this file except in compliance with the License.\n * A copy of the License is located at\n *\n *  http://aws.amazon.com/apache2.0\n *\n * or in the \"license\" file accompanying this file. This file is distributed\n * on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either\n * express or implied. See the License for the specific language governing\n * permissions and limitations under the License.\n */\n\n\n/*%******************************************************************************************%*/\n// CLASS\n\n/**\n * Handles the conversion of data from JSON to other formats.\n *\n * @version 2012.07.27\n * @license See the included NOTICE.md file for more information.\n * @copyright See the included NOTICE.md file for more information.\n * @link http://aws.amazon.com/php/ PHP Developer Center\n */\nclass CFJSON\n{\n\t/**\n\t * Converts a JSON string to a CFSimpleXML object.\n\t *\n\t * @param string|array $json (Required) Pass either a valid JSON-formatted string, or an associative array.\n\t * @param string $parser (Optional) The name of the class to use to parse the XML. This class should extend <code>SimpleXMLElement</code>. Has a default value of <code>CFSimpleXML</code>.\n\t * @return CFSimpleXML An XML representation of the data.\n\t */\n\tpublic static function to_xml($json, $parser = 'CFSimpleXML')\n\t{\n\t\t// If we haven't parsed the JSON, do it\n\t\tif (!is_array($json))\n\t\t{\n\t\t\t// Handle the case of JSON-encoded NULL value\n\t\t\tif ($json === 'null')\n\t\t\t{\n\t\t\t\t$json = null;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$json = json_decode($json, true);\n\n\t\t\t\tif (function_exists('json_last_error'))\n\t\t\t\t{\n\t\t\t\t\t// Did we encounter an error?\n\t\t\t\t\tswitch (json_last_error())\n\t\t\t\t\t{\n\t\t\t\t\t\tcase JSON_ERROR_DEPTH:\n\t\t\t\t\t\t\tthrow new JSON_Exception('Maximum stack depth exceeded.');\n\n\t\t\t\t\t\tcase JSON_ERROR_CTRL_CHAR:\n\t\t\t\t\t\t\tthrow new JSON_Exception('Unexpected control character found.');\n\n\t\t\t\t\t\tcase JSON_ERROR_SYNTAX:\n\t\t\t\t\t\t\tthrow new JSON_Exception('Syntax error; Malformed JSON.');\n\n\t\t\t\t\t\tcase JSON_ERROR_STATE_MISMATCH:\n\t\t\t\t\t\t\tthrow new JSON_Exception('Invalid or malformed JSON.');\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// json_last_error() not available?\n\t\t\t\telseif ($json === null)\n\t\t\t\t{\n\t\t\t\t\tthrow new JSON_Exception('Unknown JSON error. Be sure to validate your JSON and read the notes on http://php.net/json_decode.');\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Hand off for the recursive work\n\t\t$string = Transmogrifier::to_xml($json, 'rootElement');\n\n\t\treturn simplexml_load_string($string, $parser, LIBXML_NOCDATA);\n\t}\n}\n\n\n/**\n * Default JSON Exception.\n */\nclass JSON_Exception extends Exception {}\n"
  },
  {
    "path": "utilities/manifest.class.php",
    "content": "<?php\n/*\n * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\").\n * You may not use this file except in compliance with the License.\n * A copy of the License is located at\n *\n *  http://aws.amazon.com/apache2.0\n *\n * or in the \"license\" file accompanying this file. This file is distributed\n * on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either\n * express or implied. See the License for the specific language governing\n * permissions and limitations under the License.\n */\n\n\n/*%******************************************************************************************%*/\n// CLASS\n\n/**\n * Simplifies the process of generating manifests for the AWS Import/Export service.\n *\n * @version 2010.11.22\n * @license See the included NOTICE.md file for more information.\n * @copyright See the included NOTICE.md file for more information.\n * @link http://aws.amazon.com/php/ PHP Developer Center\n */\nclass CFManifest\n{\n\n\t/**\n\t * Takes a JSON object as a string to convert to a YAML manifest.\n\t *\n\t * @param string $json (Required) A JSON object. The JSON string should use canonical rules (e.g., double quotes, quoted keys) as is required by PHP's <php:json_encode()> function.\n\t * @return string A YAML manifest document.\n\t */\n\tpublic static function json($json)\n\t{\n\t\t$map = json_decode($json, true);\n\t\treturn sfYaml::dump($map);\n\t}\n\n\t/**\n\t * Takes an associative array to convert to a YAML manifest.\n\t *\n\t * @param array $map (Required) An associative array.\n\t * @return string A YAML manifest document.\n\t */\n\tpublic static function map($map)\n\t{\n\t\treturn sfYaml::dump($map);\n\t}\n}\n"
  },
  {
    "path": "utilities/mimetypes.class.php",
    "content": "<?php\n/*\n * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\").\n * You may not use this file except in compliance with the License.\n * A copy of the License is located at\n *\n *  http://aws.amazon.com/apache2.0\n *\n * or in the \"license\" file accompanying this file. This file is distributed\n * on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either\n * express or implied. See the License for the specific language governing\n * permissions and limitations under the License.\n */\n\n\n/*%******************************************************************************************%*/\n// CLASS\n\n/**\n * Simplifies the process of looking up the content-types for a variety of file extensions.\n *\n * @version 2010.07.20\n * @license See the included NOTICE.md file for more information.\n * @copyright See the included NOTICE.md file for more information.\n * @link http://aws.amazon.com/php/ PHP Developer Center\n */\nclass CFMimeTypes\n{\n\t/**\n\t * Map of the extension-to-mime-types that we support.\n\t */\n\tpublic static $mime_types = array(\n\t\t'3gp' => 'video/3gpp',\n\t\t'ai' => 'application/postscript',\n\t\t'aif' => 'audio/x-aiff',\n\t\t'aifc' => 'audio/x-aiff',\n\t\t'aiff' => 'audio/x-aiff',\n\t\t'asc' => 'text/plain',\n\t\t'atom' => 'application/atom+xml',\n\t\t'au' => 'audio/basic',\n\t\t'avi' => 'video/x-msvideo',\n\t\t'bcpio' => 'application/x-bcpio',\n\t\t'bin' => 'application/octet-stream',\n\t\t'bmp' => 'image/bmp',\n\t\t'cdf' => 'application/x-netcdf',\n\t\t'cgm' => 'image/cgm',\n\t\t'class' => 'application/octet-stream',\n\t\t'cpio' => 'application/x-cpio',\n\t\t'cpt' => 'application/mac-compactpro',\n\t\t'csh' => 'application/x-csh',\n\t\t'css' => 'text/css',\n\t\t'dcr' => 'application/x-director',\n\t\t'dif' => 'video/x-dv',\n\t\t'dir' => 'application/x-director',\n\t\t'djv' => 'image/vnd.djvu',\n\t\t'djvu' => 'image/vnd.djvu',\n\t\t'dll' => 'application/octet-stream',\n\t\t'dmg' => 'application/octet-stream',\n\t\t'dms' => 'application/octet-stream',\n\t\t'doc' => 'application/msword',\n\t\t'dtd' => 'application/xml-dtd',\n\t\t'dv' => 'video/x-dv',\n\t\t'dvi' => 'application/x-dvi',\n\t\t'dxr' => 'application/x-director',\n\t\t'eps' => 'application/postscript',\n\t\t'etx' => 'text/x-setext',\n\t\t'exe' => 'application/octet-stream',\n\t\t'ez' => 'application/andrew-inset',\n\t\t'flv' => 'video/x-flv',\n\t\t'gif' => 'image/gif',\n\t\t'gram' => 'application/srgs',\n\t\t'grxml' => 'application/srgs+xml',\n\t\t'gtar' => 'application/x-gtar',\n\t\t'gz' => 'application/x-gzip',\n\t\t'hdf' => 'application/x-hdf',\n\t\t'hqx' => 'application/mac-binhex40',\n\t\t'htm' => 'text/html',\n\t\t'html' => 'text/html',\n\t\t'ice' => 'x-conference/x-cooltalk',\n\t\t'ico' => 'image/x-icon',\n\t\t'ics' => 'text/calendar',\n\t\t'ief' => 'image/ief',\n\t\t'ifb' => 'text/calendar',\n\t\t'iges' => 'model/iges',\n\t\t'igs' => 'model/iges',\n\t\t'jnlp' => 'application/x-java-jnlp-file',\n\t\t'jp2' => 'image/jp2',\n\t\t'jpe' => 'image/jpeg',\n\t\t'jpeg' => 'image/jpeg',\n\t\t'jpg' => 'image/jpeg',\n\t\t'js' => 'application/x-javascript',\n\t\t'kar' => 'audio/midi',\n\t\t'latex' => 'application/x-latex',\n\t\t'lha' => 'application/octet-stream',\n\t\t'lzh' => 'application/octet-stream',\n\t\t'm3u' => 'audio/x-mpegurl',\n\t\t'm4a' => 'audio/mp4a-latm',\n\t\t'm4p' => 'audio/mp4a-latm',\n\t\t'm4u' => 'video/vnd.mpegurl',\n\t\t'm4v' => 'video/x-m4v',\n\t\t'mac' => 'image/x-macpaint',\n\t\t'man' => 'application/x-troff-man',\n\t\t'mathml' => 'application/mathml+xml',\n\t\t'me' => 'application/x-troff-me',\n\t\t'mesh' => 'model/mesh',\n\t\t'mid' => 'audio/midi',\n\t\t'midi' => 'audio/midi',\n\t\t'mif' => 'application/vnd.mif',\n\t\t'mov' => 'video/quicktime',\n\t\t'movie' => 'video/x-sgi-movie',\n\t\t'mp2' => 'audio/mpeg',\n\t\t'mp3' => 'audio/mpeg',\n\t\t'mp4' => 'video/mp4',\n\t\t'mpe' => 'video/mpeg',\n\t\t'mpeg' => 'video/mpeg',\n\t\t'mpg' => 'video/mpeg',\n\t\t'mpga' => 'audio/mpeg',\n\t\t'ms' => 'application/x-troff-ms',\n\t\t'msh' => 'model/mesh',\n\t\t'mxu' => 'video/vnd.mpegurl',\n\t\t'nc' => 'application/x-netcdf',\n\t\t'oda' => 'application/oda',\n\t\t'ogg' => 'application/ogg',\n\t\t'ogv' => 'video/ogv',\n\t\t'pbm' => 'image/x-portable-bitmap',\n\t\t'pct' => 'image/pict',\n\t\t'pdb' => 'chemical/x-pdb',\n\t\t'pdf' => 'application/pdf',\n\t\t'pgm' => 'image/x-portable-graymap',\n\t\t'pgn' => 'application/x-chess-pgn',\n\t\t'pic' => 'image/pict',\n\t\t'pict' => 'image/pict',\n\t\t'png' => 'image/png',\n\t\t'pnm' => 'image/x-portable-anymap',\n\t\t'pnt' => 'image/x-macpaint',\n\t\t'pntg' => 'image/x-macpaint',\n\t\t'ppm' => 'image/x-portable-pixmap',\n\t\t'ppt' => 'application/vnd.ms-powerpoint',\n\t\t'ps' => 'application/postscript',\n\t\t'qt' => 'video/quicktime',\n\t\t'qti' => 'image/x-quicktime',\n\t\t'qtif' => 'image/x-quicktime',\n\t\t'ra' => 'audio/x-pn-realaudio',\n\t\t'ram' => 'audio/x-pn-realaudio',\n\t\t'ras' => 'image/x-cmu-raster',\n\t\t'rdf' => 'application/rdf+xml',\n\t\t'rgb' => 'image/x-rgb',\n\t\t'rm' => 'application/vnd.rn-realmedia',\n\t\t'roff' => 'application/x-troff',\n\t\t'rtf' => 'text/rtf',\n\t\t'rtx' => 'text/richtext',\n\t\t'sgm' => 'text/sgml',\n\t\t'sgml' => 'text/sgml',\n\t\t'sh' => 'application/x-sh',\n\t\t'shar' => 'application/x-shar',\n\t\t'silo' => 'model/mesh',\n\t\t'sit' => 'application/x-stuffit',\n\t\t'skd' => 'application/x-koan',\n\t\t'skm' => 'application/x-koan',\n\t\t'skp' => 'application/x-koan',\n\t\t'skt' => 'application/x-koan',\n\t\t'smi' => 'application/smil',\n\t\t'smil' => 'application/smil',\n\t\t'snd' => 'audio/basic',\n\t\t'so' => 'application/octet-stream',\n\t\t'spl' => 'application/x-futuresplash',\n\t\t'src' => 'application/x-wais-source',\n\t\t'sv4cpio' => 'application/x-sv4cpio',\n\t\t'sv4crc' => 'application/x-sv4crc',\n\t\t'svg' => 'image/svg+xml',\n\t\t'swf' => 'application/x-shockwave-flash',\n\t\t't' => 'application/x-troff',\n\t\t'tar' => 'application/x-tar',\n\t\t'tcl' => 'application/x-tcl',\n\t\t'tex' => 'application/x-tex',\n\t\t'texi' => 'application/x-texinfo',\n\t\t'texinfo' => 'application/x-texinfo',\n\t\t'tif' => 'image/tiff',\n\t\t'tiff' => 'image/tiff',\n\t\t'tr' => 'application/x-troff',\n\t\t'tsv' => 'text/tab-separated-values',\n\t\t'txt' => 'text/plain',\n\t\t'ustar' => 'application/x-ustar',\n\t\t'vcd' => 'application/x-cdlink',\n\t\t'vrml' => 'model/vrml',\n\t\t'vxml' => 'application/voicexml+xml',\n\t\t'wav' => 'audio/x-wav',\n\t\t'wbmp' => 'image/vnd.wap.wbmp',\n\t\t'wbxml' => 'application/vnd.wap.wbxml',\n\t\t'webm' => 'video/webm',\n\t\t'wml' => 'text/vnd.wap.wml',\n\t\t'wmlc' => 'application/vnd.wap.wmlc',\n\t\t'wmls' => 'text/vnd.wap.wmlscript',\n\t\t'wmlsc' => 'application/vnd.wap.wmlscriptc',\n\t\t'wmv' => 'video/x-ms-wmv',\n\t\t'wrl' => 'model/vrml',\n\t\t'xbm' => 'image/x-xbitmap',\n\t\t'xht' => 'application/xhtml+xml',\n\t\t'xhtml' => 'application/xhtml+xml',\n\t\t'xls' => 'application/vnd.ms-excel',\n\t\t'xml' => 'application/xml',\n\t\t'xpm' => 'image/x-xpixmap',\n\t\t'xsl' => 'application/xml',\n\t\t'xslt' => 'application/xslt+xml',\n\t\t'xul' => 'application/vnd.mozilla.xul+xml',\n\t\t'xwd' => 'image/x-xwindowdump',\n\t\t'xyz' => 'chemical/x-xyz',\n\t\t'zip' => 'application/zip',\n\t);\n\n\t/**\n\t * Attempt to match the file extension to a known mime-type.\n\t *\n\t * @param string $ext (Required) The file extension to attempt to map.\n\t * @return string The mime-type to use for the file extension.\n\t */\n\tpublic static function get_mimetype($ext)\n\t{\n\t\t$ext = strtolower($ext);  // Make sure the passed in extension is lowercase\n\t\treturn isset(self::$mime_types[$ext]) ? self::$mime_types[$ext] : 'application/octet-stream';\n\t}\n}\n"
  },
  {
    "path": "utilities/policy.class.php",
    "content": "<?php\n/*\n * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\").\n * You may not use this file except in compliance with the License.\n * A copy of the License is located at\n *\n *  http://aws.amazon.com/apache2.0\n *\n * or in the \"license\" file accompanying this file. This file is distributed\n * on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either\n * express or implied. See the License for the specific language governing\n * permissions and limitations under the License.\n */\n\n\n/*%******************************************************************************************%*/\n// CLASS\n\n/**\n * Simplifies the process of signing JSON policy documents.\n *\n * @version 2011.04.25\n * @license See the included NOTICE.md file for more information.\n * @copyright See the included NOTICE.md file for more information.\n * @link http://aws.amazon.com/php/ PHP Developer Center\n */\nclass CFPolicy\n{\n\t/**\n\t * Stores the object that contains the authentication credentials.\n\t */\n\tpublic $auth;\n\n\t/**\n\t * Stores the policy object that we're working with.\n\t */\n\tpublic $json_policy;\n\n\t/**\n\t * Constructs a new instance of this class.\n\t *\n\t * @param CFRuntime $auth (Required) An instance of any authenticated AWS object that is an instance of <CFRuntime> (e.g. <AmazonEC2>, <AmazonS3>).\n\t * @param string|array $policy (Required) The associative array representing the S3 policy to use, or a string of JSON content.\n\t * @return $this A reference to the current instance.\n\t * @link http://docs.amazonwebservices.com/AmazonS3/2006-03-01/dev/index.html?HTTPPOSTForms.html S3 Policies\n\t * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/index.html?AccessPolicyLanguage.html Access Policy Language\n\t */\n\tpublic function __construct($auth, $policy)\n\t{\n\t\t$this->auth = $auth;\n\n\t\tif (is_array($policy)) // We received an associative array...\n\t\t{\n\t\t\t$this->json_policy = json_encode($policy);\n\t\t}\n\t\telse // We received a valid, parseable JSON string...\n\t\t{\n\t\t\t$this->json_policy = json_encode(json_decode($policy, true));\n\t\t}\n\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Alternate approach to constructing a new instance. Supports chaining.\n\t *\n\t * @param CFRuntime $auth (Required) An instance of any authenticated AWS object that is an instance of <CFRuntime> (e.g. <AmazonEC2>, <AmazonS3>).\n\t * @param string|array $policy (Required) The associative array representing the S3 policy to use, or a string of JSON content.\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic static function init($auth, $policy)\n\t{\n\t\tif (version_compare(PHP_VERSION, '5.3.0', '<'))\n\t\t{\n\t\t\tthrow new Exception('PHP 5.3 or newer is required to instantiate a new class with CLASS::init().');\n\t\t}\n\n\t\t$self = get_called_class();\n\t\treturn new $self($auth, $policy);\n\t}\n\n\t/**\n\t * Get the key from the authenticated instance.\n\t *\n\t * @return string The key from the authenticated instance.\n\t */\n\tpublic function get_key()\n\t{\n\t\treturn $this->auth->key;\n\t}\n\n\t/**\n\t * Base64-encodes the JSON string.\n\t *\n\t * @return string The Base64-encoded version of the JSON string.\n\t */\n\tpublic function get_policy()\n\t{\n\t\treturn base64_encode($this->json_policy);\n\t}\n\n\t/**\n\t * Gets the JSON string with the whitespace removed.\n\t *\n\t * @return string The JSON string without extraneous whitespace.\n\t */\n\tpublic function get_json()\n\t{\n\t\treturn $this->json_policy;\n\t}\n\n\t/**\n\t * Gets the JSON string with the whitespace removed.\n\t *\n\t * @return string The Base64-encoded, signed JSON string.\n\t */\n\tpublic function get_policy_signature()\n\t{\n\t\treturn base64_encode(hash_hmac('sha1', $this->get_policy(), $this->auth->secret_key));\n\t}\n\n\t/**\n\t * Decode a policy that was returned from the service.\n\t *\n\t * @param string $response (Required) The policy returned by AWS that you want to decode into an object.\n\t * @return string The Base64-encoded, signed JSON string.\n\t */\n\tpublic static function decode_policy($response)\n\t{\n\t\treturn json_decode(urldecode($response), true);\n\t}\n}\n"
  },
  {
    "path": "utilities/request.class.php",
    "content": "<?php\n/*\n * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\").\n * You may not use this file except in compliance with the License.\n * A copy of the License is located at\n *\n *  http://aws.amazon.com/apache2.0\n *\n * or in the \"license\" file accompanying this file. This file is distributed\n * on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either\n * express or implied. See the License for the specific language governing\n * permissions and limitations under the License.\n */\n\n\n/*%******************************************************************************************%*/\n// CLASS\n\n/**\n * Wraps the underlying `RequestCore` class with some AWS-specific customizations.\n *\n * @version 2011.12.02\n * @license See the included NOTICE.md file for more information.\n * @copyright See the included NOTICE.md file for more information.\n * @link http://aws.amazon.com/php/ PHP Developer Center\n */\nclass CFRequest extends RequestCore\n{\n\t/**\n\t * The default class to use for HTTP Requests (defaults to <CFRequest>).\n\t */\n\tpublic $request_class = 'CFRequest';\n\n\t/**\n\t * The default class to use for HTTP Responses (defaults to <CFResponse>).\n\t */\n\tpublic $response_class = 'CFResponse';\n\n\t/**\n\t * The active credential set.\n\t */\n\tpublic $credentials;\n\n\n\t/*%******************************************************************************************%*/\n\t// CONSTRUCTOR\n\n\t/**\n\t * Constructs a new instance of this class.\n\t *\n\t * @param string $url (Optional) The URL to request or service endpoint to query.\n\t * @param string $proxy (Optional) The faux-url to use for proxy settings. Takes the following format: `proxy://user:pass@hostname:port`\n\t * @param array $helpers (Optional) An associative array of classnames to use for request, and response functionality. Gets passed in automatically by the calling class.\n\t * @param CFCredential $credentials (Required) The credentials to use for signing and making requests.\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function __construct($url = null, $proxy = null, $helpers = null, CFCredential $credentials = null)\n\t{\n\t\tparent::__construct($url, $proxy, $helpers);\n\n\t\t// Standard settings for all requests\n\t\t$this->set_useragent(CFRUNTIME_USERAGENT);\n\t\t$this->credentials = $credentials;\n\t\t$this->cacert_location = ($this->credentials['certificate_authority'] ? $this->credentials['certificate_authority'] : false);\n\n\t\tif (strpos(parse_url($url, PHP_URL_HOST), 'dynamodb') === 0)\n\t\t\t{\n\t\t\t\t$this->use_gzip_enconding = false;\n\t\t\t}\n\n\t\treturn $this;\n\t}\n}\n"
  },
  {
    "path": "utilities/response.class.php",
    "content": "<?php\n/*\n * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\").\n * You may not use this file except in compliance with the License.\n * A copy of the License is located at\n *\n *  http://aws.amazon.com/apache2.0\n *\n * or in the \"license\" file accompanying this file. This file is distributed\n * on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either\n * express or implied. See the License for the specific language governing\n * permissions and limitations under the License.\n */\n\n\n/*%******************************************************************************************%*/\n// CLASS\n\n/**\n * Wraps the underlying `ResponseCore` class with some AWS-specific customizations. Response\n * bodies are typically represented as CFSimpleXML objects.\n *\n * @version 2010.10.11\n * @license See the included NOTICE.md file for more information.\n * @copyright See the included NOTICE.md file for more information.\n * @link http://aws.amazon.com/php/ PHP Developer Center\n */\nclass CFResponse extends ResponseCore {}\n"
  },
  {
    "path": "utilities/simplexml.class.php",
    "content": "<?php\n/*\n * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\").\n * You may not use this file except in compliance with the License.\n * A copy of the License is located at\n *\n *  http://aws.amazon.com/apache2.0\n *\n * or in the \"license\" file accompanying this file. This file is distributed\n * on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either\n * express or implied. See the License for the specific language governing\n * permissions and limitations under the License.\n */\n\n\n/*%******************************************************************************************%*/\n// CLASS\n\n/**\n * Wraps the underlying `SimpleXMLIterator` class with enhancements for rapidly traversing the\n * DOM tree, converting types, and comparisons. You will need to be familiar with traversing\n * objects with the PHP SimpleXML extension in order to use this class effectively. Also,\n * CFResponse bodies are typically represented as CFSimpleXML objects.\n *\n * @version 2012.05.31\n * @license See the included NOTICE.md file for more information.\n * @copyright See the included NOTICE.md file for more information.\n * @link http://aws.amazon.com/php/ PHP Developer Center\n * @link http://php.net/SimpleXML SimpleXML\n */\nclass CFSimpleXML extends SimpleXMLIterator\n{\n\t/**\n\t * Stores the namespace name to use in XPath queries.\n\t */\n\tpublic $xml_ns;\n\n\t/**\n\t * Stores the namespace URI to use in XPath queries.\n\t */\n\tpublic $xml_ns_url;\n\n\t/**\n\t * Stores whether or not the value is encoded.\n\t */\n\tpublic $encoded = false;\n\n\t/**\n\t * Catches requests made to methods that don't exist. Specifically, looks for child nodes via XPath.\n\t *\n\t * @param string $name (Required) The name of the method.\n\t * @param array $arguments (Required) The arguments passed to the method.\n\t * @return mixed Either an array of matches, or a single <CFSimpleXML> element.\n\t */\n\tpublic function __call($name, $arguments)\n\t{\n\t\t// Remap $this\n\t\t$self = $this;\n\n\t\t// Re-base the XML\n\t\t$self = new CFSimpleXML($self->asXML());\n\n\t\t// Determine XPath query\n\t\t$self->xpath_expression = 'descendant-or-self::' . $name;\n\n\t\t// Get the results and augment with CFArray\n\t\t$results = $self->xpath($self->xpath_expression);\n\t\tif (!count($results)) return false;\n\t\t$results = new CFArray($results);\n\n\t\t// If an integer was passed, return only that result\n\t\tif (isset($arguments[0]) && is_int($arguments[0]))\n\t\t{\n\t\t\tif (isset($results[$arguments[0]]))\n\t\t\t{\n\t\t\t\treturn $results[$arguments[0]];\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\t\treturn $results;\n\t}\n\n\t/**\n\t * Gets the current XML node as a true string.\n\t *\n\t * @return string The current XML node as a true string.\n\t */\n\tpublic function __toString()\n\t{\n\t\treturn $this->to_string();\n\t}\n\n\t/**\n\t * Alternate approach to constructing a new instance. Supports chaining.\n\t *\n\t * @param string $data (Required) A well-formed XML string or the path or URL to an XML document if $data_is_url is <code>true</code>.\n\t * @param integer $options (Optional) Used to specify additional LibXML parameters. The default value is <code>0</code>.\n\t * @param boolean $data_is_url (Optional) Specify a value of <code>true</code> to specify that data is a path or URL to an XML document instead of string data. The default value is <code>false</code>.\n\t * @param string $ns (Optional) The XML namespace to return values for.\n\t * @param boolean $is_prefix (Optional) (No description provided by PHP.net.)\n\t * @return CFSimpleXML Creates a new <CFSimpleXML> element.\n\t */\n\tpublic static function init($data, $options = 0, $data_is_url, $ns, $is_prefix = false)\n\t{\n\t\tif (version_compare(PHP_VERSION, '5.3.0', '<'))\n\t\t{\n\t\t\tthrow new Exception('PHP 5.3 or newer is required to instantiate a new class with CLASS::init().');\n\t\t}\n\n\t\t$self = get_called_class();\n\t\treturn new $self($data, $options, $data_is_url, $ns, $is_prefix);\n\t}\n\n\n\t/*%******************************************************************************************%*/\n\t// TRAVERSAL\n\n\t/**\n\t * Wraps the results of an XPath query in a <CFArray> object.\n\t *\n\t * @param string $expr (Required) The XPath expression to use to query the XML response.\n\t * @return CFArray A <CFArray> object containing the results of the XPath query.\n\t */\n\tpublic function query($expr)\n\t{\n\t\treturn new CFArray($this->xpath($expr));\n\t}\n\n\t/**\n\t * Gets the parent or a preferred ancestor of the current element.\n\t *\n\t * @param string $node (Optional) Name of the ancestor element to match and return.\n\t * @return CFSimpleXML A <CFSimpleXML> object containing the requested node.\n\t */\n\tpublic function parent($node = null)\n\t{\n\t\tif ($node)\n\t\t{\n\t\t\t$parents = $this->xpath('ancestor-or-self::' . $node);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$parents = $this->xpath('parent::*');\n\t\t}\n\n\t\treturn $parents[0];\n\t}\n\n\n\t/*%******************************************************************************************%*/\n\t// ALTERNATE FORMATS\n\n\t/**\n\t * Gets the current XML node as a true string.\n\t *\n\t * @return string The current XML node as a true string.\n\t */\n\tpublic function to_string()\n\t{\n\t\t$s = parent::__toString();\n\n\t\tif ($this->attributes())\n\t\t{\n\t\t\treturn json_decode(substr($s, 14));\n\t\t}\n\n\t\treturn $s;\n\t}\n\n\t/**\n\t * Gets the current XML node as <CFArray>, a child class of PHP's <php:ArrayObject> class.\n\t *\n\t * @return CFArray The current XML node as a <CFArray> object.\n\t */\n\tpublic function to_array()\n\t{\n\t\treturn new CFArray(json_decode(json_encode($this), true));\n\t}\n\n\t/**\n\t * Gets the current XML node as a stdClass object.\n\t *\n\t * @return array The current XML node as a stdClass object.\n\t */\n\tpublic function to_stdClass()\n\t{\n\t\treturn json_decode(json_encode($this));\n\t}\n\n\t/**\n\t * Gets the current XML node as a JSON string.\n\t *\n\t * @return string The current XML node as a JSON string.\n\t */\n\tpublic function to_json()\n\t{\n\t\treturn json_encode($this);\n\t}\n\n\t/**\n\t * Gets the current XML node as a YAML string.\n\t *\n\t * @return string The current XML node as a YAML string.\n\t */\n\tpublic function to_yaml()\n\t{\n\t\treturn sfYaml::dump(json_decode(json_encode($this), true), 5);\n\t}\n\n\n\t/*%******************************************************************************************%*/\n\t// COMPARISONS\n\n\t/**\n\t * Whether or not the current node exactly matches the compared value.\n\t *\n\t * @param string $value (Required) The value to compare the current node to.\n\t * @return boolean Whether or not the current node exactly matches the compared value.\n\t */\n\tpublic function is($value)\n\t{\n\t\treturn ((string) $this === $value);\n\t}\n\n\t/**\n\t * Whether or not the current node contains the compared value.\n\t *\n\t * @param string $value (Required) The value to use to determine whether it is contained within the node.\n\t * @return boolean Whether or not the current node contains the compared value.\n\t */\n\tpublic function contains($value)\n\t{\n\t\treturn (stripos((string) $this, $value) !== false);\n\t}\n\n\t/**\n\t * Whether or not the current node matches the regular expression pattern.\n\t *\n\t * @param string $pattern (Required) The pattern to match the current node against.\n\t * @return boolean Whether or not the current node matches the pattern.\n\t */\n\tpublic function matches($pattern)\n\t{\n\t\treturn (bool) preg_match($pattern, (string) $this);\n\t}\n\n\t/**\n\t * Whether or not the current node starts with the compared value.\n\t *\n\t * @param string $value (Required) The value to compare the current node to.\n\t * @return boolean Whether or not the current node starts with the compared value.\n\t */\n\tpublic function starts_with($value)\n\t{\n\t\treturn $this->matches(\"@^$value@u\");\n\t}\n\n\t/**\n\t * Whether or not the current node ends with the compared value.\n\t *\n\t * @param string $value (Required) The value to compare the current node to.\n\t * @return boolean Whether or not the current node ends with the compared value.\n\t */\n\tpublic function ends_with($value)\n\t{\n\t\treturn $this->matches(\"@$value$@u\");\n\t}\n}\n"
  },
  {
    "path": "utilities/stepconfig.class.php",
    "content": "<?php\n/*\n * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\").\n * You may not use this file except in compliance with the License.\n * A copy of the License is located at\n *\n *  http://aws.amazon.com/apache2.0\n *\n * or in the \"license\" file accompanying this file. This file is distributed\n * on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either\n * express or implied. See the License for the specific language governing\n * permissions and limitations under the License.\n */\n\n\n/*%******************************************************************************************%*/\n// CLASS\n\n/**\n * Contains functionality for simplifying Amazon EMR Hadoop steps.\n *\n * @version 2010.11.16\n * @license See the included NOTICE.md file for more information.\n * @copyright See the included NOTICE.md file for more information.\n * @link http://aws.amazon.com/php/ PHP Developer Center\n */\nclass CFStepConfig\n{\n\n\t/**\n\t * Stores the configuration map.\n\t */\n\tpublic $config;\n\n\t/**\n\t * Constructs a new instance of this class.\n\t *\n\t * @param array $config (Required) An associative array representing the Hadoop step configuration.\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function __construct($config)\n\t{\n\t\t// Handle Hadoop jar arguments\n\t\tif (isset($config['HadoopJarStep']['Args']) && $args = $config['HadoopJarStep']['Args'])\n\t\t{\n\t\t\t$config['HadoopJarStep']['Args'] = is_array($args) ? $args : array($args);\n\t\t}\n\n\t\t$this->config = $config;\n\t}\n\n\t/**\n\t * Constructs a new instance of this class, and allows chaining.\n\t *\n\t * @param array $config (Required) An associative array representing the Hadoop step configuration.\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic static function init($config)\n\t{\n\t\tif (version_compare(PHP_VERSION, '5.3.0', '<'))\n\t\t{\n\t\t\tthrow new Exception('PHP 5.3 or newer is required to instantiate a new class with CLASS::init().');\n\t\t}\n\n\t\t$self = get_called_class();\n\t\treturn new $self($config);\n\t}\n\n\t/**\n\t * Returns a JSON representation of the object when typecast as a string.\n\t *\n\t * @return string A JSON representation of the object.\n\t * @link http://www.php.net/manual/en/language.oop5.magic.php#language.oop5.magic.tostring PHP Magic Methods\n\t */\n\tpublic function __toString()\n\t{\n\t\treturn json_encode($this->config);\n\t}\n\n\t/**\n\t * Returns the configuration data.\n\t *\n\t * @return array The configuration data.\n\t */\n\tpublic function get_config()\n\t{\n\t\treturn $this->config;\n\t}\n}\n"
  },
  {
    "path": "utilities/utilities.class.php",
    "content": "<?php\n/*\n * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\").\n * You may not use this file except in compliance with the License.\n * A copy of the License is located at\n *\n *  http://aws.amazon.com/apache2.0\n *\n * or in the \"license\" file accompanying this file. This file is distributed\n * on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either\n * express or implied. See the License for the specific language governing\n * permissions and limitations under the License.\n */\n\n\n/*%******************************************************************************************%*/\n// CLASS\n\n/**\n * Contains a set of utility methods for connecting to, and working with, AWS.\n *\n * @version 2010.09.30\n * @license See the included NOTICE.md file for more information.\n * @copyright See the included NOTICE.md file for more information.\n * @link http://aws.amazon.com/php/ PHP Developer Center\n */\nclass CFUtilities\n{\n\n\t/*%******************************************************************************************%*/\n\t// CONSTANTS\n\n\t/**\n\t * Define the RFC 2616-compliant date format.\n\t */\n\tconst DATE_FORMAT_RFC2616 = 'D, d M Y H:i:s \\G\\M\\T';\n\n\t/**\n\t * Define the ISO-8601-compliant date format.\n\t */\n\tconst DATE_FORMAT_ISO8601 = 'Y-m-d\\TH:i:s\\Z';\n\n\t/**\n\t * Define the MySQL-compliant date format.\n\t */\n\tconst DATE_FORMAT_MYSQL = 'Y-m-d H:i:s';\n\n\t/**\n\t * Define the Signature v4 date format.\n\t */\n\tconst DATE_FORMAT_SIGV4 = 'Ymd\\THis\\Z';\n\n\n\t/*%******************************************************************************************%*/\n\t// METHODS\n\n\t/**\n\t * Constructs a new instance of this class.\n\t *\n\t * @return $this A reference to the current instance.\n\t */\n\tpublic function __construct()\n\t{\n\t\treturn $this;\n\t}\n\n\t/**\n\t * Retrieves the value of a class constant, while avoiding the `T_PAAMAYIM_NEKUDOTAYIM` error. Misspelled because `const` is a reserved word.\n\t *\n\t * @param object $class (Required) An instance of the class containing the constant.\n\t * @param string $const (Required) The name of the constant to retrieve.\n\t * @return mixed The value of the class constant.\n\t */\n\tpublic function konst($class, $const)\n\t{\n\t\tif (is_string($class))\n\t\t{\n\t\t\t$ref = new ReflectionClass($class);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t$ref = new ReflectionObject($class);\n\t\t}\n\n\t\treturn $ref->getConstant($const);\n\t}\n\n\t/**\n\t * Convert a HEX value to Base64.\n\t *\n\t * @param string $str (Required) Value to convert.\n\t * @return string Base64-encoded string.\n\t */\n\tpublic function hex_to_base64($str)\n\t{\n\t\t$raw = '';\n\n\t\tfor ($i = 0; $i < strlen($str); $i += 2)\n\t\t{\n\t\t\t$raw .= chr(hexdec(substr($str, $i, 2)));\n\t\t}\n\n\t\treturn base64_encode($raw);\n\t}\n\n\t/**\n\t * Convert an associative array into a query string.\n\t *\n\t * @param array $array (Required) Array to convert.\n\t * @return string URL-friendly query string.\n\t */\n\tpublic function to_query_string($array)\n\t{\n\t\t$temp = array();\n\n\t\tforeach ($array as $key => $value)\n\t\t{\n\t\t\tif (is_string($key) && !is_array($value))\n\t\t\t{\n\t\t\t\t$temp[] = rawurlencode($key) . '=' . rawurlencode($value);\n\t\t\t}\n\t\t}\n\n\t\treturn implode('&', $temp);\n\t}\n\n\t/**\n\t * Convert an associative array into a sign-able string.\n\t *\n\t * @param array $array (Required) Array to convert.\n\t * @return string URL-friendly sign-able string.\n\t */\n\tpublic function to_signable_string($array)\n\t{\n\t\t$t = array();\n\n\t\tforeach ($array as $k => $v)\n\t\t{\n\t\t\t$t[] = $this->encode_signature2($k) . '=' . $this->encode_signature2($v);\n\t\t}\n\n\t\treturn implode('&', $t);\n\t}\n\n\t/**\n\t * Encode the value according to RFC 3986.\n\t *\n\t * @param string $string (Required) String to convert.\n\t * @return string URL-friendly sign-able string.\n\t */\n\tpublic function encode_signature2($string)\n\t{\n\t\t$string = rawurlencode($string);\n\t\treturn str_replace('%7E', '~', $string);\n\t}\n\n\t/**\n\t * Convert a query string into an associative array. Multiple, identical keys will become an indexed array.\n\t *\n\t * @param string $qs (Required) Query string to convert.\n\t * @return array Associative array of keys and values.\n\t */\n\tpublic function query_to_array($qs)\n\t{\n\t\t$query = explode('&', $qs);\n\t\t$data = array();\n\n\t\tforeach ($query as $q)\n\t\t{\n\t\t\t$q = explode('=', $q);\n\n\t\t\tif (isset($data[$q[0]]) && is_array($data[$q[0]]))\n\t\t\t{\n\t\t\t\t$data[$q[0]][] = urldecode($q[1]);\n\t\t\t}\n\t\t\telse if (isset($data[$q[0]]) && !is_array($data[$q[0]]))\n\t\t\t{\n\t\t\t\t$data[$q[0]] = array($data[$q[0]]);\n\t\t\t\t$data[$q[0]][] = urldecode($q[1]);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t$data[urldecode($q[0])] = urldecode($q[1]);\n\t\t\t}\n\t\t}\n\t\treturn $data;\n\t}\n\n\t/**\n\t * Return human readable file sizes.\n\t *\n\t * @author Aidan Lister <aidan@php.net>\n\t * @author Ryan Parman <ryan@getcloudfusion.com>\n\t * @license http://www.php.net/license/3_01.txt PHP License\n\t * @param integer $size (Required) Filesize in bytes.\n\t * @param string $unit (Optional) The maximum unit to use. Defaults to the largest appropriate unit.\n\t * @param string $default (Optional) The format for the return string. Defaults to `%01.2f %s`.\n\t * @return string The human-readable file size.\n\t * @link http://aidanlister.com/repos/v/function.size_readable.php Original Function\n\t */\n\tpublic function size_readable($size, $unit = null, $default = null)\n\t{\n\t\t// Units\n\t\t$sizes = array('B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB');\n\t\t$mod = 1024;\n\t\t$ii = count($sizes) - 1;\n\n\t\t// Max unit\n\t\t$unit = array_search((string) $unit, $sizes);\n\t\tif ($unit === null || $unit === false)\n\t\t{\n\t\t\t$unit = $ii;\n\t\t}\n\n\t\t// Return string\n\t\tif ($default === null)\n\t\t{\n\t\t\t$default = '%01.2f %s';\n\t\t}\n\n\t\t// Loop\n\t\t$i = 0;\n\t\twhile ($unit != $i && $size >= 1024 && $i < $ii)\n\t\t{\n\t\t\t$size /= $mod;\n\t\t\t$i++;\n\t\t}\n\n\t\treturn sprintf($default, $size, $sizes[$i]);\n\t}\n\n\t/**\n\t * Convert a number of seconds into Hours:Minutes:Seconds.\n\t *\n\t * @param integer $seconds (Required) The number of seconds to convert.\n\t * @return string The formatted time.\n\t */\n\tpublic function time_hms($seconds)\n\t{\n\t\t$time = '';\n\n\t\t// First pass\n\t\t$hours = (int) ($seconds / 3600);\n\t\t$seconds = $seconds % 3600;\n\t\t$minutes = (int) ($seconds / 60);\n\t\t$seconds = $seconds % 60;\n\n\t\t// Cleanup\n\t\t$time .= ($hours) ? $hours . ':' : '';\n\t\t$time .= ($minutes < 10 && $hours > 0) ? '0' . $minutes : $minutes;\n\t\t$time .= ':';\n\t\t$time .= ($seconds < 10) ? '0' . $seconds : $seconds;\n\n\t\treturn $time;\n\t}\n\n\t/**\n\t * Returns the first value that is set. Based on [Try.these()](http://api.prototypejs.org/language/Try/these/) from [Prototype](http://prototypejs.org).\n\t *\n\t * @param array $attrs (Required) The attributes to test, as strings. Intended for testing properties of the $base object, but also works with variables if you place an @ symbol at the beginning of the command.\n\t * @param object $base (Optional) The base object to use, if any.\n\t * @param mixed $default (Optional) What to return if there are no matches. Defaults to `null`.\n\t * @return mixed Either a matching property of a given object, boolean `false`, or any other data type you might choose.\n\t */\n\tpublic function try_these($attrs, $base = null, $default = null)\n\t{\n\t\tif ($base)\n\t\t{\n\t\t\tforeach ($attrs as $attr)\n\t\t\t{\n\t\t\t\tif (isset($base->$attr))\n\t\t\t\t{\n\t\t\t\t\treturn $base->$attr;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\tforeach ($attrs as $attr)\n\t\t\t{\n\t\t\t\tif (isset($attr))\n\t\t\t\t{\n\t\t\t\t\treturn $attr;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn $default;\n\t}\n\n\t/**\n\t * Can be removed once all calls are updated.\n\t *\n\t * @deprecated Use <php:json_encode()> instead.\n\t * @param mixed $obj (Required) The PHP object to convert into a JSON string.\n\t * @return string A JSON string.\n\t */\n\tpublic function json_encode($obj)\n\t{\n\t\treturn json_encode($obj);\n\t}\n\n\t/**\n\t * Converts a SimpleXML response to an array structure.\n\t *\n\t * @param ResponseCore $response (Required) A response value.\n\t * @return array The response value as a standard, multi-dimensional array.\n\t */\n\tpublic function convert_response_to_array(ResponseCore $response)\n\t{\n\t\treturn json_decode(json_encode($response), true);\n\t}\n\n\t/**\n\t * Checks to see if a date stamp is ISO-8601 formatted, and if not, makes it so.\n\t *\n\t * @param string $datestamp (Required) A date stamp, or a string that can be parsed into a date stamp.\n\t * @return string An ISO-8601 formatted date stamp.\n\t */\n\tpublic function convert_date_to_iso8601($datestamp)\n\t{\n\t\tif (!preg_match('/\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}((\\+|-)\\d{2}:\\d{2}|Z)/m', $datestamp))\n\t\t{\n\t\t\treturn gmdate(self::DATE_FORMAT_ISO8601, strtotime($datestamp));\n\t\t}\n\n\t\treturn $datestamp;\n\t}\n\n\t/**\n\t * Determines whether the data is Base64 encoded or not.\n\t *\n\t * @license http://us.php.net/manual/en/function.base64-decode.php#81425 PHP License\n\t * @param string $s (Required) The string to test.\n\t * @return boolean Whether the string is Base64 encoded or not.\n\t */\n\tpublic function is_base64($s)\n\t{\n\t\treturn (bool) preg_match('/^[a-zA-Z0-9\\/\\r\\n+]*={0,2}$/', $s);\n\t}\n\n\t/**\n\t * Determines whether the data is a JSON string or not.\n\t *\n\t * @param string $s (Required) The string to test.\n\t * @return boolean Whether the string is a valid JSON object or not.\n\t */\n\tpublic function is_json($s)\n\t{\n\t\treturn !!(json_decode($s) instanceof stdClass);\n\t}\n\n\t/**\n\t * Decodes `\\uXXXX` entities into their real unicode character equivalents.\n\t *\n\t * @param string $s (Required) The string to decode.\n\t * @return string The decoded string.\n\t */\n\tpublic function decode_uhex($s)\n\t{\n\t\tpreg_match_all('/\\\\\\u([0-9a-f]{4})/i', $s, $matches);\n\t\t$matches = $matches[count($matches) - 1];\n\t\t$map = array();\n\n\t\tforeach ($matches as $match)\n\t\t{\n\t\t\tif (!isset($map[$match]))\n\t\t\t{\n\t\t\t\t$map['\\u' . $match] = html_entity_decode('&#' . hexdec($match) . ';', ENT_NOQUOTES, 'UTF-8');\n\t\t\t}\n\t\t}\n\n\t\treturn str_replace(array_keys($map), $map, $s);\n\t}\n\n\t/**\n\t * Generates a random GUID.\n\t *\n\t * @author Alix Axel <http://www.php.net/manual/en/function.com-create-guid.php#99425>\n\t * @license http://www.php.net/license/3_01.txt PHP License\n\t * @return string A random GUID.\n\t */\n\tpublic function generate_guid()\n\t{\n\t    return sprintf(\n\t\t\t'%04X%04X-%04X-%04X-%04X-%04X%04X%04X',\n\t\t\tmt_rand(0, 65535),\n\t\t\tmt_rand(0, 65535),\n\t\t\tmt_rand(0, 65535),\n\t\t\tmt_rand(16384, 20479),\n\t\t\tmt_rand(32768, 49151),\n\t\t\tmt_rand(0, 65535),\n\t\t\tmt_rand(0, 65535),\n\t\t\tmt_rand(0, 65535)\n\t\t);\n\t}\n}\n"
  }
]