PageRenderTime 55ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/aws/services/s3.class.php

https://bitbucket.org/ardydedase/web-build-optimizer
PHP | 3639 lines | 1821 code | 463 blank | 1355 comment | 295 complexity | 96c14e0c753ef445895070abbb7c616e MD5 | raw file
Possible License(s): Apache-2.0, BSD-3-Clause

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /*
  3. * Copyright 2010-2011 Amazon.com, Inc. or its affiliates. All Rights Reserved.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License").
  6. * You may not use this file except in compliance with the License.
  7. * A copy of the License is located at
  8. *
  9. * http://aws.amazon.com/apache2.0
  10. *
  11. * or in the "license" file accompanying this file. This file is distributed
  12. * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
  13. * express or implied. See the License for the specific language governing
  14. * permissions and limitations under the License.
  15. */
  16. /*%******************************************************************************************%*/
  17. // EXCEPTIONS
  18. /**
  19. * Default S3 Exception.
  20. */
  21. class S3_Exception extends Exception {}
  22. /*%******************************************************************************************%*/
  23. // MAIN CLASS
  24. /**
  25. * Amazon S3 is a web service that enables you to store data in the cloud. You can then download the data
  26. * or use the data with other AWS services, such as Amazon Elastic Cloud Computer (EC2).
  27. *
  28. * Amazon Simple Storage Service (Amazon S3) is storage for the Internet. You can use Amazon S3 to store
  29. * and retrieve any amount of data at any time, from anywhere on the web. You can accomplish these tasks
  30. * using the AWS Management Console, which is a simple and intuitive web interface.
  31. *
  32. * To get the most out of Amazon S3, you need to understand a few simple concepts. Amazon S3 stores data
  33. * as objects in buckets. An object is comprised of a file and optionally any metadata that describes
  34. * that file.
  35. *
  36. * To store an object in Amazon S3, you upload the file you want to store to a bucket. When you upload a
  37. * file, you can set permissions on the object as well as any metadata.
  38. *
  39. * Buckets are the containers for objects. You can have one or more buckets. For each bucket, you can control
  40. * access to the bucket (who can create, delete, and list objects in the bucket), view access logs for the
  41. * bucket and its objects, and choose the geographical region where Amazon S3 will store the bucket and its
  42. * contents.
  43. *
  44. * Visit <http://aws.amazon.com/s3/> for more information.
  45. *
  46. * @version 2011.12.02
  47. * @license See the included NOTICE.md file for more information.
  48. * @copyright See the included NOTICE.md file for more information.
  49. * @link http://aws.amazon.com/s3/ Amazon Simple Storage Service
  50. * @link http://aws.amazon.com/documentation/s3/ Amazon Simple Storage Service documentation
  51. */
  52. class AmazonS3 extends CFRuntime
  53. {
  54. /*%******************************************************************************************%*/
  55. // CLASS CONSTANTS
  56. /**
  57. * The default endpoint.
  58. */
  59. const DEFAULT_URL = 's3.amazonaws.com';
  60. /**
  61. * Specify the queue URL for the US-East (Northern Virginia) Region.
  62. */
  63. const REGION_US_E1 = '';
  64. /**
  65. * Specify the queue URL for the US-West (Northern California) Region.
  66. */
  67. const REGION_US_W1 = 'us-west-1';
  68. /**
  69. * Specify the queue URL for the US-West (Oregon) Region.
  70. */
  71. const REGION_US_W2 = 'us-west-2';
  72. /**
  73. * Specify the queue URL for the EU (Ireland) Region.
  74. */
  75. const REGION_EU_W1 = 'EU';
  76. /**
  77. * Specify the queue URL for the Asia Pacific (Singapore) Region.
  78. */
  79. const REGION_APAC_SE1 = 'ap-southeast-1';
  80. /**
  81. * Specify the queue URL for the Asia Pacific (Japan) Region.
  82. */
  83. const REGION_APAC_NE1 = 'ap-northeast-1';
  84. /**
  85. * ACL: Owner-only read/write.
  86. */
  87. const ACL_PRIVATE = 'private';
  88. /**
  89. * ACL: Owner read/write, public read.
  90. */
  91. const ACL_PUBLIC = 'public-read';
  92. /**
  93. * ACL: Public read/write.
  94. */
  95. const ACL_OPEN = 'public-read-write';
  96. /**
  97. * ACL: Owner read/write, authenticated read.
  98. */
  99. const ACL_AUTH_READ = 'authenticated-read';
  100. /**
  101. * ACL: Bucket owner read.
  102. */
  103. const ACL_OWNER_READ = 'bucket-owner-read';
  104. /**
  105. * ACL: Bucket owner full control.
  106. */
  107. const ACL_OWNER_FULL_CONTROL = 'bucket-owner-full-control';
  108. /**
  109. * When applied to a bucket, grants permission to list the bucket. When applied to an object, this
  110. * grants permission to read the object data and/or metadata.
  111. */
  112. const GRANT_READ = 'READ';
  113. /**
  114. * When applied to a bucket, grants permission to create, overwrite, and delete any object in the
  115. * bucket. This permission is not supported for objects.
  116. */
  117. const GRANT_WRITE = 'WRITE';
  118. /**
  119. * Grants permission to read the ACL for the applicable bucket or object. The owner of a bucket or
  120. * object always has this permission implicitly.
  121. */
  122. const GRANT_READ_ACP = 'READ_ACP';
  123. /**
  124. * Gives permission to overwrite the ACP for the applicable bucket or object. The owner of a bucket
  125. * or object always has this permission implicitly. Granting this permission is equivalent to granting
  126. * FULL_CONTROL because the grant recipient can make any changes to the ACP.
  127. */
  128. const GRANT_WRITE_ACP = 'WRITE_ACP';
  129. /**
  130. * Provides READ, WRITE, READ_ACP, and WRITE_ACP permissions. It does not convey additional rights and
  131. * is provided only for convenience.
  132. */
  133. const GRANT_FULL_CONTROL = 'FULL_CONTROL';
  134. /**
  135. * The "AuthenticatedUsers" group for access control policies.
  136. */
  137. const USERS_AUTH = 'http://acs.amazonaws.com/groups/global/AuthenticatedUsers';
  138. /**
  139. * The "AllUsers" group for access control policies.
  140. */
  141. const USERS_ALL = 'http://acs.amazonaws.com/groups/global/AllUsers';
  142. /**
  143. * The "LogDelivery" group for access control policies.
  144. */
  145. const USERS_LOGGING = 'http://acs.amazonaws.com/groups/s3/LogDelivery';
  146. /**
  147. * PCRE: Match all items
  148. */
  149. const PCRE_ALL = '/.*/i';
  150. /**
  151. * Standard storage redundancy.
  152. */
  153. const STORAGE_STANDARD = 'STANDARD';
  154. /**
  155. * Reduced storage redundancy.
  156. */
  157. const STORAGE_REDUCED = 'REDUCED_REDUNDANCY';
  158. /*%******************************************************************************************%*/
  159. // PROPERTIES
  160. /**
  161. * The request URL.
  162. */
  163. public $request_url;
  164. /**
  165. * The virtual host setting.
  166. */
  167. public $vhost;
  168. /**
  169. * The base XML elements to use for access control policy methods.
  170. */
  171. public $base_acp_xml;
  172. /**
  173. * The base XML elements to use for creating buckets in regions.
  174. */
  175. public $base_location_constraint;
  176. /**
  177. * The base XML elements to use for logging methods.
  178. */
  179. public $base_logging_xml;
  180. /**
  181. * The base XML elements to use for notifications.
  182. */
  183. public $base_notification_xml;
  184. /**
  185. * The base XML elements to use for versioning.
  186. */
  187. public $base_versioning_xml;
  188. /**
  189. * The base XML elements to use for completing a multipart upload.
  190. */
  191. public $complete_mpu_xml;
  192. /**
  193. * The base XML elements to use for website support.
  194. */
  195. public $website_config_xml;
  196. /**
  197. * The DNS vs. Path-style setting.
  198. */
  199. public $path_style = false;
  200. /**
  201. * The state of whether the prefix change is temporary or permanent.
  202. */
  203. public $temporary_prefix = false;
  204. /**
  205. * The state of whether the response should be parsed or not.
  206. */
  207. public $parse_the_response = true;
  208. /*%******************************************************************************************%*/
  209. // CONSTRUCTOR
  210. /**
  211. * Constructs a new instance of <AmazonS3>. If the <code>AWS_DEFAULT_CACHE_CONFIG</code> configuration
  212. * option is set, requests will be authenticated using a session token. Otherwise, requests will use
  213. * the older authentication method.
  214. *
  215. * @param string $key (Optional) Your AWS key, or a session key. If blank, it will look for the <code>AWS_KEY</code> constant.
  216. * @param string $secret_key (Optional) Your AWS secret key, or a session secret key. If blank, it will look for the <code>AWS_SECRET_KEY</code> constant.
  217. * @param string $token (optional) An AWS session token. If blank, a request will be made to the AWS Secure Token Service to fetch a set of session credentials.
  218. * @return boolean A value of <code>false</code> if no valid values are set, otherwise <code>true</code>.
  219. */
  220. public function __construct($key = null, $secret_key = null, $token = null)
  221. {
  222. $this->vhost = null;
  223. $this->api_version = '2006-03-01';
  224. $this->hostname = self::DEFAULT_URL;
  225. $this->base_acp_xml = '<?xml version="1.0" encoding="UTF-8"?><AccessControlPolicy xmlns="http://s3.amazonaws.com/doc/latest/"></AccessControlPolicy>';
  226. $this->base_location_constraint = '<?xml version="1.0" encoding="UTF-8"?><CreateBucketConfiguration xmlns="http://s3.amazonaws.com/doc/' . $this->api_version . '/"><LocationConstraint></LocationConstraint></CreateBucketConfiguration>';
  227. $this->base_logging_xml = '<?xml version="1.0" encoding="utf-8"?><BucketLoggingStatus xmlns="http://doc.s3.amazonaws.com/' . $this->api_version . '"></BucketLoggingStatus>';
  228. $this->base_notification_xml = '<?xml version="1.0" encoding="utf-8"?><NotificationConfiguration></NotificationConfiguration>';
  229. $this->base_versioning_xml = '<?xml version="1.0" encoding="utf-8"?><VersioningConfiguration xmlns="http://s3.amazonaws.com/doc/' . $this->api_version . '/"></VersioningConfiguration>';
  230. $this->complete_mpu_xml = '<?xml version="1.0" encoding="utf-8"?><CompleteMultipartUpload></CompleteMultipartUpload>';
  231. $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>';
  232. $this->multi_object_delete_xml = '<?xml version="1.0" encoding="utf-8"?><Delete></Delete>';
  233. if (!$key && !defined('AWS_KEY'))
  234. {
  235. // @codeCoverageIgnoreStart
  236. throw new S3_Exception('No account key was passed into the constructor, nor was it set in the AWS_KEY constant.');
  237. // @codeCoverageIgnoreEnd
  238. }
  239. if (!$secret_key && !defined('AWS_SECRET_KEY'))
  240. {
  241. // @codeCoverageIgnoreStart
  242. throw new S3_Exception('No account secret was passed into the constructor, nor was it set in the AWS_SECRET_KEY constant.');
  243. // @codeCoverageIgnoreEnd
  244. }
  245. if (defined('AWS_DEFAULT_CACHE_CONFIG') && AWS_DEFAULT_CACHE_CONFIG)
  246. {
  247. return parent::session_based_auth($key, $secret_key, $token);
  248. }
  249. return parent::__construct($key, $secret_key, $token);
  250. }
  251. /*%******************************************************************************************%*/
  252. // AUTHENTICATION
  253. /**
  254. * Authenticates a connection to Amazon S3. Do not use directly unless implementing custom methods for
  255. * this class.
  256. *
  257. * @param string $bucket (Required) The name of the bucket to use.
  258. * @param array $opt (Optional) An associative array of parameters for authenticating. See the individual methods for allowed keys.
  259. * @param string $location (Do Not Use) Used internally by this function on occasions when Amazon S3 returns a redirect code and it needs to call itself recursively.
  260. * @param integer $redirects (Do Not Use) Used internally by this function on occasions when Amazon S3 returns a redirect code and it needs to call itself recursively.
  261. * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
  262. * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/S3_Authentication.html REST authentication
  263. */
  264. public function authenticate($bucket, $opt = null, $location = null, $redirects = 0, $nothing = null)
  265. {
  266. /*
  267. * Overriding or extending this class? You can pass the following "magic" keys into $opt.
  268. *
  269. * ## verb, resource, sub_resource and query_string ##
  270. * <verb> /<resource>?<sub_resource>&<query_string>
  271. * GET /filename.txt?versions&prefix=abc&max-items=1
  272. *
  273. * ## versionId, uploadId, partNumber, response-* ##
  274. * These don't follow the same rules as above, in that the they needs to be signed, while
  275. * other query_string values do not.
  276. *
  277. * ## curlopts ##
  278. * These values get passed directly to the cURL methods in RequestCore.
  279. *
  280. * ## fileUpload, fileDownload, seekTo ##
  281. * These are slightly modified and then passed to the cURL methods in RequestCore.
  282. *
  283. * ## headers ##
  284. * $opt['headers'] is an array, whose keys are HTTP headers to be sent.
  285. *
  286. * ## body ##
  287. * This is the request body that is sent to the server via PUT/POST.
  288. *
  289. * ## preauth ##
  290. * This is a hook that tells authenticate() to generate a pre-authenticated URL.
  291. *
  292. * ## returnCurlHandle ##
  293. * Tells authenticate() to return the cURL handle for the request instead of executing it.
  294. */
  295. /**
  296. * @todo: Handle duplicate headers with different values.
  297. */
  298. // Validate the S3 bucket name
  299. if (!$this->validate_bucketname_support($bucket))
  300. {
  301. // @codeCoverageIgnoreStart
  302. throw new S3_Exception('S3 does not support "' . $bucket . '" as a valid bucket name. Review "Bucket Restrictions and Limitations" in the S3 Developer Guide for more information.');
  303. // @codeCoverageIgnoreEnd
  304. }
  305. // Die if $opt isn't set.
  306. if (!$opt) return false;
  307. $method_arguments = func_get_args();
  308. // Use the caching flow to determine if we need to do a round-trip to the server.
  309. if ($this->use_cache_flow)
  310. {
  311. // Generate an identifier specific to this particular set of arguments.
  312. $cache_id = $this->key . '_' . get_class($this) . '_' . $bucket . '_' . sha1(serialize($method_arguments));
  313. // Instantiate the appropriate caching object.
  314. $this->cache_object = new $this->cache_class($cache_id, $this->cache_location, $this->cache_expires, $this->cache_compress);
  315. if ($this->delete_cache)
  316. {
  317. $this->use_cache_flow = false;
  318. $this->delete_cache = false;
  319. return $this->cache_object->delete();
  320. }
  321. // Invoke the cache callback function to determine whether to pull data from the cache or make a fresh request.
  322. $data = $this->cache_object->response_manager(array($this, 'cache_callback'), $method_arguments);
  323. if ($this->parse_the_response)
  324. {
  325. // Parse the XML body
  326. $data = $this->parse_callback($data);
  327. }
  328. // End!
  329. return $data;
  330. }
  331. // If we haven't already set a resource prefix...
  332. if (!$this->resource_prefix || $this->path_style)
  333. {
  334. // And if the bucket name isn't DNS-valid...
  335. if (!$this->validate_bucketname_create($bucket))
  336. {
  337. // Fall back to the older path-style URI
  338. $this->set_resource_prefix('/' . $bucket);
  339. $this->temporary_prefix = true;
  340. }
  341. elseif ($this->path_style)
  342. {
  343. // Fall back to the older path-style URI
  344. $this->set_resource_prefix('/' . $bucket);
  345. }
  346. }
  347. // Determine hostname
  348. $scheme = $this->use_ssl ? 'https://' : 'http://';
  349. if ($this->resource_prefix || $this->path_style) // Use bucket-in-path method.
  350. {
  351. $hostname = $this->hostname . $this->resource_prefix . (($bucket === '' || $this->resource_prefix === '/' . $bucket) ? '' : ('/' . $bucket));
  352. }
  353. else
  354. {
  355. $hostname = $this->vhost ? $this->vhost : (($bucket === '') ? $this->hostname : ($bucket . '.') . $this->hostname);
  356. }
  357. // Get the UTC timestamp in RFC 2616 format
  358. $date = gmdate(CFUtilities::DATE_FORMAT_RFC2616, (time() + (integer) $this->adjust_offset));
  359. // Storage for request parameters.
  360. $resource = '';
  361. $sub_resource = '';
  362. $querystringparams = array();
  363. $signable_querystringparams = array();
  364. $string_to_sign = '';
  365. $headers = array(
  366. 'Content-MD5' => '',
  367. 'Content-Type' => 'application/x-www-form-urlencoded',
  368. 'Date' => $date
  369. );
  370. /*%******************************************************************************************%*/
  371. // Do we have an authentication token?
  372. if ($this->auth_token)
  373. {
  374. $headers['X-Amz-Security-Token'] = $this->auth_token;
  375. }
  376. // Handle specific resources
  377. if (isset($opt['resource']))
  378. {
  379. $resource .= $opt['resource'];
  380. }
  381. // Merge query string values
  382. if (isset($opt['query_string']))
  383. {
  384. $querystringparams = array_merge($querystringparams, $opt['query_string']);
  385. }
  386. $query_string = $this->util->to_query_string($querystringparams);
  387. // Merge the signable query string values. Must be alphabetical.
  388. $signable_list = array(
  389. 'partNumber',
  390. 'response-cache-control',
  391. 'response-content-disposition',
  392. 'response-content-encoding',
  393. 'response-content-language',
  394. 'response-content-type',
  395. 'response-expires',
  396. 'uploadId',
  397. 'versionId'
  398. );
  399. foreach ($signable_list as $item)
  400. {
  401. if (isset($opt[$item]))
  402. {
  403. $signable_querystringparams[$item] = $opt[$item];
  404. }
  405. }
  406. $signable_query_string = $this->util->to_query_string($signable_querystringparams);
  407. // Merge the HTTP headers
  408. if (isset($opt['headers']))
  409. {
  410. $headers = array_merge($headers, $opt['headers']);
  411. }
  412. // Compile the URI to request
  413. $conjunction = '?';
  414. $signable_resource = '/' . str_replace('%2F', '/', rawurlencode($resource));
  415. $non_signable_resource = '';
  416. if (isset($opt['sub_resource']))
  417. {
  418. $signable_resource .= $conjunction . rawurlencode($opt['sub_resource']);
  419. $conjunction = '&';
  420. }
  421. if ($signable_query_string !== '')
  422. {
  423. $signable_query_string = $conjunction . $signable_query_string;
  424. $conjunction = '&';
  425. }
  426. if ($query_string !== '')
  427. {
  428. $non_signable_resource .= $conjunction . $query_string;
  429. $conjunction = '&';
  430. }
  431. $this->request_url = $scheme . $hostname . $signable_resource . $signable_query_string . $non_signable_resource;
  432. // Instantiate the request class
  433. $request = new $this->request_class($this->request_url, $this->proxy);
  434. // Update RequestCore settings
  435. $request->request_class = $this->request_class;
  436. $request->response_class = $this->response_class;
  437. // Pass along registered stream callbacks
  438. if ($this->registered_streaming_read_callback)
  439. {
  440. $request->register_streaming_read_callback($this->registered_streaming_read_callback);
  441. }
  442. if ($this->registered_streaming_write_callback)
  443. {
  444. $request->register_streaming_write_callback($this->registered_streaming_write_callback);
  445. }
  446. // Streaming uploads
  447. if (isset($opt['fileUpload']))
  448. {
  449. if (is_resource($opt['fileUpload']))
  450. {
  451. // Determine the length to read from the stream
  452. $length = null; // From current position until EOF by default, size determined by set_read_stream()
  453. if (isset($headers['Content-Length']))
  454. {
  455. $length = $headers['Content-Length'];
  456. }
  457. elseif (isset($opt['seekTo']))
  458. {
  459. // Read from seekTo until EOF by default
  460. $stats = fstat($opt['fileUpload']);
  461. if ($stats && $stats['size'] >= 0)
  462. {
  463. $length = $stats['size'] - (integer) $opt['seekTo'];
  464. }
  465. }
  466. $request->set_read_stream($opt['fileUpload'], $length);
  467. if ($headers['Content-Type'] === 'application/x-www-form-urlencoded')
  468. {
  469. $headers['Content-Type'] = 'application/octet-stream';
  470. }
  471. }
  472. else
  473. {
  474. $request->set_read_file($opt['fileUpload']);
  475. // Determine the length to read from the file
  476. $length = $request->read_stream_size; // The file size by default
  477. if (isset($headers['Content-Length']))
  478. {
  479. $length = $headers['Content-Length'];
  480. }
  481. elseif (isset($opt['seekTo']) && isset($length))
  482. {
  483. // Read from seekTo until EOF by default
  484. $length -= (integer) $opt['seekTo'];
  485. }
  486. $request->set_read_stream_size($length);
  487. // Attempt to guess the correct mime-type
  488. if ($headers['Content-Type'] === 'application/x-www-form-urlencoded')
  489. {
  490. $extension = explode('.', $opt['fileUpload']);
  491. $extension = array_pop($extension);
  492. $mime_type = CFMimeTypes::get_mimetype($extension);
  493. $headers['Content-Type'] = $mime_type;
  494. }
  495. }
  496. $headers['Content-Length'] = $request->read_stream_size;
  497. $headers['Content-MD5'] = '';
  498. }
  499. // Handle streaming file offsets
  500. if (isset($opt['seekTo']))
  501. {
  502. // Pass the seek position to RequestCore
  503. $request->set_seek_position((integer) $opt['seekTo']);
  504. }
  505. // Streaming downloads
  506. if (isset($opt['fileDownload']))
  507. {
  508. if (is_resource($opt['fileDownload']))
  509. {
  510. $request->set_write_stream($opt['fileDownload']);
  511. }
  512. else
  513. {
  514. $request->set_write_file($opt['fileDownload']);
  515. }
  516. }
  517. $curlopts = array();
  518. // Set custom CURLOPT settings
  519. if (isset($opt['curlopts']))
  520. {
  521. $curlopts = $opt['curlopts'];
  522. unset($opt['curlopts']);
  523. }
  524. // Debug mode
  525. if ($this->debug_mode)
  526. {
  527. $curlopts[CURLOPT_VERBOSE] = true;
  528. }
  529. // Set the curl options.
  530. if (count($curlopts))
  531. {
  532. $request->set_curlopts($curlopts);
  533. }
  534. // Do we have a verb?
  535. if (isset($opt['verb']))
  536. {
  537. $request->set_method($opt['verb']);
  538. $string_to_sign .= $opt['verb'] . "\n";
  539. }
  540. // Add headers and content when we have a body
  541. if (isset($opt['body']))
  542. {
  543. $request->set_body($opt['body']);
  544. $headers['Content-Length'] = strlen($opt['body']);
  545. if ($headers['Content-Type'] === 'application/x-www-form-urlencoded')
  546. {
  547. $headers['Content-Type'] = 'application/octet-stream';
  548. }
  549. if (!isset($opt['NoContentMD5']) || $opt['NoContentMD5'] !== true)
  550. {
  551. $headers['Content-MD5'] = $this->util->hex_to_base64(md5($opt['body']));
  552. }
  553. }
  554. // Handle query-string authentication
  555. if (isset($opt['preauth']) && (integer) $opt['preauth'] > 0)
  556. {
  557. unset($headers['Date']);
  558. $headers['Content-Type'] = '';
  559. $headers['Expires'] = is_int($opt['preauth']) ? $opt['preauth'] : strtotime($opt['preauth']);
  560. }
  561. // Sort headers
  562. uksort($headers, 'strnatcasecmp');
  563. // Add headers to request and compute the string to sign
  564. foreach ($headers as $header_key => $header_value)
  565. {
  566. // Strip linebreaks from header values as they're illegal and can allow for security issues
  567. $header_value = str_replace(array("\r", "\n"), '', $header_value);
  568. // Add the header if it has a value
  569. if ($header_value !== '')
  570. {
  571. $request->add_header($header_key, $header_value);
  572. }
  573. // Generate the string to sign
  574. if (
  575. strtolower($header_key) === 'content-md5' ||
  576. strtolower($header_key) === 'content-type' ||
  577. strtolower($header_key) === 'date' ||
  578. (strtolower($header_key) === 'expires' && isset($opt['preauth']) && (integer) $opt['preauth'] > 0)
  579. )
  580. {
  581. $string_to_sign .= $header_value . "\n";
  582. }
  583. elseif (substr(strtolower($header_key), 0, 6) === 'x-amz-')
  584. {
  585. $string_to_sign .= strtolower($header_key) . ':' . $header_value . "\n";
  586. }
  587. }
  588. // Add the signable resource location
  589. $string_to_sign .= ($this->resource_prefix ? $this->resource_prefix : '');
  590. $string_to_sign .= (($bucket === '' || $this->resource_prefix === '/' . $bucket) ? '' : ('/' . $bucket)) . $signable_resource . urldecode($signable_query_string);
  591. // Hash the AWS secret key and generate a signature for the request.
  592. $signature = base64_encode(hash_hmac('sha1', $string_to_sign, $this->secret_key, true));
  593. $request->add_header('Authorization', 'AWS ' . $this->key . ':' . $signature);
  594. // If we're generating a URL, return certain data to the calling method.
  595. if (isset($opt['preauth']) && (integer) $opt['preauth'] > 0)
  596. {
  597. return $this->request_url . $conjunction . 'AWSAccessKeyId=' . $this->key . '&Expires=' . $headers['Expires'] . '&Signature=' . rawurlencode($signature);
  598. }
  599. elseif (isset($opt['preauth']))
  600. {
  601. return $this->request_url;
  602. }
  603. /*%******************************************************************************************%*/
  604. // If our changes were temporary, reset them.
  605. if ($this->temporary_prefix)
  606. {
  607. $this->temporary_prefix = false;
  608. $this->resource_prefix = null;
  609. }
  610. // Manage the (newer) batch request API or the (older) returnCurlHandle setting.
  611. if ($this->use_batch_flow)
  612. {
  613. $handle = $request->prep_request();
  614. $this->batch_object->add($handle);
  615. $this->use_batch_flow = false;
  616. return $handle;
  617. }
  618. elseif (isset($opt['returnCurlHandle']) && $opt['returnCurlHandle'] === true)
  619. {
  620. return $request->prep_request();
  621. }
  622. // Send!
  623. $request->send_request();
  624. // Prepare the response
  625. $headers = $request->get_response_header();
  626. $headers['x-aws-request-url'] = $this->request_url;
  627. $headers['x-aws-redirects'] = $redirects;
  628. $headers['x-aws-stringtosign'] = $string_to_sign;
  629. $headers['x-aws-requestheaders'] = $request->request_headers;
  630. // Did we have a request body?
  631. if (isset($opt['body']))
  632. {
  633. $headers['x-aws-requestbody'] = $opt['body'];
  634. }
  635. $data = new $this->response_class($headers, $this->parse_callback($request->get_response_body()), $request->get_response_code());
  636. // Did Amazon tell us to redirect? Typically happens for multiple rapid requests EU datacenters.
  637. // @see: http://docs.amazonwebservices.com/AmazonS3/latest/dev/Redirects.html
  638. // @codeCoverageIgnoreStart
  639. if ((integer) $request->get_response_code() === 307) // Temporary redirect to new endpoint.
  640. {
  641. $data = $this->authenticate($bucket, $opt, $headers['location'], ++$redirects);
  642. }
  643. // Was it Amazon's fault the request failed? Retry the request until we reach $max_retries.
  644. elseif ((integer) $request->get_response_code() === 500 || (integer) $request->get_response_code() === 503)
  645. {
  646. if ($redirects <= $this->max_retries)
  647. {
  648. // Exponential backoff
  649. $delay = (integer) (pow(4, $redirects) * 100000);
  650. usleep($delay);
  651. $data = $this->authenticate($bucket, $opt, null, ++$redirects);
  652. }
  653. }
  654. // @codeCoverageIgnoreEnd
  655. // Return!
  656. return $data;
  657. }
  658. /**
  659. * Validates whether or not the specified Amazon S3 bucket name is valid for DNS-style access. This
  660. * method is leveraged by any method that creates buckets.
  661. *
  662. * @param string $bucket (Required) The name of the bucket to validate.
  663. * @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.
  664. */
  665. public function validate_bucketname_create($bucket)
  666. {
  667. // list_buckets() uses this. Let it pass.
  668. if ($bucket === '') return true;
  669. if (
  670. ($bucket === null || $bucket === false) || // Must not be null or false
  671. preg_match('/[^(a-z0-9\-\.)]/', $bucket) || // Must be in the lowercase Roman alphabet, period or hyphen
  672. !preg_match('/^([a-z]|\d)/', $bucket) || // Must start with a number or letter
  673. !(strlen($bucket) >= 3 && strlen($bucket) <= 63) || // Must be between 3 and 63 characters long
  674. (strpos($bucket, '..') !== false) || // Bucket names cannot contain two, adjacent periods
  675. (strpos($bucket, '-.') !== false) || // Bucket names cannot contain dashes next to periods
  676. (strpos($bucket, '.-') !== false) || // Bucket names cannot contain dashes next to periods
  677. preg_match('/(-|\.)$/', $bucket) || // Bucket names should not end with a dash or period
  678. preg_match('/^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/', $bucket) // Must not be formatted as an IP address
  679. ) return false;
  680. return true;
  681. }
  682. /**
  683. * Validates whether or not the specified Amazon S3 bucket name is valid for path-style access. This
  684. * method is leveraged by any method that reads from buckets.
  685. *
  686. * @param string $bucket (Required) The name of the bucket to validate.
  687. * @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.
  688. */
  689. public function validate_bucketname_support($bucket)
  690. {
  691. // list_buckets() uses this. Let it pass.
  692. if ($bucket === '') return true;
  693. // Validate
  694. if (
  695. ($bucket === null || $bucket === false) || // Must not be null or false
  696. preg_match('/[^(a-z0-9_\-\.)]/i', $bucket) || // Must be in the Roman alphabet, period, hyphen or underscore
  697. !preg_match('/^([a-z]|\d)/i', $bucket) || // Must start with a number or letter
  698. !(strlen($bucket) >= 3 && strlen($bucket) <= 255) || // Must be between 3 and 255 characters long
  699. preg_match('/^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/', $bucket) // Must not be formatted as an IP address
  700. ) return false;
  701. return true;
  702. }
  703. /**
  704. * The callback function that is executed when the cache doesn't exist or has expired. The response of
  705. * this method is cached. Accepts identical parameters as the <authenticate()> method. Never call this
  706. * method directly -- it is used internally by the caching system.
  707. *
  708. * @param string $bucket (Required) The name of the bucket to use.
  709. * @param array $opt (Optional) An associative array of parameters for authenticating. See the individual methods for allowed keys.
  710. * @param string $location (Optional) Used internally by this method when Amazon S3 returns a redirect code and needs to call itself recursively.
  711. * @param integer $redirects (Optional) Used internally by this method when Amazon S3 returns a redirect code and needs to call itself recursively.
  712. * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
  713. */
  714. public function cache_callback($bucket, $opt = null, $location = null, $redirects = 0)
  715. {
  716. // Disable the cache flow since it's already been handled.
  717. $this->use_cache_flow = false;
  718. // Make the request
  719. $response = $this->authenticate($bucket, $opt, $location, $redirects);
  720. if (isset($response->body) && ($response->body instanceof SimpleXMLElement))
  721. {
  722. $response->body = $response->body->asXML();
  723. }
  724. return $response;
  725. }
  726. /*%******************************************************************************************%*/
  727. // SETTERS
  728. /**
  729. * Sets the region to use for subsequent Amazon S3 operations. This will also reset any prior use of
  730. * <enable_path_style()>.
  731. *
  732. * @param string $region (Required) The region to use for subsequent Amazon S3 operations. [Allowed values: `AmazonS3::REGION_US_E1 `, `AmazonS3::REGION_US_W1`, `AmazonS3::REGION_EU_W1`, `AmazonS3::REGION_APAC_SE1`, `AmazonS3::REGION_APAC_NE1`]
  733. * @return $this A reference to the current instance.
  734. */
  735. public function set_region($region)
  736. {
  737. switch ($region)
  738. {
  739. case self::REGION_US_E1: // Northern Virginia
  740. $this->set_hostname(self::DEFAULT_URL);
  741. $this->enable_path_style(false);
  742. break;
  743. case self::REGION_EU_W1: // Ireland
  744. $this->set_hostname('s3-eu-west-1.amazonaws.com');
  745. $this->enable_path_style(); // Always use path-style access for EU endpoint.
  746. break;
  747. case self::REGION_US_W1: // Northern California
  748. case self::REGION_APAC_SE1: // Singapore
  749. case self::REGION_APAC_NE1: // Japan
  750. default:
  751. $this->set_hostname('s3-' . $region . '.amazonaws.com');
  752. $this->enable_path_style(false);
  753. break;
  754. // @codeCoverageIgnoreStart
  755. }
  756. // @codeCoverageIgnoreEnd
  757. return $this;
  758. }
  759. /**
  760. * Sets the virtual host to use in place of the default `bucket.s3.amazonaws.com` domain.
  761. *
  762. * @param string $vhost (Required) The virtual host to use in place of the default `bucket.s3.amazonaws.com` domain.
  763. * @return $this A reference to the current instance.
  764. * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/VirtualHosting.html Virtual Hosting of Buckets
  765. */
  766. public function set_vhost($vhost)
  767. {
  768. $this->vhost = $vhost;
  769. return $this;
  770. }
  771. /**
  772. * Enables the use of the older path-style URI access for all requests.
  773. *
  774. * @param string $style (Optional) Whether or not to enable path-style URI access for all requests. The default value is <code>true</code>.
  775. * @return $this A reference to the current instance.
  776. */
  777. public function enable_path_style($style = true)
  778. {
  779. $this->path_style = $style;
  780. return $this;
  781. }
  782. /*%******************************************************************************************%*/
  783. // BUCKET METHODS
  784. /**
  785. * Creates an Amazon S3 bucket.
  786. *
  787. * Every object stored in Amazon S3 is contained in a bucket. Buckets partition the namespace of
  788. * objects stored in Amazon S3 at the top level. in a bucket, any name can be used for objects.
  789. * However, bucket names must be unique across all of Amazon S3.
  790. *
  791. * @param string $bucket (Required) The name of the bucket to create.
  792. * @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`]
  793. * @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>]. The default value is <ACL_PRIVATE>.
  794. * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
  795. * <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>
  796. * <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>
  797. * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
  798. * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/UsingBucket.html Working with Amazon S3 Buckets
  799. */
  800. public function create_bucket($bucket, $region, $acl = self::ACL_PRIVATE, $opt = null)
  801. {
  802. // If the bucket contains uppercase letters...
  803. if (preg_match('/[A-Z]/', $bucket))
  804. {
  805. // Throw a warning
  806. trigger_error('Since DNS-valid bucket names cannot contain uppercase characters, "' . $bucket . '" has been automatically converted to "' . strtolower($bucket) . '"', E_USER_WARNING);
  807. // Force the bucketname to lowercase
  808. $bucket = strtolower($bucket);
  809. }
  810. // Validate the S3 bucket name for creation
  811. if (!$this->validate_bucketname_create($bucket))
  812. {
  813. // @codeCoverageIgnoreStart
  814. throw new S3_Exception('"' . $bucket . '" is not DNS-valid (i.e., <bucketname>.s3.amazonaws.com), and cannot be used as an S3 bucket name. Review "Bucket Restrictions and Limitations" in the S3 Developer Guide for more information.');
  815. // @codeCoverageIgnoreEnd
  816. }
  817. if (!$opt) $opt = array();
  818. $opt['verb'] = 'PUT';
  819. $opt['headers'] = array(
  820. 'Content-Type' => 'application/xml',
  821. 'x-amz-acl' => $acl
  822. );
  823. // Defaults
  824. $this->set_region($region);
  825. $xml = simplexml_load_string($this->base_location_constraint);
  826. switch ($region)
  827. {
  828. case self::REGION_US_E1: // Northern Virginia
  829. $opt['body'] = '';
  830. break;
  831. case self::REGION_EU_W1: // Ireland
  832. $this->enable_path_style(); // DNS-style doesn't seem to work for creation, only in EU. Switch over to path-style.
  833. $xml->LocationConstraint = $region;
  834. $opt['body'] = $xml->asXML();
  835. break;
  836. case self::REGION_US_W1: // Northern California
  837. case self::REGION_APAC_SE1: // Singapore
  838. case self::REGION_APAC_NE1: // Japan
  839. default:
  840. $xml->LocationConstraint = $region;
  841. $opt['body'] = $xml->asXML();
  842. break;
  843. // @codeCoverageIgnoreStart
  844. }
  845. // @codeCoverageIgnoreEnd
  846. $response = $this->authenticate($bucket, $opt);
  847. return $response;
  848. }
  849. /**
  850. * Gets the region in which the specified Amazon S3 bucket is located.
  851. *
  852. * @param string $bucket (Required) The name of the bucket to use.
  853. * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
  854. * <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>
  855. * <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>
  856. * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
  857. */
  858. public function get_bucket_region($bucket, $opt = null)
  859. {
  860. // Add this to our request
  861. if (!$opt) $opt = array();
  862. $opt['verb'] = 'GET';
  863. $opt['sub_resource'] = 'location';
  864. // Authenticate to S3
  865. $response = $this->authenticate($bucket, $opt);
  866. if ($response->isOK())
  867. {
  868. // Handle body
  869. $response->body = (string) $response->body;
  870. }
  871. return $response;
  872. }
  873. /**
  874. * Gets the HTTP headers for the specified Amazon S3 bucket.
  875. *
  876. * @param string $bucket (Required) The name of the bucket to use.
  877. * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
  878. * <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>
  879. * <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>
  880. * <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>
  881. * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
  882. */
  883. public function get_bucket_headers($bucket, $opt = null)
  884. {
  885. if (!$opt) $opt = array();
  886. $opt['verb'] = 'HEAD';
  887. return $this->authenticate($bucket, $opt);
  888. }
  889. /**
  890. * Deletes a bucket from an Amazon S3 account. A bucket must be empty before the bucket itself can be deleted.
  891. *
  892. * @param string $bucket (Required) The name of the bucket to use.
  893. * @param boolean $force (Optional) Whether to force-delete the bucket and all of its contents. The default value is <code>false</code>.
  894. * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
  895. * <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>
  896. * <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>
  897. * @return mixed A <CFResponse> object if the bucket was deleted successfully. Returns boolean <code>false</code> if otherwise.
  898. */
  899. public function delete_bucket($bucket, $force = false, $opt = null)
  900. {
  901. // Set default value
  902. $success = true;
  903. if ($force)
  904. {
  905. // Delete all of the items from the bucket.
  906. $success = $this->delete_all_object_versions($bucket);
  907. }
  908. // As long as we were successful...
  909. if ($success)
  910. {
  911. if (!$opt) $opt = array();
  912. $opt['verb'] = 'DELETE';
  913. return $this->authenticate($bucket, $opt);
  914. }
  915. // @codeCoverageIgnoreStart
  916. return false;
  917. // @codeCoverageIgnoreEnd
  918. }
  919. /**
  920. * Gets a list of all buckets contained in the caller's Amazon S3 account.
  921. *
  922. * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
  923. * <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>
  924. * <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>
  925. * <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>
  926. * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
  927. */
  928. public function list_buckets($opt = null)
  929. {
  930. if (!$opt) $opt = array();
  931. $opt['verb'] = 'GET';
  932. return $this->authenticate('', $opt);
  933. }
  934. /**
  935. * Gets the access control list (ACL) settings for the specified Amazon S3 bucket.
  936. *
  937. * @param string $bucket (Required) The name of the bucket to use.
  938. * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
  939. * <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>
  940. * <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>
  941. * <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>
  942. * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
  943. * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAccessPolicy.html REST Access Control Policy
  944. */
  945. public function get_bucket_acl($bucket, $opt = null)
  946. {
  947. // Add this to our request
  948. if (!$opt) $opt = array();
  949. $opt['verb'] = 'GET';
  950. $opt['sub_resource'] = 'acl';
  951. // Authenticate to S3
  952. return $this->authenticate($bucket, $opt);
  953. }
  954. /**
  955. * Sets the access control list (ACL) settings for the specified Amazon S3 bucket.
  956. *
  957. * @param string $bucket (Required) The name of the bucket to use.
  958. * @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>.
  959. * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
  960. * <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>
  961. * <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>
  962. * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
  963. * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAccessPolicy.html REST Access Control Policy
  964. */
  965. public function set_bucket_acl($bucket, $acl = self::ACL_PRIVATE, $opt = null)
  966. {
  967. // Add this to our request
  968. if (!$opt) $opt = array();
  969. $opt['verb'] = 'PUT';
  970. $opt['sub_resource'] = 'acl';
  971. $opt['headers'] = array(
  972. 'Content-Type' => 'application/xml'
  973. );
  974. // Make sure these are defined.
  975. // @codeCoverageIgnoreStart
  976. if (!defined('AWS_CANONICAL_ID') || !defined('AWS_CANONICAL_NAME'))
  977. {
  978. // Fetch the data live.
  979. $canonical = $this->get_canonical_user_id();
  980. define('AWS_CANONICAL_ID', $canonical['id']);
  981. define('AWS_CANONICAL_NAME', $canonical['display_name']);
  982. }
  983. // @codeCoverageIgnoreEnd
  984. if (is_array($acl))
  985. {
  986. $opt['body'] = $this->generate_access_policy(AWS_CANONICAL_ID, AWS_CANONICAL_NAME, $acl);
  987. }
  988. else
  989. {
  990. $opt['body'] = '';
  991. $opt['headers']['x-amz-acl'] = $acl;
  992. }
  993. // Authenticate to S3
  994. return $this->authenticate($bucket, $opt);
  995. }
  996. /*%******************************************************************************************%*/
  997. // OBJECT METHODS
  998. /**
  999. * Creates an Amazon S3 object. After an Amazon S3 bucket is created, objects can be stored in it.
  1000. *
  1001. * Each standard object can hold up to 5 GB of data. When an object is stored in Amazon S3, the data is streamed
  1002. * to multiple storage servers in multiple data centers. This ensures the data remains available in the
  1003. * event of internal network or hardware failure.
  1004. *
  1005. * @param string $bucket (Required) The name of the bucket to use.
  1006. * @param string $filename (Required) The file name for the object.
  1007. * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
  1008. * <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>
  1009. * <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>
  1010. * <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>
  1011. * <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>
  1012. * <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>
  1013. * <li><code>encryption</code> - <code>string</code> - Optional - The algorithm to use for encrypting the object. [Allowed values: <code>AES256</code>]</li>
  1014. * <li><code>headers</code> - <code>array</code> - Optional - The standard HTTP headers to send along in the request.</li>
  1015. * <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>
  1016. * <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>
  1017. * <li><code>seekTo</code> - <code>integer</code> - Optional - The starting position in bytes within the file/stream to upload from.</li>
  1018. * <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>
  1019. * <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>
  1020. * <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>
  1021. * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
  1022. * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAccessPolicy.html REST Access Control Policy
  1023. */
  1024. public function create_object($bucket, $filename, $opt = null)
  1025. {
  1026. if (!$opt) $opt = array();
  1027. // Add this to our request
  1028. $opt['verb'] = 'PUT';
  1029. $opt['resource'] = $filename;
  1030. // Handle content length. Can also be passed as an HTTP header.
  1031. if (isset($opt['length']))
  1032. {
  1033. $opt['headers']['Content-Length'] = $opt['length'];
  1034. unset($opt['length']);
  1035. }
  1036. // Handle content type. Can also be passed as an HTTP header.
  1037. if (isset($opt['contentType']))
  1038. {
  1039. $opt['headers']['Content-Type'] = $opt['contentType'];
  1040. unset($opt['contentType']);
  1041. }
  1042. // Handle Access Control Lists. Can also be passed as an HTTP header.
  1043. if (isset($opt['acl']))
  1044. {
  1045. $opt['headers']['x-amz-acl'] = $opt['acl'];
  1046. unset($opt['acl']);
  1047. }
  1048. // Handle storage settings. Can also be passed as an HTTP header.
  1049. if (isset($opt['storage']))
  1050. {
  1051. $opt['headers']['x-amz-storage-class'] = $opt['storage'];
  1052. unset($opt['storage']);
  1053. }
  1054. // Handle encryption settings. Can also be passed as an HTTP header.
  1055. if (isset($opt['encryption']))
  1056. {
  1057. $opt['headers']['x-amz-server-side-encryption'] = $opt['encryption'];
  1058. unset($opt['encryption']);
  1059. }
  1060. // Handle meta tags. Can also be passed as an HTTP header.
  1061. if (isset($opt['meta']))
  1062. {
  1063. foreach ($opt['meta'] as $meta_key => $meta_value)
  1064. {
  1065. // e.g., `My Meta Header` is converted to `x-amz-meta-my-meta-header`.
  1066. $opt['headers']['x-amz-meta-' . strtolower(str_replace(' ', '-', $meta_key))] = $meta_value;
  1067. }
  1068. unset($opt['meta']);
  1069. }
  1070. // Authenticate to S3
  1071. return $this->authenticate($bucket, $opt);
  1072. }
  1073. /**
  1074. * Gets the contents of an Amazon S3 object in the specified bucket.
  1075. *
  1076. * @param string $bucket (Required) The name of the bucket to use.
  1077. * @param string $filename (Required) The file name for the object.
  1078. * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
  1079. * <li…

Large files files are truncated, but you can click here to view the full file