Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/*
3
 * Copyright 2010-2012 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
 
18
/*%******************************************************************************************%*/
19
// EXCEPTIONS
20
 
21
/**
22
 * Default S3 Exception.
23
 */
24
class S3_Exception extends Exception {}
25
 
26
 
27
/*%******************************************************************************************%*/
28
// MAIN CLASS
29
 
30
/**
31
 * Amazon S3 is a web service that enables you to store data in the cloud. You can then download the data
32
 * or use the data with other AWS services, such as Amazon Elastic Cloud Computer (EC2).
33
 *
34
 * Amazon Simple Storage Service (Amazon S3) is storage for the Internet. You can use Amazon S3 to store
35
 * and retrieve any amount of data at any time, from anywhere on the web. You can accomplish these tasks
36
 * using the AWS Management Console, which is a simple and intuitive web interface.
37
 *
38
 * To get the most out of Amazon S3, you need to understand a few simple concepts. Amazon S3 stores data
39
 * as objects in buckets. An object is comprised of a file and optionally any metadata that describes
40
 * that file.
41
 *
42
 * To store an object in Amazon S3, you upload the file you want to store to a bucket. When you upload a
43
 * file, you can set permissions on the object as well as any metadata.
44
 *
45
 * Buckets are the containers for objects. You can have one or more buckets. For each bucket, you can control
46
 * access to the bucket (who can create, delete, and list objects in the bucket), view access logs for the
47
 * bucket and its objects, and choose the geographical region where Amazon S3 will store the bucket and its
48
 * contents.
49
 *
50
 * Visit <http://aws.amazon.com/s3/> for more information.
51
 *
52
 * @version 2012.01.17
53
 * @license See the included NOTICE.md file for more information.
54
 * @copyright See the included NOTICE.md file for more information.
55
 * @link http://aws.amazon.com/s3/ Amazon Simple Storage Service
56
 * @link http://aws.amazon.com/documentation/s3/ Amazon Simple Storage Service documentation
57
 */
58
class AmazonS3 extends CFRuntime
59
{
60
	/*%******************************************************************************************%*/
61
	// REGIONAL ENDPOINTS
62
 
63
	/**
64
	 * Specify the queue URL for the US-Standard (Northern Virginia & Washington State) Region.
65
	 */
66
	const REGION_US_E1 = 's3.amazonaws.com';
67
 
68
	/**
69
	 * Specify the queue URL for the US-Standard (Northern Virginia & Washington State) Region.
70
	 */
71
	const REGION_VIRGINIA = self::REGION_US_E1;
72
 
73
	/**
74
	 * Specify the queue URL for the US-Standard (Northern Virginia & Washington State) Region.
75
	 */
76
	const REGION_US_STANDARD = self::REGION_US_E1;
77
 
78
	/**
79
	 * Specify the queue URL for the US-West 1 (Northern California) Region.
80
	 */
81
	const REGION_US_W1 = 's3-us-west-1.amazonaws.com';
82
 
83
	/**
84
	 * Specify the queue URL for the US-West 1 (Northern California) Region.
85
	 */
86
	const REGION_CALIFORNIA = self::REGION_US_W1;
87
 
88
	/**
89
	 * Specify the queue URL for the US-West 2 (Oregon) Region.
90
	 */
91
	const REGION_US_W2 = 's3-us-west-2.amazonaws.com';
92
 
93
	/**
94
	 * Specify the queue URL for the US-West 2 (Oregon) Region.
95
	 */
96
	const REGION_OREGON = self::REGION_US_W2;
97
 
98
	/**
99
	 * Specify the queue URL for the EU (Ireland) Region.
100
	 */
101
	const REGION_EU_W1 = 's3-eu-west-1.amazonaws.com';
102
 
103
	/**
104
	 * Specify the queue URL for the EU (Ireland) Region.
105
	 */
106
	const REGION_IRELAND = self::REGION_EU_W1;
107
 
108
	/**
109
	 * Specify the queue URL for the Asia Pacific (Singapore) Region.
110
	 */
111
	const REGION_APAC_SE1 = 's3-ap-southeast-1.amazonaws.com';
112
 
113
	/**
114
	 * Specify the queue URL for the Asia Pacific (Singapore) Region.
115
	 */
116
	const REGION_SINGAPORE = self::REGION_APAC_SE1;
117
 
118
	/**
119
	 * Specify the queue URL for the Asia Pacific (Japan) Region.
120
	 */
121
	const REGION_APAC_NE1 = 's3-ap-northeast-1.amazonaws.com';
122
 
123
	/**
124
	 * Specify the queue URL for the Asia Pacific (Japan) Region.
125
	 */
126
	const REGION_TOKYO = self::REGION_APAC_NE1;
127
 
128
	/**
129
	 * Specify the queue URL for the South America (Sao Paulo) Region.
130
	 */
131
	const REGION_SA_E1 = 's3-sa-east-1.amazonaws.com';
132
 
133
	/**
134
	 * Specify the queue URL for the South America (Sao Paulo) Region.
135
	 */
136
	const REGION_SAO_PAULO = self::REGION_SA_E1;
137
 
138
	/**
139
	 * Specify the queue URL for the United States GovCloud Region.
140
	 */
141
	const REGION_US_GOV1 = 's3-us-gov-west-1.amazonaws.com';
142
 
143
	/**
144
	 * Specify the queue URL for the United States GovCloud FIPS 140-2 Region.
145
	 */
146
	const REGION_US_GOV1_FIPS = 's3-fips-us-gov-west-1.amazonaws.com';
147
 
148
	/**
149
	 * The default endpoint.
150
	 */
151
	const DEFAULT_URL = self::REGION_US_E1;
152
 
153
 
154
	/*%******************************************************************************************%*/
155
	// REGIONAL WEBSITE ENDPOINTS
156
 
157
	/**
158
	 * Specify the queue URL for the US-Standard (Northern Virginia & Washington State) Website Region.
159
	 */
160
	const REGION_US_E1_WEBSITE = 's3-website-us-east-1.amazonaws.com';
161
 
162
	/**
163
	 * Specify the queue URL for the US-Standard (Northern Virginia & Washington State) Website Region.
164
	 */
165
	const REGION_VIRGINIA_WEBSITE = self::REGION_US_E1_WEBSITE;
166
 
167
	/**
168
	 * Specify the queue URL for the US-Standard (Northern Virginia & Washington State) Website Region.
169
	 */
170
	const REGION_US_STANDARD_WEBSITE = self::REGION_US_E1_WEBSITE;
171
 
172
	/**
173
	 * Specify the queue URL for the US-West 1 (Northern California) Website Region.
174
	 */
175
	const REGION_US_W1_WEBSITE = 's3-website-us-west-1.amazonaws.com';
176
 
177
	/**
178
	 * Specify the queue URL for the US-West 1 (Northern California) Website Region.
179
	 */
180
	const REGION_CALIFORNIA_WEBSITE = self::REGION_US_W1_WEBSITE;
181
 
182
	/**
183
	 * Specify the queue URL for the US-West 2 (Oregon) Website Region.
184
	 */
185
	const REGION_US_W2_WEBSITE = 's3-website-us-west-2.amazonaws.com';
186
 
187
	/**
188
	 * Specify the queue URL for the US-West 2 (Oregon) Website Region.
189
	 */
190
	const REGION_OREGON_WEBSITE = self::REGION_US_W2_WEBSITE;
191
 
192
	/**
193
	 * Specify the queue URL for the EU (Ireland) Website Region.
194
	 */
195
	const REGION_EU_W1_WEBSITE = 's3-website-eu-west-1.amazonaws.com';
196
 
197
	/**
198
	 * Specify the queue URL for the EU (Ireland) Website Region.
199
	 */
200
	const REGION_IRELAND_WEBSITE = self::REGION_EU_W1_WEBSITE;
201
 
202
	/**
203
	 * Specify the queue URL for the Asia Pacific (Singapore) Website Region.
204
	 */
205
	const REGION_APAC_SE1_WEBSITE = 's3-website-ap-southeast-1.amazonaws.com';
206
 
207
	/**
208
	 * Specify the queue URL for the Asia Pacific (Singapore) Website Region.
209
	 */
210
	const REGION_SINGAPORE_WEBSITE = self::REGION_APAC_SE1_WEBSITE;
211
 
212
	/**
213
	 * Specify the queue URL for the Asia Pacific (Japan) Website Region.
214
	 */
215
	const REGION_APAC_NE1_WEBSITE = 's3-website-ap-northeast-1.amazonaws.com';
216
 
217
	/**
218
	 * Specify the queue URL for the Asia Pacific (Japan) Website Region.
219
	 */
220
	const REGION_TOKYO_WEBSITE = self::REGION_APAC_NE1_WEBSITE;
221
 
222
	/**
223
	 * Specify the queue URL for the South America (Sao Paulo) Website Region.
224
	 */
225
	const REGION_SA_E1_WEBSITE = 's3-website-sa-east-1.amazonaws.com';
226
 
227
	/**
228
	 * Specify the queue URL for the South America (Sao Paulo) Website Region.
229
	 */
230
	const REGION_SAO_PAULO_WEBSITE = self::REGION_SA_E1_WEBSITE;
231
 
232
	/**
233
	 * Specify the queue URL for the United States GovCloud Website Region.
234
	 */
235
	const REGION_US_GOV1_WEBSITE = 's3-website-us-gov-west-1.amazonaws.com';
236
 
237
 
238
	/*%******************************************************************************************%*/
239
	// ACL
240
 
241
	/**
242
	 * ACL: Owner-only read/write.
243
	 */
244
	const ACL_PRIVATE = 'private';
245
 
246
	/**
247
	 * ACL: Owner read/write, public read.
248
	 */
249
	const ACL_PUBLIC = 'public-read';
250
 
251
	/**
252
	 * ACL: Public read/write.
253
	 */
254
	const ACL_OPEN = 'public-read-write';
255
 
256
	/**
257
	 * ACL: Owner read/write, authenticated read.
258
	 */
259
	const ACL_AUTH_READ = 'authenticated-read';
260
 
261
	/**
262
	 * ACL: Bucket owner read.
263
	 */
264
	const ACL_OWNER_READ = 'bucket-owner-read';
265
 
266
	/**
267
	 * ACL: Bucket owner full control.
268
	 */
269
	const ACL_OWNER_FULL_CONTROL = 'bucket-owner-full-control';
270
 
271
 
272
	/*%******************************************************************************************%*/
273
	// GRANTS
274
 
275
	/**
276
	 * When applied to a bucket, grants permission to list the bucket. When applied to an object, this
277
	 * grants permission to read the object data and/or metadata.
278
	 */
279
	const GRANT_READ = 'READ';
280
 
281
	/**
282
	 * When applied to a bucket, grants permission to create, overwrite, and delete any object in the
283
	 * bucket. This permission is not supported for objects.
284
	 */
285
	const GRANT_WRITE = 'WRITE';
286
 
287
	/**
288
	 * Grants permission to read the ACL for the applicable bucket or object. The owner of a bucket or
289
	 * object always has this permission implicitly.
290
	 */
291
	const GRANT_READ_ACP = 'READ_ACP';
292
 
293
	/**
294
	 * Gives permission to overwrite the ACP for the applicable bucket or object. The owner of a bucket
295
	 * or object always has this permission implicitly. Granting this permission is equivalent to granting
296
	 * FULL_CONTROL because the grant recipient can make any changes to the ACP.
297
	 */
298
	const GRANT_WRITE_ACP = 'WRITE_ACP';
299
 
300
	/**
301
	 * Provides READ, WRITE, READ_ACP, and WRITE_ACP permissions. It does not convey additional rights and
302
	 * is provided only for convenience.
303
	 */
304
	const GRANT_FULL_CONTROL = 'FULL_CONTROL';
305
 
306
 
307
	/*%******************************************************************************************%*/
308
	// USERS
309
 
310
	/**
311
	 * The "AuthenticatedUsers" group for access control policies.
312
	 */
313
	const USERS_AUTH = 'http://acs.amazonaws.com/groups/global/AuthenticatedUsers';
314
 
315
	/**
316
	 * The "AllUsers" group for access control policies.
317
	 */
318
	const USERS_ALL = 'http://acs.amazonaws.com/groups/global/AllUsers';
319
 
320
	/**
321
	 * The "LogDelivery" group for access control policies.
322
	 */
323
	const USERS_LOGGING = 'http://acs.amazonaws.com/groups/s3/LogDelivery';
324
 
325
 
326
	/*%******************************************************************************************%*/
327
	// PATTERNS
328
 
329
	/**
330
	 * PCRE: Match all items
331
	 */
332
	const PCRE_ALL = '/.*/i';
333
 
334
 
335
	/*%******************************************************************************************%*/
336
	// STORAGE
337
 
338
	/**
339
	 * Standard storage redundancy.
340
	 */
341
	const STORAGE_STANDARD = 'STANDARD';
342
 
343
	/**
344
	 * Reduced storage redundancy.
345
	 */
346
	const STORAGE_REDUCED = 'REDUCED_REDUNDANCY';
347
 
348
 
349
	/*%******************************************************************************************%*/
350
	// PROPERTIES
351
 
352
	/**
353
	 * The request URL.
354
	 */
355
	public $request_url;
356
 
357
	/**
358
	 * The virtual host setting.
359
	 */
360
	public $vhost;
361
 
362
	/**
363
	 * The base XML elements to use for access control policy methods.
364
	 */
365
	public $base_acp_xml;
366
 
367
	/**
368
	 * The base XML elements to use for creating buckets in regions.
369
	 */
370
	public $base_location_constraint;
371
 
372
	/**
373
	 * The base XML elements to use for logging methods.
374
	 */
375
	public $base_logging_xml;
376
 
377
	/**
378
	 * The base XML elements to use for notifications.
379
	 */
380
	public $base_notification_xml;
381
 
382
	/**
383
	 * The base XML elements to use for versioning.
384
	 */
385
	public $base_versioning_xml;
386
 
387
	/**
388
	 * The base XML elements to use for completing a multipart upload.
389
	 */
390
	public $complete_mpu_xml;
391
 
392
	/**
393
	 * The base XML elements to use for website support.
394
	 */
395
	public $website_config_xml;
396
 
397
	/**
398
	 * The base XML elements to use for multi-object delete support.
399
	 */
400
	public $multi_object_delete_xml;
401
 
402
	/**
403
	 * The base XML elements to use for object expiration support.
404
	 */
405
	public $object_expiration_xml;
406
 
407
	/**
408
	 * The DNS vs. Path-style setting.
409
	 */
410
	public $path_style = false;
411
 
412
	/**
413
	 * The state of whether the prefix change is temporary or permanent.
414
	 */
415
	public $temporary_prefix = false;
416
 
417
 
418
	/*%******************************************************************************************%*/
419
	// CONSTRUCTOR
420
 
421
	/**
422
	 * Constructs a new instance of <AmazonS3>.
423
	 *
424
	 * @param array $options (Optional) An associative array of parameters that can have the following keys: <ul>
425
	 * 	<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>
426
	 * 	<li><code>credentials</code> - <code>string</code> - Optional - The name of the credential set to use for authentication.</li>
427
	 * 	<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>
428
	 * 	<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>
429
	 * 	<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>
430
	 * 	<li><code>token</code> - <code>string</code> - Optional - An AWS session token.</li></ul>
431
	 * @return void
432
	 */
433
	public function __construct(array $options = array())
434
	{
435
		$this->vhost = null;
436
		$this->api_version = '2006-03-01';
437
		$this->hostname = self::DEFAULT_URL;
438
 
439
		$this->base_acp_xml             = '<?xml version="1.0" encoding="UTF-8"?><AccessControlPolicy xmlns="http://s3.amazonaws.com/doc/latest/"></AccessControlPolicy>';
440
		$this->base_location_constraint = '<?xml version="1.0" encoding="UTF-8"?><CreateBucketConfiguration xmlns="http://s3.amazonaws.com/doc/' . $this->api_version . '/"><LocationConstraint></LocationConstraint></CreateBucketConfiguration>';
441
		$this->base_logging_xml         = '<?xml version="1.0" encoding="utf-8"?><BucketLoggingStatus xmlns="http://doc.s3.amazonaws.com/' . $this->api_version . '"></BucketLoggingStatus>';
442
		$this->base_notification_xml    = '<?xml version="1.0" encoding="utf-8"?><NotificationConfiguration></NotificationConfiguration>';
443
		$this->base_versioning_xml      = '<?xml version="1.0" encoding="utf-8"?><VersioningConfiguration xmlns="http://s3.amazonaws.com/doc/' . $this->api_version . '/"></VersioningConfiguration>';
444
		$this->complete_mpu_xml         = '<?xml version="1.0" encoding="utf-8"?><CompleteMultipartUpload></CompleteMultipartUpload>';
445
		$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>';
446
		$this->multi_object_delete_xml  = '<?xml version="1.0" encoding="utf-8"?><Delete></Delete>';
447
		$this->object_expiration_xml    = '<?xml version="1.0" encoding="utf-8"?><LifecycleConfiguration></LifecycleConfiguration>';
448
 
449
		parent::__construct($options);
450
	}
451
 
452
 
453
	/*%******************************************************************************************%*/
454
	// AUTHENTICATION
455
 
456
	/**
457
	 * Authenticates a connection to Amazon S3. Do not use directly unless implementing custom methods for
458
	 * this class.
459
	 *
460
	 * @param string $operation (Required) The name of the bucket to operate on (S3 Only).
461
	 * @param array $payload (Required) An associative array of parameters for authenticating. See inline comments for allowed keys.
462
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
463
	 * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/S3_Authentication.html REST authentication
464
	 */
465
	public function authenticate($operation, $payload)
466
	{
467
		/*
468
		 * Overriding or extending this class? You can pass the following "magic" keys into $opt.
469
		 *
470
		 * ## verb, resource, sub_resource and query_string ##
471
		 * 	<verb> /<resource>?<sub_resource>&<query_string>
472
		 * 	GET /filename.txt?versions&prefix=abc&max-items=1
473
		 *
474
		 * ## versionId, uploadId, partNumber, response-* ##
475
		 * 	These don't follow the same rules as above, in that the they needs to be signed, while
476
		 * 	other query_string values do not.
477
		 *
478
		 * ## curlopts ##
479
		 * 	These values get passed directly to the cURL methods in RequestCore.
480
		 *
481
		 * ## fileUpload, fileDownload, seekTo ##
482
		 * 	These are slightly modified and then passed to the cURL methods in RequestCore.
483
		 *
484
		 * ## headers ##
485
		 * 	$opt['headers'] is an array, whose keys are HTTP headers to be sent.
486
		 *
487
		 * ## body ##
488
		 * 	This is the request body that is sent to the server via PUT/POST.
489
		 *
490
		 * ## preauth ##
491
		 * 	This is a hook that tells authenticate() to generate a pre-authenticated URL.
492
		 *
493
		 * ## returnCurlHandle ##
494
		 * 	Tells authenticate() to return the cURL handle for the request instead of executing it.
495
		 */
496
 
497
		// Rename variables (to overcome inheritence issues)
498
		$bucket = $operation;
499
		$opt = $payload;
500
 
501
		// Validate the S3 bucket name
502
		if (!$this->validate_bucketname_support($bucket))
503
		{
504
			// @codeCoverageIgnoreStart
505
			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.');
506
			// @codeCoverageIgnoreEnd
507
		}
508
 
509
		// Die if $opt isn't set.
510
		if (!$opt) return false;
511
 
512
		$method_arguments = func_get_args();
513
 
514
		// Use the caching flow to determine if we need to do a round-trip to the server.
515
		if ($this->use_cache_flow)
516
		{
517
			// Generate an identifier specific to this particular set of arguments.
518
			$cache_id = $this->key . '_' . get_class($this) . '_' . $bucket . '_' . sha1(serialize($method_arguments));
519
 
520
			// Instantiate the appropriate caching object.
521
			$this->cache_object = new $this->cache_class($cache_id, $this->cache_location, $this->cache_expires, $this->cache_compress);
522
 
523
			if ($this->delete_cache)
524
			{
525
				$this->use_cache_flow = false;
526
				$this->delete_cache = false;
527
				return $this->cache_object->delete();
528
			}
529
 
530
			// Invoke the cache callback function to determine whether to pull data from the cache or make a fresh request.
531
			$data = $this->cache_object->response_manager(array($this, 'cache_callback'), $method_arguments);
532
 
533
			if ($this->parse_the_response)
534
			{
535
				// Parse the XML body
536
				$data = $this->parse_callback($data);
537
			}
538
 
539
			// End!
540
			return $data;
541
		}
542
 
543
		// If we haven't already set a resource prefix...
544
		if (!$this->resource_prefix || $this->path_style)
545
		{
546
			// And if the bucket name isn't DNS-valid...
547
			if (!$this->validate_bucketname_create($bucket))
548
			{
549
				// Fall back to the older path-style URI
550
				$this->set_resource_prefix('/' . $bucket);
551
				$this->temporary_prefix = true;
552
			}
553
			elseif ($this->path_style)
554
			{
555
				// Fall back to the older path-style URI
556
				$this->set_resource_prefix('/' . $bucket);
557
			}
558
		}
559
 
560
		// Determine hostname
561
		$scheme = $this->use_ssl ? 'https://' : 'http://';
562
		if ($this->resource_prefix || $this->path_style) // Use bucket-in-path method.
563
		{
564
			$hostname = $this->hostname . $this->resource_prefix . (($bucket === '' || $this->resource_prefix === '/' . $bucket) ? '' : ('/' . $bucket));
565
		}
566
		else
567
		{
568
			$hostname = $this->vhost ? $this->vhost : (($bucket === '') ? $this->hostname : ($bucket . '.') . $this->hostname);
569
		}
570
 
571
		// Get the UTC timestamp in RFC 2616 format
572
		$date = gmdate(CFUtilities::DATE_FORMAT_RFC2616, time());
573
 
574
		// Storage for request parameters.
575
		$resource = '';
576
		$sub_resource = '';
577
		$querystringparams = array();
578
		$signable_querystringparams = array();
579
		$string_to_sign = '';
580
		$headers = array(
581
			'Content-MD5' => '',
582
			'Content-Type' => 'application/x-www-form-urlencoded',
583
			'Date' => $date
584
		);
585
 
586
		/*%******************************************************************************************%*/
587
 
588
		// Do we have an authentication token?
589
		if ($this->auth_token)
590
		{
591
			$headers['X-Amz-Security-Token'] = $this->auth_token;
592
		}
593
 
594
		// Handle specific resources
595
		if (isset($opt['resource']))
596
		{
597
			$resource .= $opt['resource'];
598
		}
599
 
600
		// Merge query string values
601
		if (isset($opt['query_string']))
602
		{
603
			$querystringparams = array_merge($querystringparams, $opt['query_string']);
604
		}
605
		$query_string = $this->util->to_query_string($querystringparams);
606
 
607
		// Merge the signable query string values. Must be alphabetical.
608
		$signable_list = array(
609
			'partNumber',
610
			'response-cache-control',
611
			'response-content-disposition',
612
			'response-content-encoding',
613
			'response-content-language',
614
			'response-content-type',
615
			'response-expires',
616
			'uploadId',
617
			'versionId'
618
		);
619
		foreach ($signable_list as $item)
620
		{
621
			if (isset($opt[$item]))
622
			{
623
				$signable_querystringparams[$item] = $opt[$item];
624
			}
625
		}
626
		$signable_query_string = $this->util->to_query_string($signable_querystringparams);
627
 
628
		// Merge the HTTP headers
629
		if (isset($opt['headers']))
630
		{
631
			$headers = array_merge($headers, $opt['headers']);
632
		}
633
 
634
		// Compile the URI to request
635
		$conjunction = '?';
636
		$signable_resource = '/' . str_replace('%2F', '/', rawurlencode($resource));
637
		$non_signable_resource = '';
638
 
639
		if (isset($opt['sub_resource']))
640
		{
641
			$signable_resource .= $conjunction . rawurlencode($opt['sub_resource']);
642
			$conjunction = '&';
643
		}
644
		if ($signable_query_string !== '')
645
		{
646
			$signable_query_string = $conjunction . $signable_query_string;
647
			$conjunction = '&';
648
		}
649
		if ($query_string !== '')
650
		{
651
			$non_signable_resource .= $conjunction . $query_string;
652
			$conjunction = '&';
653
		}
654
		$this->request_url = $scheme . $hostname . $signable_resource . $signable_query_string . $non_signable_resource;
655
 
656
		if (isset($opt['location']))
657
		{
658
			$this->request_url = $opt['location'];
659
		}
660
 
661
		// Gather information to pass along to other classes.
662
		$helpers = array(
663
			'utilities' => $this->utilities_class,
664
			'request' => $this->request_class,
665
			'response' => $this->response_class,
666
		);
667
 
668
		// Instantiate the request class
669
		$request = new $this->request_class($this->request_url, $this->proxy, $helpers, $this->credentials);
670
 
671
		// Update RequestCore settings
672
		$request->request_class = $this->request_class;
673
		$request->response_class = $this->response_class;
674
		$request->ssl_verification = $this->ssl_verification;
675
 
676
		// Pass along registered stream callbacks
677
		if ($this->registered_streaming_read_callback)
678
		{
679
			$request->register_streaming_read_callback($this->registered_streaming_read_callback);
680
		}
681
 
682
		if ($this->registered_streaming_write_callback)
683
		{
684
			$request->register_streaming_write_callback($this->registered_streaming_write_callback);
685
		}
686
 
687
		// Streaming uploads
688
		if (isset($opt['fileUpload']))
689
		{
690
			if (is_resource($opt['fileUpload']))
691
			{
692
				// Determine the length to read from the stream
693
				$length = null; // From current position until EOF by default, size determined by set_read_stream()
694
 
695
				if (isset($headers['Content-Length']))
696
				{
697
					$length = $headers['Content-Length'];
698
				}
699
				elseif (isset($opt['seekTo']))
700
				{
701
					// Read from seekTo until EOF by default
702
					$stats = fstat($opt['fileUpload']);
703
 
704
					if ($stats && $stats['size'] >= 0)
705
					{
706
						$length = $stats['size'] - (integer) $opt['seekTo'];
707
					}
708
				}
709
 
710
				$request->set_read_stream($opt['fileUpload'], $length);
711
 
712
				if ($headers['Content-Type'] === 'application/x-www-form-urlencoded')
713
				{
714
					$headers['Content-Type'] = 'application/octet-stream';
715
				}
716
			}
717
			else
718
			{
719
				$request->set_read_file($opt['fileUpload']);
720
 
721
				// Determine the length to read from the file
722
				$length = $request->read_stream_size; // The file size by default
723
 
724
				if (isset($headers['Content-Length']))
725
				{
726
					$length = $headers['Content-Length'];
727
				}
728
				elseif (isset($opt['seekTo']) && isset($length))
729
				{
730
					// Read from seekTo until EOF by default
731
					$length -= (integer) $opt['seekTo'];
732
				}
733
 
734
				$request->set_read_stream_size($length);
735
 
736
				// Attempt to guess the correct mime-type
737
				if ($headers['Content-Type'] === 'application/x-www-form-urlencoded')
738
				{
739
					$extension = explode('.', $opt['fileUpload']);
740
					$extension = array_pop($extension);
741
					$mime_type = CFMimeTypes::get_mimetype($extension);
742
					$headers['Content-Type'] = $mime_type;
743
				}
744
			}
745
 
746
			$headers['Content-Length'] = $request->read_stream_size;
747
			$headers['Content-MD5'] = '';
748
		}
749
 
750
		// Handle streaming file offsets
751
		if (isset($opt['seekTo']))
752
		{
753
			// Pass the seek position to RequestCore
754
			$request->set_seek_position((integer) $opt['seekTo']);
755
		}
756
 
757
		// Streaming downloads
758
		if (isset($opt['fileDownload']))
759
		{
760
			if (is_resource($opt['fileDownload']))
761
			{
762
				$request->set_write_stream($opt['fileDownload']);
763
			}
764
			else
765
			{
766
				$request->set_write_file($opt['fileDownload']);
767
			}
768
		}
769
 
770
		$curlopts = array();
771
 
772
		// Set custom CURLOPT settings
773
		if (isset($opt['curlopts']))
774
		{
775
			$curlopts = $opt['curlopts'];
776
		}
777
 
778
		// Debug mode
779
		if ($this->debug_mode)
780
		{
781
			$curlopts[CURLOPT_VERBOSE] = true;
782
		}
783
 
784
		// Set the curl options.
785
		if (count($curlopts))
786
		{
787
			$request->set_curlopts($curlopts);
788
		}
789
 
790
		// Do we have a verb?
791
		if (isset($opt['verb']))
792
		{
793
			$request->set_method($opt['verb']);
794
			$string_to_sign .= $opt['verb'] . "\n";
795
		}
796
 
797
		// Add headers and content when we have a body
798
		if (isset($opt['body']))
799
		{
800
			$request->set_body($opt['body']);
801
			$headers['Content-Length'] = strlen($opt['body']);
802
 
803
			if ($headers['Content-Type'] === 'application/x-www-form-urlencoded')
804
			{
805
				$headers['Content-Type'] = 'application/octet-stream';
806
			}
807
 
808
			if (!isset($opt['NoContentMD5']) || $opt['NoContentMD5'] !== true)
809
			{
810
				$headers['Content-MD5'] = $this->util->hex_to_base64(md5($opt['body']));
811
			}
812
		}
813
 
814
		// Handle query-string authentication
815
		if (isset($opt['preauth']) && (integer) $opt['preauth'] > 0)
816
		{
817
			unset($headers['Date']);
818
			$headers['Content-Type'] = '';
819
			$headers['Expires'] = is_int($opt['preauth']) ? $opt['preauth'] : strtotime($opt['preauth']);
820
		}
821
 
822
		// Sort headers
823
		uksort($headers, 'strnatcasecmp');
824
 
825
		// Add headers to request and compute the string to sign
826
		foreach ($headers as $header_key => $header_value)
827
		{
828
			// Strip linebreaks from header values as they're illegal and can allow for security issues
829
			$header_value = str_replace(array("\r", "\n"), '', $header_value);
830
 
831
			// Add the header if it has a value
832
			if ($header_value !== '')
833
			{
834
				$request->add_header($header_key, $header_value);
835
			}
836
 
837
			// Generate the string to sign
838
			if (
839
				strtolower($header_key) === 'content-md5' ||
840
				strtolower($header_key) === 'content-type' ||
841
				strtolower($header_key) === 'date' ||
842
				(strtolower($header_key) === 'expires' && isset($opt['preauth']) && (integer) $opt['preauth'] > 0)
843
			)
844
			{
845
				$string_to_sign .= $header_value . "\n";
846
			}
847
			elseif (substr(strtolower($header_key), 0, 6) === 'x-amz-')
848
			{
849
				$string_to_sign .= strtolower($header_key) . ':' . $header_value . "\n";
850
			}
851
		}
852
 
853
		// Add the signable resource location
854
		$string_to_sign .= ($this->resource_prefix ? $this->resource_prefix : '');
855
		$string_to_sign .= (($bucket === '' || $this->resource_prefix === '/' . $bucket) ? '' : ('/' . $bucket)) . $signable_resource . urldecode($signable_query_string);
856
 
857
		// Hash the AWS secret key and generate a signature for the request.
858
		$signature = base64_encode(hash_hmac('sha1', $string_to_sign, $this->secret_key, true));
859
		$request->add_header('Authorization', 'AWS ' . $this->key . ':' . $signature);
860
 
861
		// If we're generating a URL, return certain data to the calling method.
862
		if (isset($opt['preauth']) && (integer) $opt['preauth'] > 0)
863
		{
864
			return $this->request_url . $conjunction . 'AWSAccessKeyId=' . $this->key . '&Expires=' . $headers['Expires'] . '&Signature=' . rawurlencode($signature);
865
		}
866
		elseif (isset($opt['preauth']))
867
		{
868
			return $this->request_url;
869
		}
870
 
871
		/*%******************************************************************************************%*/
872
 
873
		// If our changes were temporary, reset them.
874
		if ($this->temporary_prefix)
875
		{
876
			$this->temporary_prefix = false;
877
			$this->resource_prefix = null;
878
		}
879
 
880
		// Manage the (newer) batch request API or the (older) returnCurlHandle setting.
881
		if ($this->use_batch_flow)
882
		{
883
			$handle = $request->prep_request();
884
			$this->batch_object->add($handle);
885
			$this->use_batch_flow = false;
886
 
887
			return $handle;
888
		}
889
		elseif (isset($opt['returnCurlHandle']) && $opt['returnCurlHandle'] === true)
890
		{
891
			return $request->prep_request();
892
		}
893
 
894
		// Send!
895
		$request->send_request();
896
 
897
		// Prepare the response
898
		$headers = $request->get_response_header();
899
		$headers['x-aws-request-url'] = $this->request_url;
900
		$headers['x-aws-redirects'] = $this->redirects;
901
		$headers['x-aws-stringtosign'] = $string_to_sign;
902
		$headers['x-aws-requestheaders'] = $request->request_headers;
903
 
904
		// Did we have a request body?
905
		if (isset($opt['body']))
906
		{
907
			$headers['x-aws-requestbody'] = $opt['body'];
908
		}
909
 
910
		$data = new $this->response_class($headers, $this->parse_callback($request->get_response_body()), $request->get_response_code());
911
 
912
		// Did Amazon tell us to redirect? Typically happens for multiple rapid requests EU datacenters.
913
		// @see: http://docs.amazonwebservices.com/AmazonS3/latest/dev/Redirects.html
914
		// @codeCoverageIgnoreStart
915
		if ((integer) $request->get_response_code() === 307) // Temporary redirect to new endpoint.
916
		{
917
			$this->redirects++;
918
			$opt['location'] = $headers['location'];
919
			$data = $this->authenticate($bucket, $opt);
920
		}
921
 
922
		// Was it Amazon's fault the request failed? Retry the request until we reach $max_retries.
923
		elseif ((integer) $request->get_response_code() === 500 || (integer) $request->get_response_code() === 503)
924
		{
925
			if ($this->redirects <= $this->max_retries)
926
			{
927
				// Exponential backoff
928
				$delay = (integer) (pow(4, $this->redirects) * 100000);
929
				usleep($delay);
930
				$this->redirects++;
931
				$data = $this->authenticate($bucket, $opt);
932
			}
933
		}
934
		// @codeCoverageIgnoreEnd
935
 
936
		// Return!
937
		$this->redirects = 0;
938
		return $data;
939
	}
940
 
941
	/**
942
	 * Validates whether or not the specified Amazon S3 bucket name is valid for DNS-style access. This
943
	 * method is leveraged by any method that creates buckets.
944
	 *
945
	 * @param string $bucket (Required) The name of the bucket to validate.
946
	 * @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.
947
	 */
948
	public function validate_bucketname_create($bucket)
949
	{
950
		// list_buckets() uses this. Let it pass.
951
		if ($bucket === '') return true;
952
 
953
		if (
954
			($bucket === null || $bucket === false) ||                  // Must not be null or false
955
			preg_match('/[^(a-z0-9\-\.)]/', $bucket) ||                 // Must be in the lowercase Roman alphabet, period or hyphen
956
			!preg_match('/^([a-z]|\d)/', $bucket) ||                    // Must start with a number or letter
957
			!(strlen($bucket) >= 3 && strlen($bucket) <= 63) ||         // Must be between 3 and 63 characters long
958
			(strpos($bucket, '..') !== false) ||                        // Bucket names cannot contain two, adjacent periods
959
			(strpos($bucket, '-.') !== false) ||                        // Bucket names cannot contain dashes next to periods
960
			(strpos($bucket, '.-') !== false) ||                        // Bucket names cannot contain dashes next to periods
961
			preg_match('/(-|\.)$/', $bucket) ||                         // Bucket names should not end with a dash or period
962
			preg_match('/^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/', $bucket)    // Must not be formatted as an IP address
963
		) return false;
964
 
965
		return true;
966
	}
967
 
968
	/**
969
	 * Validates whether or not the specified Amazon S3 bucket name is valid for path-style access. This
970
	 * method is leveraged by any method that reads from buckets.
971
	 *
972
	 * @param string $bucket (Required) The name of the bucket to validate.
973
	 * @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.
974
	 */
975
	public function validate_bucketname_support($bucket)
976
	{
977
		// list_buckets() uses this. Let it pass.
978
		if ($bucket === '') return true;
979
 
980
		// Validate
981
		if (
982
			($bucket === null || $bucket === false) ||                  // Must not be null or false
983
			preg_match('/[^(a-z0-9_\-\.)]/i', $bucket) ||               // Must be in the Roman alphabet, period, hyphen or underscore
984
			!preg_match('/^([a-z]|\d)/i', $bucket) ||                   // Must start with a number or letter
985
			!(strlen($bucket) >= 3 && strlen($bucket) <= 255) ||        // Must be between 3 and 255 characters long
986
			preg_match('/^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/', $bucket)    // Must not be formatted as an IP address
987
		) return false;
988
 
989
		return true;
990
	}
991
 
992
	/*%******************************************************************************************%*/
993
	// SETTERS
994
 
995
	/**
996
	 * Sets the region to use for subsequent Amazon S3 operations. This will also reset any prior use of
997
	 * <enable_path_style()>.
998
	 *
999
	 * @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.
1000
	 * @return $this A reference to the current instance.
1001
	 */
1002
	public function set_region($region)
1003
	{
1004
		// @codeCoverageIgnoreStart
1005
		$this->set_hostname($region);
1006
 
1007
		switch ($region)
1008
		{
1009
			case self::REGION_US_E1: // Northern Virginia
1010
				$this->enable_path_style(false);
1011
				break;
1012
 
1013
			case self::REGION_EU_W1: // Ireland
1014
				$this->enable_path_style(); // Always use path-style access for EU endpoint.
1015
				break;
1016
 
1017
			default:
1018
				$this->enable_path_style(false);
1019
				break;
1020
 
1021
		}
1022
		// @codeCoverageIgnoreEnd
1023
 
1024
		return $this;
1025
	}
1026
 
1027
	/**
1028
	 * Sets the virtual host to use in place of the default `bucket.s3.amazonaws.com` domain.
1029
	 *
1030
	 * @param string $vhost (Required) The virtual host to use in place of the default `bucket.s3.amazonaws.com` domain.
1031
	 * @return $this A reference to the current instance.
1032
	 * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/VirtualHosting.html Virtual Hosting of Buckets
1033
	 */
1034
	public function set_vhost($vhost)
1035
	{
1036
		$this->vhost = $vhost;
1037
		return $this;
1038
	}
1039
 
1040
	/**
1041
	 * Enables the use of the older path-style URI access for all requests.
1042
	 *
1043
	 * @param string $style (Optional) Whether or not to enable path-style URI access for all requests. The default value is <code>true</code>.
1044
	 * @return $this A reference to the current instance.
1045
	 */
1046
	public function enable_path_style($style = true)
1047
	{
1048
		$this->path_style = $style;
1049
		return $this;
1050
	}
1051
 
1052
 
1053
	/*%******************************************************************************************%*/
1054
	// BUCKET METHODS
1055
 
1056
	/**
1057
	 * Creates an Amazon S3 bucket.
1058
	 *
1059
	 * Every object stored in Amazon S3 is contained in a bucket. Buckets partition the namespace of
1060
	 * objects stored in Amazon S3 at the top level. in a bucket, any name can be used for objects.
1061
	 * However, bucket names must be unique across all of Amazon S3.
1062
	 *
1063
	 * @param string $bucket (Required) The name of the bucket to create.
1064
	 * @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`]
1065
	 * @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>.
1066
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
1067
	 * 	<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>
1068
	 * 	<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>
1069
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
1070
	 * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/UsingBucket.html Working with Amazon S3 Buckets
1071
	 */
1072
	public function create_bucket($bucket, $region, $acl = self::ACL_PRIVATE, $opt = null)
1073
	{
1074
		// If the bucket contains uppercase letters...
1075
		if (preg_match('/[A-Z]/', $bucket))
1076
		{
1077
			// Throw a warning
1078
			trigger_error('Since DNS-valid bucket names cannot contain uppercase characters, "' . $bucket . '" has been automatically converted to "' . strtolower($bucket) . '"', E_USER_WARNING);
1079
 
1080
			// Force the bucketname to lowercase
1081
			$bucket = strtolower($bucket);
1082
		}
1083
 
1084
		// Validate the S3 bucket name for creation
1085
		if (!$this->validate_bucketname_create($bucket))
1086
		{
1087
			// @codeCoverageIgnoreStart
1088
			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.');
1089
			// @codeCoverageIgnoreEnd
1090
		}
1091
 
1092
		if (!$opt) $opt = array();
1093
		$opt['verb'] = 'PUT';
1094
		$opt['headers'] = array(
1095
			'Content-Type' => 'application/xml',
1096
			'x-amz-acl' => $acl
1097
		);
1098
 
1099
		// Defaults
1100
		$this->set_region($region);
1101
		$xml = simplexml_load_string($this->base_location_constraint);
1102
 
1103
		switch ($region)
1104
		{
1105
			case self::REGION_US_E1: // Northern Virginia
1106
				$opt['body'] = '';
1107
				break;
1108
 
1109
			case self::REGION_EU_W1:    // Ireland
1110
				$this->enable_path_style(); // DNS-style doesn't seem to work for creation, only in EU. Switch over to path-style.
1111
				$xml->LocationConstraint = 'EU';
1112
				$opt['body'] = $xml->asXML();
1113
				break;
1114
 
1115
			default:
1116
				$this->enable_path_style(false);
1117
				$xml->LocationConstraint = str_replace(array('s3-', '.amazonaws.com'), '', $region);
1118
				$opt['body'] = $xml->asXML();
1119
				break;
1120
 
1121
		// @codeCoverageIgnoreStart
1122
		}
1123
		// @codeCoverageIgnoreEnd
1124
 
1125
		$response = $this->authenticate($bucket, $opt);
1126
 
1127
		return $response;
1128
	}
1129
 
1130
	/**
1131
	 * Gets the region in which the specified Amazon S3 bucket is located.
1132
	 *
1133
	 * @param string $bucket (Required) The name of the bucket to use.
1134
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
1135
	 * 	<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>
1136
	 * 	<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>
1137
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
1138
	 */
1139
	public function get_bucket_region($bucket, $opt = null)
1140
	{
1141
		// Add this to our request
1142
		if (!$opt) $opt = array();
1143
		$opt['verb'] = 'GET';
1144
		$opt['sub_resource'] = 'location';
1145
 
1146
		// Authenticate to S3
1147
		$response = $this->authenticate($bucket, $opt);
1148
 
1149
		if ($response->isOK())
1150
		{
1151
			// Handle body
1152
			$response->body = (string) $response->body;
1153
		}
1154
 
1155
		return $response;
1156
	}
1157
 
1158
	/**
1159
	 * Gets the HTTP headers for the specified Amazon S3 bucket.
1160
	 *
1161
	 * @param string $bucket (Required) The name of the bucket to use.
1162
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
1163
	 * 	<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>
1164
	 * 	<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>
1165
	 * 	<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>
1166
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
1167
	 */
1168
	public function get_bucket_headers($bucket, $opt = null)
1169
	{
1170
		if (!$opt) $opt = array();
1171
		$opt['verb'] = 'HEAD';
1172
 
1173
		return $this->authenticate($bucket, $opt);
1174
	}
1175
 
1176
	/**
1177
	 * Deletes a bucket from an Amazon S3 account. A bucket must be empty before the bucket itself can be deleted.
1178
	 *
1179
	 * @param string $bucket (Required) The name of the bucket to use.
1180
	 * @param boolean $force (Optional) Whether to force-delete the bucket and all of its contents. The default value is <code>false</code>.
1181
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
1182
	 * 	<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>
1183
	 * 	<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>
1184
	 * @return mixed A <CFResponse> object if the bucket was deleted successfully. Returns boolean <code>false</code> if otherwise.
1185
	 */
1186
	public function delete_bucket($bucket, $force = false, $opt = null)
1187
	{
1188
		// Set default value
1189
		$success = true;
1190
 
1191
		if ($force)
1192
		{
1193
			// Delete all of the items from the bucket.
1194
			$success = $this->delete_all_object_versions($bucket);
1195
		}
1196
 
1197
		// As long as we were successful...
1198
		if ($success)
1199
		{
1200
			if (!$opt) $opt = array();
1201
			$opt['verb'] = 'DELETE';
1202
 
1203
			return $this->authenticate($bucket, $opt);
1204
		}
1205
 
1206
		// @codeCoverageIgnoreStart
1207
		return false;
1208
		// @codeCoverageIgnoreEnd
1209
	}
1210
 
1211
	/**
1212
	 * Gets a list of all buckets contained in the caller's Amazon S3 account.
1213
	 *
1214
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
1215
	 * 	<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>
1216
	 * 	<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>
1217
	 * 	<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>
1218
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
1219
	 */
1220
	public function list_buckets($opt = null)
1221
	{
1222
		if (!$opt) $opt = array();
1223
		$opt['verb'] = 'GET';
1224
 
1225
		return $this->authenticate('', $opt);
1226
	}
1227
 
1228
	/**
1229
	 * Gets the access control list (ACL) settings for the specified Amazon S3 bucket.
1230
	 *
1231
	 * @param string $bucket (Required) The name of the bucket to use.
1232
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
1233
	 * 	<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>
1234
	 * 	<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>
1235
	 * 	<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>
1236
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
1237
	 * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAccessPolicy.html REST Access Control Policy
1238
	 */
1239
	public function get_bucket_acl($bucket, $opt = null)
1240
	{
1241
		// Add this to our request
1242
		if (!$opt) $opt = array();
1243
		$opt['verb'] = 'GET';
1244
		$opt['sub_resource'] = 'acl';
1245
 
1246
		// Authenticate to S3
1247
		return $this->authenticate($bucket, $opt);
1248
	}
1249
 
1250
	/**
1251
	 * Sets the access control list (ACL) settings for the specified Amazon S3 bucket.
1252
	 *
1253
	 * @param string $bucket (Required) The name of the bucket to use.
1254
	 * @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>.
1255
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
1256
	 * 	<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>
1257
	 * 	<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>
1258
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
1259
	 * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAccessPolicy.html REST Access Control Policy
1260
	 */
1261
	public function set_bucket_acl($bucket, $acl = self::ACL_PRIVATE, $opt = null)
1262
	{
1263
		// Add this to our request
1264
		if (!$opt) $opt = array();
1265
		$opt['verb'] = 'PUT';
1266
		$opt['sub_resource'] = 'acl';
1267
		$opt['headers'] = array(
1268
			'Content-Type' => 'application/xml'
1269
		);
1270
 
1271
		// Make sure these are defined.
1272
		// @codeCoverageIgnoreStart
1273
		if (!$this->credentials->canonical_id || !$this->credentials->canonical_name)
1274
		{
1275
			// Fetch the data live.
1276
			$canonical = $this->get_canonical_user_id();
1277
			$this->credentials->canonical_id = $canonical['id'];
1278
			$this->credentials->canonical_name = $canonical['display_name'];
1279
		}
1280
		// @codeCoverageIgnoreEnd
1281
 
1282
		if (is_array($acl))
1283
		{
1284
			$opt['body'] = $this->generate_access_policy($this->credentials->canonical_id, $this->credentials->canonical_name, $acl);
1285
		}
1286
		else
1287
		{
1288
			$opt['body'] = '';
1289
			$opt['headers']['x-amz-acl'] = $acl;
1290
		}
1291
 
1292
		// Authenticate to S3
1293
		return $this->authenticate($bucket, $opt);
1294
	}
1295
 
1296
 
1297
	/*%******************************************************************************************%*/
1298
	// OBJECT METHODS
1299
 
1300
	/**
1301
	 * Creates an Amazon S3 object. After an Amazon S3 bucket is created, objects can be stored in it.
1302
	 *
1303
	 * Each standard object can hold up to 5 GB of data. When an object is stored in Amazon S3, the data is streamed
1304
	 * to multiple storage servers in multiple data centers. This ensures the data remains available in the
1305
	 * event of internal network or hardware failure.
1306
	 *
1307
	 * @param string $bucket (Required) The name of the bucket to use.
1308
	 * @param string $filename (Required) The file name for the object.
1309
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
1310
	 * 	<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>
1311
	 * 	<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>
1312
	 * 	<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>
1313
	 * 	<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>
1314
	 * 	<li><code>encryption</code> - <code>string</code> - Optional - The algorithm to use for encrypting the object. [Allowed values: <code>AES256</code>]</li>
1315
	 * 	<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>
1316
	 * 	<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>
1317
	 * 	<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>
1318
	 * 	<li><code>seekTo</code> - <code>integer</code> - Optional - The starting position in bytes within the file/stream to upload from.</li>
1319
	 * 	<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>
1320
	 * 	<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>
1321
	 * 	<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>
1322
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
1323
	 * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAccessPolicy.html REST Access Control Policy
1324
	 */
1325
	public function create_object($bucket, $filename, $opt = null)
1326
	{
1327
		if (!$opt) $opt = array();
1328
 
1329
		// Add this to our request
1330
		$opt['verb'] = 'PUT';
1331
		$opt['resource'] = $filename;
1332
 
1333
		// Handle content length. Can also be passed as an HTTP header.
1334
		if (isset($opt['length']))
1335
		{
1336
			$opt['headers']['Content-Length'] = $opt['length'];
1337
			unset($opt['length']);
1338
		}
1339
 
1340
		// Handle content type. Can also be passed as an HTTP header.
1341
		if (isset($opt['contentType']))
1342
		{
1343
			$opt['headers']['Content-Type'] = $opt['contentType'];
1344
			unset($opt['contentType']);
1345
		}
1346
 
1347
		// Handle Access Control Lists. Can also be passed as an HTTP header.
1348
		if (isset($opt['acl']))
1349
		{
1350
			$opt['headers']['x-amz-acl'] = $opt['acl'];
1351
			unset($opt['acl']);
1352
		}
1353
 
1354
		// Handle storage settings. Can also be passed as an HTTP header.
1355
		if (isset($opt['storage']))
1356
		{
1357
			$opt['headers']['x-amz-storage-class'] = $opt['storage'];
1358
			unset($opt['storage']);
1359
		}
1360
 
1361
		// Handle encryption settings. Can also be passed as an HTTP header.
1362
		if (isset($opt['encryption']))
1363
		{
1364
			$opt['headers']['x-amz-server-side-encryption'] = $opt['encryption'];
1365
			unset($opt['encryption']);
1366
		}
1367
 
1368
		// Handle meta tags. Can also be passed as an HTTP header.
1369
		if (isset($opt['meta']))
1370
		{
1371
			foreach ($opt['meta'] as $meta_key => $meta_value)
1372
			{
1373
				// e.g., `My Meta Header` is converted to `x-amz-meta-my-meta-header`.
1374
				$opt['headers']['x-amz-meta-' . strtolower(str_replace(' ', '-', $meta_key))] = $meta_value;
1375
			}
1376
			unset($opt['meta']);
1377
		}
1378
 
1379
		// Authenticate to S3
1380
		return $this->authenticate($bucket, $opt);
1381
	}
1382
 
1383
	/**
1384
	 * Gets the contents of an Amazon S3 object in the specified bucket.
1385
	 *
1386
	 * @param string $bucket (Required) The name of the bucket to use.
1387
	 * @param string $filename (Required) The file name for the object.
1388
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
1389
	 * 	<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>
1390
	 * 	<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>
1391
	 * 	<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>
1392
	 * 	<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>
1393
	 * 	<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>
1394
	 * 	<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>
1395
	 * 	<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>
1396
	 * 	<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>
1397
	 * 	<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>
1398
	 * 	<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>
1399
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
1400
	 */
1401
	public function get_object($bucket, $filename, $opt = null)
1402
	{
1403
		if (!$opt) $opt = array();
1404
		$this->parse_the_response = false;
1405
 
1406
		// Add this to our request
1407
		$opt['verb'] = 'GET';
1408
		$opt['resource'] = $filename;
1409
 
1410
		if (!isset($opt['headers']) || !is_array($opt['headers']))
1411
		{
1412
			$opt['headers'] = array();
1413
		}
1414
 
1415
		if (isset($opt['lastmodified']))
1416
		{
1417
			$opt['headers']['If-Modified-Since'] = $opt['lastmodified'];
1418
		}
1419
 
1420
		if (isset($opt['etag']))
1421
		{
1422
			$opt['headers']['If-None-Match'] = $opt['etag'];
1423
		}
1424
 
1425
		// Partial content range
1426
		if (isset($opt['range']))
1427
		{
1428
			$opt['headers']['Range'] = 'bytes=' . $opt['range'];
1429
		}
1430
 
1431
		// GET responses
1432
		if (isset($opt['response']))
1433
		{
1434
			foreach ($opt['response'] as $key => $value)
1435
			{
1436
				$opt['response-' . $key] = $value;
1437
				unset($opt['response'][$key]);
1438
			}
1439
		}
1440
 
1441
		// Authenticate to S3
1442
		return $this->authenticate($bucket, $opt);
1443
	}
1444
 
1445
	/**
1446
	 * Gets the HTTP headers for the specified Amazon S3 object.
1447
	 *
1448
	 * @param string $bucket (Required) The name of the bucket to use.
1449
	 * @param string $filename (Required) The file name for the object.
1450
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
1451
	 * 	<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>
1452
	 * 	<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>
1453
	 * 	<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>
1454
	 * 	<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>
1455
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
1456
	 */
1457
	public function get_object_headers($bucket, $filename, $opt = null)
1458
	{
1459
		// Add this to our request
1460
		if (!$opt) $opt = array();
1461
		$opt['verb'] = 'HEAD';
1462
		$opt['resource'] = $filename;
1463
 
1464
		// Authenticate to S3
1465
		return $this->authenticate($bucket, $opt);
1466
	}
1467
 
1468
	/**
1469
	 * Deletes an Amazon S3 object from the specified bucket.
1470
	 *
1471
	 * @param string $bucket (Required) The name of the bucket to use.
1472
	 * @param string $filename (Required) The file name for the object.
1473
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
1474
	 * 	<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>
1475
	 * 	<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>
1476
	 * 	<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>
1477
	 * 	<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>
1478
	 * 	<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>
1479
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
1480
	 * @link http://aws.amazon.com/mfa/ Multi-Factor Authentication
1481
	 */
1482
	public function delete_object($bucket, $filename, $opt = null)
1483
	{
1484
		// Add this to our request
1485
		if (!$opt) $opt = array();
1486
		$opt['verb'] = 'DELETE';
1487
		$opt['resource'] = $filename;
1488
 
1489
		// Enable MFA delete?
1490
		// @codeCoverageIgnoreStart
1491
		if (isset($opt['MFASerial']) && isset($opt['MFAToken']))
1492
		{
1493
			$opt['headers'] = array(
1494
				'x-amz-mfa' => ($opt['MFASerial'] . ' ' . $opt['MFAToken'])
1495
			);
1496
		}
1497
		// @codeCoverageIgnoreEnd
1498
 
1499
		// Authenticate to S3
1500
		return $this->authenticate($bucket, $opt);
1501
	}
1502
 
1503
	/**
1504
	 * Deletes two or more specified Amazon S3 objects from the specified bucket.
1505
	 *
1506
	 * @param string $bucket (Required) The name of the bucket to use.
1507
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
1508
	 * 	<li><code>objects</code> - <code>array</code> - Required - The object references to delete from the bucket. <ul>
1509
	 * 		<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>
1510
	 * 		<li><code>version_id</code> - <code>string</code> - Optional - If the object is versioned, include the version ID to delete.</li>
1511
	 * 	</ul></li>
1512
	 * 	<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>
1513
	 * 	<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>
1514
	 * 	<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>
1515
	 * 	<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>
1516
	 * 	<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>
1517
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
1518
	 * @link http://aws.amazon.com/mfa/ Multi-Factor Authentication
1519
	 */
1520
	public function delete_objects($bucket, $opt = null)
1521
	{
1522
		// Add this to our request
1523
		if (!$opt) $opt = array();
1524
		$opt['verb'] = 'POST';
1525
		$opt['sub_resource'] = 'delete';
1526
		$opt['body'] = '';
1527
 
1528
		// Bail out
1529
		if (!isset($opt['objects']) || !is_array($opt['objects']))
1530
		{
1531
			throw new S3_Exception('The ' . __FUNCTION__ . ' method requires the "objects" option to be set as an array.');
1532
		}
1533
 
1534
		$xml = new SimpleXMLElement($this->multi_object_delete_xml);
1535
 
1536
		// Add the objects
1537
		foreach ($opt['objects'] as $object)
1538
		{
1539
			$xobject = $xml->addChild('Object');
1540
			$xobject->addChild('Key', $object['key']);
1541
 
1542
			if (isset($object['version_id']))
1543
			{
1544
				$xobject->addChild('VersionId', $object['version_id']);
1545
			}
1546
		}
1547
 
1548
		// Quiet mode?
1549
		if (isset($opt['quiet']))
1550
		{
1551
			$quiet = 'false';
1552
			if (is_bool($opt['quiet'])) // Boolean
1553
			{
1554
				$quiet = $opt['quiet'] ? 'true' : 'false';
1555
			}
1556
			elseif (is_string($opt['quiet'])) // String
1557
			{
1558
				$quiet = ($opt['quiet'] === 'true') ? 'true' : 'false';
1559
			}
1560
 
1561
			$xml->addChild('Quiet', $quiet);
1562
		}
1563
 
1564
		// Enable MFA delete?
1565
		// @codeCoverageIgnoreStart
1566
		if (isset($opt['MFASerial']) && isset($opt['MFAToken']))
1567
		{
1568
			$opt['headers'] = array(
1569
				'x-amz-mfa' => ($opt['MFASerial'] . ' ' . $opt['MFAToken'])
1570
			);
1571
		}
1572
		// @codeCoverageIgnoreEnd
1573
 
1574
		$opt['body'] = $xml->asXML();
1575
 
1576
		// Authenticate to S3
1577
		return $this->authenticate($bucket, $opt);
1578
	}
1579
 
1580
	/**
1581
	 * Gets a list of all Amazon S3 objects in the specified bucket.
1582
	 *
1583
	 * 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.
1584
	 *
1585
	 * @param string $bucket (Required) The name of the bucket to use.
1586
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
1587
	 * 	<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>
1588
	 * 	<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>
1589
	 * 	<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>
1590
	 * 	<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>
1591
	 * 	<li><code>prefix</code> - <code>string</code> - Optional - Restricts the response to contain results that begin only with the specified prefix.</li>
1592
	 * 	<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>
1593
	 * 	<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>
1594
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
1595
	 */
1596
	public function list_objects($bucket, $opt = null)
1597
	{
1598
		if (!$opt) $opt = array();
1599
 
1600
		// Add this to our request
1601
		$opt['verb'] = 'GET';
1602
 
1603
		foreach (array('delimiter', 'marker', 'max-keys', 'prefix') as $param)
1604
		{
1605
			if (isset($opt[$param]))
1606
			{
1607
				$opt['query_string'][$param] = $opt[$param];
1608
				unset($opt[$param]);
1609
			}
1610
		}
1611
 
1612
		// Authenticate to S3
1613
		return $this->authenticate($bucket, $opt);
1614
	}
1615
 
1616
	/**
1617
	 * Copies an Amazon S3 object to a new location, whether in the same Amazon S3 region, bucket, or otherwise.
1618
	 *
1619
	 * @param array $source (Required) The bucket and file name to copy from. The following keys must be set: <ul>
1620
	 * 	<li><code>bucket</code> - <code>string</code> - Required - Specifies the name of the bucket containing the source object.</li>
1621
	 * 	<li><code>filename</code> - <code>string</code> - Required - Specifies the file name of the source object to copy.</li></ul>
1622
	 * @param array $dest (Required) The bucket and file name to copy to. The following keys must be set: <ul>
1623
	 * 	<li><code>bucket</code> - <code>string</code> - Required - Specifies the name of the bucket to copy the object to.</li>
1624
	 * 	<li><code>filename</code> - <code>string</code> - Required - Specifies the file name to copy the object to.</li></ul>
1625
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
1626
	 * 	<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>
1627
	 * 	<li><code>encryption</code> - <code>string</code> - Optional - The algorithm to use for encrypting the object. [Allowed values: <code>AES256</code>]</li>
1628
	 * 	<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>
1629
	 * 	<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>
1630
	 * 	<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>
1631
	 * 	<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>
1632
	 * 	<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>
1633
	 * 	<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>
1634
	 * 	<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>
1635
	 * 	<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>
1636
	 * 	<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>
1637
	 * 	<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>
1638
	 * 	<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>
1639
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
1640
	 * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/API/RESTObjectCOPY.html Copying Amazon S3 Objects
1641
	 */
1642
	public function copy_object($source, $dest, $opt = null)
1643
	{
1644
		if (!$opt) $opt = array();
1645
		$batch = array();
1646
 
1647
		// Add this to our request
1648
		$opt['verb'] = 'PUT';
1649
		$opt['resource'] = $dest['filename'];
1650
		$opt['body'] = '';
1651
 
1652
		// Handle copy source
1653
		if (isset($source['bucket']) && isset($source['filename']))
1654
		{
1655
			$opt['headers']['x-amz-copy-source'] = '/' . $source['bucket'] . '/' . rawurlencode($source['filename'])
1656
				. (isset($opt['versionId']) ? ('?' . 'versionId=' . rawurlencode($opt['versionId'])) : ''); // Append the versionId to copy, if available
1657
			unset($opt['versionId']);
1658
 
1659
			// Determine if we need to lookup the pre-existing content-type.
1660
			if (
1661
				(!$this->use_batch_flow && !isset($opt['returnCurlHandle'])) &&
1662
				!in_array(strtolower('content-type'), array_map('strtolower', array_keys($opt['headers'])))
1663
			)
1664
			{
1665
				$response = $this->get_object_headers($source['bucket'], $source['filename']);
1666
				if ($response->isOK())
1667
				{
1668
					$opt['headers']['Content-Type'] = $response->header['content-type'];
1669
				}
1670
			}
1671
		}
1672
 
1673
		// Handle metadata directive
1674
		$opt['headers']['x-amz-metadata-directive'] = 'COPY';
1675
		if ($source['bucket'] === $dest['bucket'] && $source['filename'] === $dest['filename'])
1676
		{
1677
			$opt['headers']['x-amz-metadata-directive'] = 'REPLACE';
1678
		}
1679
		if (isset($opt['metadataDirective']))
1680
		{
1681
			$opt['headers']['x-amz-metadata-directive'] = $opt['metadataDirective'];
1682
			unset($opt['metadataDirective']);
1683
		}
1684
 
1685
		// Handle Access Control Lists. Can also pass canned ACLs as an HTTP header.
1686
		if (isset($opt['acl']) && is_array($opt['acl']))
1687
		{
1688
			$batch[] = $this->set_object_acl($dest['bucket'], $dest['filename'], $opt['acl'], array(
1689
				'returnCurlHandle' => true
1690
			));
1691
			unset($opt['acl']);
1692
		}
1693
		elseif (isset($opt['acl']))
1694
		{
1695
			$opt['headers']['x-amz-acl'] = $opt['acl'];
1696
			unset($opt['acl']);
1697
		}
1698
 
1699
		// Handle storage settings. Can also be passed as an HTTP header.
1700
		if (isset($opt['storage']))
1701
		{
1702
			$opt['headers']['x-amz-storage-class'] = $opt['storage'];
1703
			unset($opt['storage']);
1704
		}
1705
 
1706
		// Handle encryption settings. Can also be passed as an HTTP header.
1707
		if (isset($opt['encryption']))
1708
		{
1709
			$opt['headers']['x-amz-server-side-encryption'] = $opt['encryption'];
1710
			unset($opt['encryption']);
1711
		}
1712
 
1713
		// Handle conditional-copy parameters
1714
		if (isset($opt['ifMatch']))
1715
		{
1716
			$opt['headers']['x-amz-copy-source-if-match'] = $opt['ifMatch'];
1717
			unset($opt['ifMatch']);
1718
		}
1719
		if (isset($opt['ifNoneMatch']))
1720
		{
1721
			$opt['headers']['x-amz-copy-source-if-none-match'] = $opt['ifNoneMatch'];
1722
			unset($opt['ifNoneMatch']);
1723
		}
1724
		if (isset($opt['ifUnmodifiedSince']))
1725
		{
1726
			$opt['headers']['x-amz-copy-source-if-unmodified-since'] = $opt['ifUnmodifiedSince'];
1727
			unset($opt['ifUnmodifiedSince']);
1728
		}
1729
		if (isset($opt['ifModifiedSince']))
1730
		{
1731
			$opt['headers']['x-amz-copy-source-if-modified-since'] = $opt['ifModifiedSince'];
1732
			unset($opt['ifModifiedSince']);
1733
		}
1734
 
1735
		// Handle meta tags. Can also be passed as an HTTP header.
1736
		if (isset($opt['meta']))
1737
		{
1738
			foreach ($opt['meta'] as $meta_key => $meta_value)
1739
			{
1740
				// e.g., `My Meta Header` is converted to `x-amz-meta-my-meta-header`.
1741
				$opt['headers']['x-amz-meta-' . strtolower(str_replace(' ', '-', $meta_key))] = $meta_value;
1742
			}
1743
			unset($opt['meta']);
1744
		}
1745
 
1746
		// Authenticate to S3
1747
		$response = $this->authenticate($dest['bucket'], $opt);
1748
 
1749
		// Attempt to reset ACLs
1750
		$http = new RequestCore();
1751
		$http->send_multi_request($batch);
1752
 
1753
		return $response;
1754
	}
1755
 
1756
	/**
1757
	 * Updates an Amazon S3 object with new headers or other metadata. To replace the content of the
1758
	 * specified Amazon S3 object, call <create_object()> with the same bucket and file name parameters.
1759
	 *
1760
	 * @param string $bucket (Required) The name of the bucket that contains the source file.
1761
	 * @param string $filename (Required) The source file name that you want to update.
1762
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
1763
	 * 	<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>
1764
	 * 	<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>
1765
	 * 	<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>
1766
	 * 	<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>
1767
	 * 	<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>
1768
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
1769
	 * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/API/RESTObjectCOPY.html Copying Amazon S3 Objects
1770
	 */
1771
	public function update_object($bucket, $filename, $opt = null)
1772
	{
1773
		if (!$opt) $opt = array();
1774
		$opt['metadataDirective'] = 'COPY';
1775
 
1776
		// Authenticate to S3
1777
		return $this->copy_object(
1778
			array('bucket' => $bucket, 'filename' => $filename),
1779
			array('bucket' => $bucket, 'filename' => $filename),
1780
			$opt
1781
		);
1782
	}
1783
 
1784
 
1785
	/*%******************************************************************************************%*/
1786
	// ACCESS CONTROL LISTS
1787
 
1788
	/**
1789
	 * Gets the access control list (ACL) settings for the specified Amazon S3 object.
1790
	 *
1791
	 * @param string $bucket (Required) The name of the bucket to use.
1792
	 * @param string $filename (Required) The file name for the object.
1793
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
1794
	 * 	<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>
1795
	 * 	<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>
1796
	 * 	<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>
1797
	 * 	<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>
1798
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
1799
	 * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAccessPolicy.html REST Access Control Policy
1800
	 */
1801
	public function get_object_acl($bucket, $filename, $opt = null)
1802
	{
1803
		// Add this to our request
1804
		if (!$opt) $opt = array();
1805
		$opt['verb'] = 'GET';
1806
		$opt['resource'] = $filename;
1807
		$opt['sub_resource'] = 'acl';
1808
 
1809
		// Authenticate to S3
1810
		return $this->authenticate($bucket, $opt);
1811
	}
1812
 
1813
	/**
1814
	 * Sets the access control list (ACL) settings for the specified Amazon S3 object.
1815
	 *
1816
	 * @param string $bucket (Required) The name of the bucket to use.
1817
	 * @param string $filename (Required) The file name for the object.
1818
	 * @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>.
1819
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
1820
	 * 	<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>
1821
	 * 	<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>
1822
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
1823
	 * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAccessPolicy.html REST Access Control Policy
1824
	 */
1825
	public function set_object_acl($bucket, $filename, $acl = self::ACL_PRIVATE, $opt = null)
1826
	{
1827
		// Add this to our request
1828
		if (!$opt) $opt = array();
1829
		$opt['verb'] = 'PUT';
1830
		$opt['resource'] = $filename;
1831
		$opt['sub_resource'] = 'acl';
1832
 
1833
		// Retrieve the original metadata
1834
		$metadata = $this->get_object_metadata($bucket, $filename);
1835
		if ($metadata && $metadata['ContentType'])
1836
		{
1837
			$opt['headers']['Content-Type'] = $metadata['ContentType'];
1838
		}
1839
		if ($metadata && $metadata['StorageClass'])
1840
		{
1841
			$opt['headers']['x-amz-storage-class'] = $metadata['StorageClass'];
1842
		}
1843
 
1844
		// Make sure these are defined.
1845
		// @codeCoverageIgnoreStart
1846
		if (!$this->credentials->canonical_id || !$this->credentials->canonical_name)
1847
		{
1848
			// Fetch the data live.
1849
			$canonical = $this->get_canonical_user_id();
1850
			$this->credentials->canonical_id = $canonical['id'];
1851
			$this->credentials->canonical_name = $canonical['display_name'];
1852
		}
1853
		// @codeCoverageIgnoreEnd
1854
 
1855
		if (is_array($acl))
1856
		{
1857
			$opt['body'] = $this->generate_access_policy($this->credentials->canonical_id, $this->credentials->canonical_name, $acl);
1858
		}
1859
		else
1860
		{
1861
			$opt['body'] = '';
1862
			$opt['headers']['x-amz-acl'] = $acl;
1863
		}
1864
 
1865
		// Authenticate to S3
1866
		return $this->authenticate($bucket, $opt);
1867
	}
1868
 
1869
	/**
1870
	 * Generates the XML to be used for the Access Control Policy.
1871
	 *
1872
	 * @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()>.
1873
	 * @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()>.
1874
	 * @param array $users (Optional) An array of associative arrays. Each associative array contains an `id` value and a `permission` value.
1875
	 * @return string Access Control Policy XML.
1876
	 * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/S3_ACLs.html Access Control Lists
1877
	 */
1878
	public function generate_access_policy($canonical_id, $canonical_name, $users)
1879
	{
1880
		$xml = simplexml_load_string($this->base_acp_xml);
1881
		$owner = $xml->addChild('Owner');
1882
		$owner->addChild('ID', $canonical_id);
1883
		$owner->addChild('DisplayName', $canonical_name);
1884
		$acl = $xml->addChild('AccessControlList');
1885
 
1886
		foreach ($users as $user)
1887
		{
1888
			$grant = $acl->addChild('Grant');
1889
			$grantee = $grant->addChild('Grantee');
1890
 
1891
			switch ($user['id'])
1892
			{
1893
				// Authorized Users
1894
				case self::USERS_AUTH:
1895
					$grantee->addAttribute('xsi:type', 'Group', 'http://www.w3.org/2001/XMLSchema-instance');
1896
					$grantee->addChild('URI', self::USERS_AUTH);
1897
					break;
1898
 
1899
				// All Users
1900
				case self::USERS_ALL:
1901
					$grantee->addAttribute('xsi:type', 'Group', 'http://www.w3.org/2001/XMLSchema-instance');
1902
					$grantee->addChild('URI', self::USERS_ALL);
1903
					break;
1904
 
1905
				// The Logging User
1906
				case self::USERS_LOGGING:
1907
					$grantee->addAttribute('xsi:type', 'Group', 'http://www.w3.org/2001/XMLSchema-instance');
1908
					$grantee->addChild('URI', self::USERS_LOGGING);
1909
					break;
1910
 
1911
				// Email Address or Canonical Id
1912
				default:
1913
					if (strpos($user['id'], '@'))
1914
					{
1915
						$grantee->addAttribute('xsi:type', 'AmazonCustomerByEmail', 'http://www.w3.org/2001/XMLSchema-instance');
1916
						$grantee->addChild('EmailAddress', $user['id']);
1917
					}
1918
					else
1919
					{
1920
						// Assume Canonical Id
1921
						$grantee->addAttribute('xsi:type', 'CanonicalUser', 'http://www.w3.org/2001/XMLSchema-instance');
1922
						$grantee->addChild('ID', $user['id']);
1923
					}
1924
					break;
1925
			}
1926
 
1927
			$grant->addChild('Permission', $user['permission']);
1928
		}
1929
 
1930
		return $xml->asXML();
1931
	}
1932
 
1933
 
1934
	/*%******************************************************************************************%*/
1935
	// LOGGING METHODS
1936
 
1937
	/**
1938
	 * Gets the access logs associated with the specified Amazon S3 bucket.
1939
	 *
1940
	 * @param string $bucket (Required) The name of the bucket to use. Pass a `null` value when using the <set_vhost()> method.
1941
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
1942
	 * 	<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>
1943
	 * 	<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>
1944
	 * 	<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>
1945
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
1946
	 * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/ServerLogs.html Server Access Logging
1947
	 */
1948
	public function get_logs($bucket, $opt = null)
1949
	{
1950
		// Add this to our request
1951
		if (!$opt) $opt = array();
1952
		$opt['verb'] = 'GET';
1953
		$opt['sub_resource'] = 'logging';
1954
 
1955
		// Authenticate to S3
1956
		return $this->authenticate($bucket, $opt);
1957
	}
1958
 
1959
	/**
1960
	 * Enables access logging for the specified Amazon S3 bucket.
1961
	 *
1962
	 * @param string $bucket (Required) The name of the bucket to enable logging for. Pass a `null` value when using the <set_vhost()> method.
1963
	 * @param string $target_bucket (Required) The name of the bucket to store the logs in.
1964
	 * @param string $target_prefix (Required) The prefix to give to the log file names.
1965
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
1966
	 * 	<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>
1967
	 * 	<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>
1968
	 * 	<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>
1969
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
1970
	 * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/LoggingAPI.html Server Access Logging Configuration API
1971
	 */
1972
	public function enable_logging($bucket, $target_bucket, $target_prefix, $opt = null)
1973
	{
1974
		// Add this to our request
1975
		if (!$opt) $opt = array();
1976
		$opt['verb'] = 'PUT';
1977
		$opt['sub_resource'] = 'logging';
1978
		$opt['headers'] = array(
1979
			'Content-Type' => 'application/xml'
1980
		);
1981
 
1982
		$xml = simplexml_load_string($this->base_logging_xml);
1983
		$LoggingEnabled = $xml->addChild('LoggingEnabled');
1984
		$LoggingEnabled->addChild('TargetBucket', $target_bucket);
1985
		$LoggingEnabled->addChild('TargetPrefix', $target_prefix);
1986
		$TargetGrants = $LoggingEnabled->addChild('TargetGrants');
1987
 
1988
		if (isset($opt['users']) && is_array($opt['users']))
1989
		{
1990
			foreach ($opt['users'] as $user)
1991
			{
1992
				$grant = $TargetGrants->addChild('Grant');
1993
				$grantee = $grant->addChild('Grantee');
1994
 
1995
				switch ($user['id'])
1996
				{
1997
					// Authorized Users
1998
					case self::USERS_AUTH:
1999
						$grantee->addAttribute('xsi:type', 'Group', 'http://www.w3.org/2001/XMLSchema-instance');
2000
						$grantee->addChild('URI', self::USERS_AUTH);
2001
						break;
2002
 
2003
					// All Users
2004
					case self::USERS_ALL:
2005
						$grantee->addAttribute('xsi:type', 'Group', 'http://www.w3.org/2001/XMLSchema-instance');
2006
						$grantee->addChild('URI', self::USERS_ALL);
2007
						break;
2008
 
2009
					// The Logging User
2010
					case self::USERS_LOGGING:
2011
						$grantee->addAttribute('xsi:type', 'Group', 'http://www.w3.org/2001/XMLSchema-instance');
2012
						$grantee->addChild('URI', self::USERS_LOGGING);
2013
						break;
2014
 
2015
					// Email Address or Canonical Id
2016
					default:
2017
						if (strpos($user['id'], '@'))
2018
						{
2019
							$grantee->addAttribute('xsi:type', 'AmazonCustomerByEmail', 'http://www.w3.org/2001/XMLSchema-instance');
2020
							$grantee->addChild('EmailAddress', $user['id']);
2021
						}
2022
						else
2023
						{
2024
							// Assume Canonical Id
2025
							$grantee->addAttribute('xsi:type', 'CanonicalUser', 'http://www.w3.org/2001/XMLSchema-instance');
2026
							$grantee->addChild('ID', $user['id']);
2027
						}
2028
						break;
2029
				}
2030
 
2031
				$grant->addChild('Permission', $user['permission']);
2032
			}
2033
		}
2034
 
2035
		$opt['body'] = $xml->asXML();
2036
 
2037
		// Authenticate to S3
2038
		return $this->authenticate($bucket, $opt);
2039
	}
2040
 
2041
	/**
2042
	 * Disables access logging for the specified Amazon S3 bucket.
2043
	 *
2044
	 * @param string $bucket (Required) The name of the bucket to use. Pass `null` if using <set_vhost()>.
2045
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
2046
	 * 	<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>
2047
	 * 	<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>
2048
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
2049
	 * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/LoggingAPI.html Server Access Logging Configuration API
2050
	 */
2051
	public function disable_logging($bucket, $opt = null)
2052
	{
2053
		// Add this to our request
2054
		if (!$opt) $opt = array();
2055
		$opt['verb'] = 'PUT';
2056
		$opt['sub_resource'] = 'logging';
2057
		$opt['headers'] = array(
2058
			'Content-Type' => 'application/xml'
2059
		);
2060
		$opt['body'] = $this->base_logging_xml;
2061
 
2062
		// Authenticate to S3
2063
		return $this->authenticate($bucket, $opt);
2064
	}
2065
 
2066
 
2067
	/*%******************************************************************************************%*/
2068
	// CONVENIENCE METHODS
2069
 
2070
	/**
2071
	 * Gets whether or not the specified Amazon S3 bucket exists in Amazon S3. This includes buckets
2072
	 * that do not belong to the caller.
2073
	 *
2074
	 * @param string $bucket (Required) The name of the bucket to use.
2075
	 * @return boolean A value of <code>true</code> if the bucket exists, or a value of <code>false</code> if it does not.
2076
	 */
2077
	public function if_bucket_exists($bucket)
2078
	{
2079
		if ($this->use_batch_flow)
2080
		{
2081
			throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested');
2082
		}
2083
 
2084
		$header = $this->get_bucket_headers($bucket);
2085
		return (bool) $header->isOK();
2086
	}
2087
 
2088
	/**
2089
	 * Gets whether or not the specified Amazon S3 object exists in the specified bucket.
2090
	 *
2091
	 * @param string $bucket (Required) The name of the bucket to use.
2092
	 * @param string $filename (Required) The file name for the object.
2093
	 * @return boolean A value of <code>true</code> if the object exists, or a value of <code>false</code> if it does not.
2094
	 */
2095
	public function if_object_exists($bucket, $filename)
2096
	{
2097
		if ($this->use_batch_flow)
2098
		{
2099
			throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested');
2100
		}
2101
 
2102
		$header = $this->get_object_headers($bucket, $filename);
2103
 
2104
		if ($header->isOK()) { return true; }
2105
		elseif ($header->status === 404) { return false; }
2106
 
2107
		// @codeCoverageIgnoreStart
2108
		return null;
2109
		// @codeCoverageIgnoreEnd
2110
	}
2111
 
2112
	/**
2113
	 * Gets whether or not the specified Amazon S3 bucket has a bucket policy associated with it.
2114
	 *
2115
	 * @param string $bucket (Required) The name of the bucket to use.
2116
	 * @return boolean A value of <code>true</code> if a bucket policy exists, or a value of <code>false</code> if one does not.
2117
	 */
2118
	public function if_bucket_policy_exists($bucket)
2119
	{
2120
		if ($this->use_batch_flow)
2121
		{
2122
			// @codeCoverageIgnoreStart
2123
			throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested');
2124
			// @codeCoverageIgnoreEnd
2125
		}
2126
 
2127
		$response = $this->get_bucket_policy($bucket);
2128
 
2129
		if ($response->isOK()) { return true; }
2130
		elseif ($response->status === 404) { return false; }
2131
 
2132
		// @codeCoverageIgnoreStart
2133
		return null;
2134
		// @codeCoverageIgnoreEnd
2135
	}
2136
 
2137
	/**
2138
	 * Gets the number of Amazon S3 objects in the specified bucket.
2139
	 *
2140
	 * @param string $bucket (Required) The name of the bucket to use.
2141
	 * @return integer The number of Amazon S3 objects in the bucket.
2142
	 */
2143
	public function get_bucket_object_count($bucket)
2144
	{
2145
		if ($this->use_batch_flow)
2146
		{
2147
			// @codeCoverageIgnoreStart
2148
			throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested');
2149
			// @codeCoverageIgnoreEnd
2150
		}
2151
 
2152
		return count($this->get_object_list($bucket));
2153
	}
2154
 
2155
	/**
2156
	 * Gets the cumulative file size of the contents of the Amazon S3 bucket.
2157
	 *
2158
	 * @param string $bucket (Required) The name of the bucket to use.
2159
	 * @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.
2160
	 * @return integer|string The number of bytes as an integer, or the friendly format as a string.
2161
	 */
2162
	public function get_bucket_filesize($bucket, $friendly_format = false)
2163
	{
2164
		if ($this->use_batch_flow)
2165
		{
2166
			throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested');
2167
		}
2168
 
2169
		$filesize = 0;
2170
		$list = $this->list_objects($bucket);
2171
 
2172
		foreach ($list->body->Contents as $filename)
2173
		{
2174
			$filesize += (integer) $filename->Size;
2175
		}
2176
 
2177
		while ((string) $list->body->IsTruncated === 'true')
2178
		{
2179
			$body = (array) $list->body;
2180
			$list = $this->list_objects($bucket, array(
2181
				'marker' => (string) end($body['Contents'])->Key
2182
			));
2183
 
2184
			foreach ($list->body->Contents as $object)
2185
			{
2186
				$filesize += (integer) $object->Size;
2187
			}
2188
		}
2189
 
2190
		if ($friendly_format)
2191
		{
2192
			$filesize = $this->util->size_readable($filesize);
2193
		}
2194
 
2195
		return $filesize;
2196
	}
2197
 
2198
	/**
2199
	 * Gets the file size of the specified Amazon S3 object.
2200
	 *
2201
	 * @param string $bucket (Required) The name of the bucket to use.
2202
	 * @param string $filename (Required) The file name for the object.
2203
	 * @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.
2204
	 * @return integer|string The number of bytes as an integer, or the friendly format as a string.
2205
	 */
2206
	public function get_object_filesize($bucket, $filename, $friendly_format = false)
2207
	{
2208
		if ($this->use_batch_flow)
2209
		{
2210
			throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested');
2211
		}
2212
 
2213
		$object = $this->get_object_headers($bucket, $filename);
2214
		$filesize = (integer) $object->header['content-length'];
2215
 
2216
		if ($friendly_format)
2217
		{
2218
			$filesize = $this->util->size_readable($filesize);
2219
		}
2220
 
2221
		return $filesize;
2222
	}
2223
 
2224
	/**
2225
	 * Changes the content type for an existing Amazon S3 object.
2226
	 *
2227
	 * @param string $bucket (Required) The name of the bucket to use.
2228
	 * @param string $filename (Required) The file name for the object.
2229
	 * @param string $contentType (Required) The content-type to apply to the object.
2230
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
2231
	 * 	<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>
2232
	 * 	<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>
2233
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
2234
	 */
2235
	public function change_content_type($bucket, $filename, $contentType, $opt = null)
2236
	{
2237
		if (!$opt) $opt = array();
2238
 
2239
		// Retrieve the original metadata
2240
		$metadata = $this->get_object_metadata($bucket, $filename);
2241
		if ($metadata && $metadata['ACL'])
2242
		{
2243
			$opt['acl'] = $metadata['ACL'];
2244
		}
2245
		if ($metadata && $metadata['StorageClass'])
2246
		{
2247
			$opt['headers']['x-amz-storage-class'] = $metadata['StorageClass'];
2248
		}
2249
 
2250
		// Merge optional parameters
2251
		$opt = array_merge_recursive(array(
2252
			'headers' => array(
2253
				'Content-Type' => $contentType
2254
			),
2255
			'metadataDirective' => 'COPY'
2256
		), $opt);
2257
 
2258
		return $this->copy_object(
2259
			array('bucket' => $bucket, 'filename' => $filename),
2260
			array('bucket' => $bucket, 'filename' => $filename),
2261
			$opt
2262
		);
2263
	}
2264
 
2265
	/**
2266
	 * Changes the storage redundancy for an existing object.
2267
	 *
2268
	 * @param string $bucket (Required) The name of the bucket to use.
2269
	 * @param string $filename (Required) The file name for the object.
2270
	 * @param string $storage (Required) The storage setting to apply to the object. [Allowed values: <code>AmazonS3::STORAGE_STANDARD</code>, <code>AmazonS3::STORAGE_REDUCED</code>]
2271
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
2272
	 * 	<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>
2273
	 * 	<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>
2274
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
2275
	 */
2276
	public function change_storage_redundancy($bucket, $filename, $storage, $opt = null)
2277
	{
2278
		if (!$opt) $opt = array();
2279
 
2280
		// Retrieve the original metadata
2281
		$metadata = $this->get_object_metadata($bucket, $filename);
2282
		if ($metadata && $metadata['ACL'])
2283
		{
2284
			$opt['acl'] = $metadata['ACL'];
2285
		}
2286
		if ($metadata && $metadata['ContentType'])
2287
		{
2288
			$opt['headers']['Content-Type'] = $metadata['ContentType'];
2289
		}
2290
 
2291
		// Merge optional parameters
2292
		$opt = array_merge(array(
2293
			'storage' => $storage,
2294
			'metadataDirective' => 'COPY',
2295
		), $opt);
2296
 
2297
		return $this->copy_object(
2298
			array('bucket' => $bucket, 'filename' => $filename),
2299
			array('bucket' => $bucket, 'filename' => $filename),
2300
			$opt
2301
		);
2302
	}
2303
 
2304
	/**
2305
	 * Gets a simplified list of bucket names on an Amazon S3 account.
2306
	 *
2307
	 * @param string $pcre (Optional) A Perl-Compatible Regular Expression (PCRE) to filter the bucket names against.
2308
	 * @return array The list of matching bucket names. If there are no results, the method will return an empty array.
2309
	 * @link http://php.net/pcre Regular Expressions (Perl-Compatible)
2310
	 */
2311
	public function get_bucket_list($pcre = null)
2312
	{
2313
		if ($this->use_batch_flow)
2314
		{
2315
			throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested');
2316
		}
2317
 
2318
		// Get a list of buckets.
2319
		$list = $this->list_buckets();
2320
		if ($list = $list->body->query('descendant-or-self::Name'))
2321
		{
2322
			$list = $list->map_string($pcre);
2323
			return $list;
2324
		}
2325
 
2326
		// @codeCoverageIgnoreStart
2327
		return array();
2328
		// @codeCoverageIgnoreEnd
2329
	}
2330
 
2331
	/**
2332
	 * Gets a simplified list of Amazon S3 object file names contained in a bucket.
2333
	 *
2334
	 * @param string $bucket (Required) The name of the bucket to use.
2335
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
2336
	 * 	<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>
2337
	 * 	<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>
2338
	 * 	<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 less.</li>
2339
	 * 	<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>
2340
	 * 	<li><code>prefix</code> - <code>string</code> - Optional - Restricts the response to contain results that begin only with the specified prefix.</li>
2341
	 * 	<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>
2342
	 * 	<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>
2343
	 * @return array The list of matching object names. If there are no results, the method will return an empty array.
2344
	 * @link http://php.net/pcre Regular Expressions (Perl-Compatible)
2345
	 */
2346
	public function get_object_list($bucket, $opt = null)
2347
	{
2348
		if ($this->use_batch_flow)
2349
		{
2350
			throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested');
2351
		}
2352
 
2353
		if (!$opt) $opt = array();
2354
 
2355
		// Set some default values
2356
		$pcre = isset($opt['pcre']) ? $opt['pcre'] : null;
2357
		$max_keys = isset($opt['max-keys']) ? (integer) $opt['max-keys'] : 'all';
2358
		$objects = array();
2359
 
2360
		if ($max_keys === 'all')
2361
		{
2362
			do
2363
			{
2364
				$list = $this->list_objects($bucket, $opt);
2365
				if ($keys = $list->body->query('descendant-or-self::Key')->map_string($pcre))
2366
				{
2367
					$objects = array_merge($objects, $keys);
2368
				}
2369
 
2370
				$body = (array) $list->body;
2371
				$opt = array_merge($opt, array(
2372
					'marker' => (isset($body['Contents']) && is_array($body['Contents'])) ?
2373
						((string) end($body['Contents'])->Key) :
2374
						((string) $list->body->Contents->Key)
2375
				));
2376
			}
2377
			while ((string) $list->body->IsTruncated === 'true');
2378
		}
2379
		else
2380
		{
2381
			$loops = ceil($max_keys / 1000);
2382
 
2383
			do
2384
			{
2385
				$list = $this->list_objects($bucket, $opt);
2386
				if ($keys = $list->body->query('descendant-or-self::Key')->map_string($pcre))
2387
				{
2388
					$objects = array_merge($objects, $keys);
2389
				}
2390
 
2391
				if ($max_keys > 1000)
2392
				{
2393
					$max_keys -= 1000;
2394
				}
2395
 
2396
				$body = (array) $list->body;
2397
				$opt = array_merge($opt, array(
2398
					'max-keys' => $max_keys,
2399
					'marker' => (isset($body['Contents']) && is_array($body['Contents'])) ?
2400
						((string) end($body['Contents'])->Key) :
2401
						((string) $list->body->Contents->Key)
2402
				));
2403
			}
2404
			while (--$loops);
2405
		}
2406
 
2407
		if (count($objects) > 0)
2408
		{
2409
			return $objects;
2410
		}
2411
 
2412
		return array();
2413
	}
2414
 
2415
	/**
2416
	 * Deletes all Amazon S3 objects inside the specified bucket.
2417
	 *
2418
	 * @param string $bucket (Required) The name of the bucket to use.
2419
	 * @param string $pcre (Optional) A Perl-Compatible Regular Expression (PCRE) to filter the names against. The default value is <PCRE_ALL>.
2420
	 * @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.
2421
	 * @link http://php.net/pcre Regular Expressions (Perl-Compatible)
2422
	 */
2423
	public function delete_all_objects($bucket, $pcre = self::PCRE_ALL)
2424
	{
2425
		// Collect all matches
2426
		$list = $this->get_object_list($bucket, array('pcre' => $pcre));
2427
 
2428
		// As long as we have at least one match...
2429
		if (count($list) > 0)
2430
		{
2431
			$objects = array();
2432
 
2433
			foreach ($list as $object)
2434
			{
2435
				$objects[] = array('key' => $object);
2436
			}
2437
 
2438
			$batch = new CFBatchRequest();
2439
			$batch->use_credentials($this->credentials);
2440
 
2441
			foreach (array_chunk($objects, 1000) as $object_set)
2442
			{
2443
				$this->batch($batch)->delete_objects($bucket, array(
2444
					'objects' => $object_set
2445
				));
2446
			}
2447
 
2448
			$responses = $this->batch($batch)->send();
2449
			$is_ok = true;
2450
 
2451
			foreach ($responses as $response)
2452
			{
2453
				if (!$response->isOK() || isset($response->body->Error))
2454
				{
2455
					$is_ok = false;
2456
				}
2457
			}
2458
 
2459
			return $is_ok;
2460
		}
2461
 
2462
		// If there are no matches, return true
2463
		return true;
2464
	}
2465
 
2466
	/**
2467
	 * Deletes all of the versions of all Amazon S3 objects inside the specified bucket.
2468
	 *
2469
	 * @param string $bucket (Required) The name of the bucket to use.
2470
	 * @param string $pcre (Optional) A Perl-Compatible Regular Expression (PCRE) to filter the names against. The default value is <PCRE_ALL>.
2471
	 * @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.
2472
	 * @link http://php.net/pcre Regular Expressions (Perl-Compatible)
2473
	 */
2474
	public function delete_all_object_versions($bucket, $pcre = null)
2475
	{
2476
		// Instantiate
2477
		$versions = $this->list_bucket_object_versions($bucket);
2478
 
2479
		// Gather all nodes together into a single array
2480
		if ($versions->body->DeleteMarker() && $versions->body->Version())
2481
		{
2482
			$markers = array_merge($versions->body->DeleteMarker()->getArrayCopy(), $versions->body->Version()->getArrayCopy());
2483
		}
2484
		elseif ($versions->body->DeleteMarker())
2485
		{
2486
			$markers = $versions->body->DeleteMarker()->getArrayCopy();
2487
		}
2488
		elseif ($versions->body->Version())
2489
		{
2490
			$markers = $versions->body->Version()->getArrayCopy();
2491
		}
2492
		else
2493
		{
2494
			$markers = array();
2495
		}
2496
 
2497
		while ((string) $versions->body->IsTruncated === 'true')
2498
		{
2499
			$versions = $this->list_bucket_object_versions($bucket, array(
2500
				'key-marker' => (string) $versions->body->NextKeyMarker
2501
			));
2502
 
2503
			// Gather all nodes together into a single array
2504
			if ($versions->body->DeleteMarker() && $versions->body->Version())
2505
			{
2506
				$markers = array_merge($markers, $versions->body->DeleteMarker()->getArrayCopy(), $versions->body->Version()->getArrayCopy());
2507
			}
2508
			elseif ($versions->body->DeleteMarker())
2509
			{
2510
				$markers = array_merge($markers, $versions->body->DeleteMarker()->getArrayCopy());
2511
			}
2512
			elseif ($versions->body->Version())
2513
			{
2514
				$markers = array_merge($markers, $versions->body->Version()->getArrayCopy());
2515
			}
2516
		}
2517
 
2518
		$objects = array();
2519
 
2520
		// Loop through markers
2521
		foreach ($markers as $marker)
2522
		{
2523
			if ($pcre)
2524
			{
2525
				if (preg_match($pcre, (string) $marker->Key))
2526
				{
2527
					$xx = array('key' => (string) $marker->Key);
2528
					if ((string) $marker->VersionId !== 'null')
2529
					{
2530
						$xx['version_id'] = (string) $marker->VersionId;
2531
					}
2532
					$objects[] = $xx;
2533
					unset($xx);
2534
				}
2535
			}
2536
			else
2537
			{
2538
				$xx = array('key' => (string) $marker->Key);
2539
				if ((string) $marker->VersionId !== 'null')
2540
				{
2541
					$xx['version_id'] = (string) $marker->VersionId;
2542
				}
2543
				$objects[] = $xx;
2544
				unset($xx);
2545
			}
2546
		}
2547
 
2548
		$batch = new CFBatchRequest();
2549
		$batch->use_credentials($this->credentials);
2550
 
2551
		foreach (array_chunk($objects, 1000) as $object_set)
2552
		{
2553
			$this->batch($batch)->delete_objects($bucket, array(
2554
				'objects' => $object_set
2555
			));
2556
		}
2557
 
2558
		$responses = $this->batch($batch)->send();
2559
		$is_ok = true;
2560
 
2561
		foreach ($responses as $response)
2562
		{
2563
			if (!$response->isOK() || isset($response->body->Error))
2564
			{
2565
				$is_ok = false;
2566
			}
2567
		}
2568
 
2569
		return $is_ok;
2570
	}
2571
 
2572
	/**
2573
	 * Gets the collective metadata for the given Amazon S3 object.
2574
	 *
2575
	 * @param string $bucket (Required) The name of the bucket to use.
2576
	 * @param string $filename (Required) The file name for the Amazon S3 object.
2577
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
2578
	 * 	<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>
2579
	 * 	<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>
2580
	 * 	<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>
2581
	 * @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>.
2582
	 */
2583
	public function get_object_metadata($bucket, $filename, $opt = null)
2584
	{
2585
		$batch = new CFBatchRequest();
2586
		$this->batch($batch)->get_object_acl($bucket, $filename); // Get ACL info
2587
		$this->batch($batch)->get_object_headers($bucket, $filename); // Get content-type
2588
		$this->batch($batch)->list_objects($bucket, array( // Get other metadata
2589
			'max-keys' => 1,
2590
			'prefix' => $filename
2591
		));
2592
		$response = $this->batch($batch)->send();
2593
 
2594
		// Fail if any requests were unsuccessful
2595
		if (!$response->areOK())
2596
		{
2597
			return false;
2598
		}
2599
 
2600
		$data = array(
2601
			'ACL' => array(),
2602
			'ContentType' => null,
2603
			'ETag' => null,
2604
			'Headers' => null,
2605
			'Key' => null,
2606
			'LastModified' => null,
2607
			'Owner' => array(),
2608
			'Size' => null,
2609
			'StorageClass' => null,
2610
		);
2611
 
2612
		// Add the content type
2613
		$data['ContentType'] = (string) $response[1]->header['content-type'];
2614
 
2615
		// Add the other metadata (including storage type)
2616
		$contents = json_decode(json_encode($response[2]->body->query('descendant-or-self::Contents')->first()), true);
2617
		$data = array_merge($data, (is_array($contents) ? $contents : array()));
2618
 
2619
		// Add ACL info
2620
		$grants = $response[0]->body->query('descendant-or-self::Grant');
2621
		$max = count($grants);
2622
 
2623
		// Add raw header info
2624
		$data['Headers'] = $response[1]->header;
2625
		foreach (array('_info', 'x-amz-id-2', 'x-amz-request-id', 'cneonction', 'server', 'content-length', 'content-type', 'etag') as $header)
2626
		{
2627
			unset($data['Headers'][$header]);
2628
		}
2629
		ksort($data['Headers']);
2630
 
2631
		if (count($grants) > 0)
2632
		{
2633
			foreach ($grants as $grant)
2634
			{
2635
				$dgrant = array(
2636
					'id' => (string) $this->util->try_these(array('ID', 'URI'), $grant->Grantee),
2637
					'permission' => (string) $grant->Permission
2638
				);
2639
 
2640
				$data['ACL'][] = $dgrant;
2641
			}
2642
		}
2643
 
2644
		return $data;
2645
	}
2646
 
2647
 
2648
	/*%******************************************************************************************%*/
2649
	// URLS
2650
 
2651
	/**
2652
	 * Gets the web-accessible URL for the Amazon S3 object or generates a time-limited signed request for
2653
	 * a private file.
2654
	 *
2655
	 * @param string $bucket (Required) The name of the bucket to use.
2656
	 * @param string $filename (Required) The file name for the Amazon S3 object.
2657
	 * @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()>.
2658
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
2659
	 * 	<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>
2660
	 * 	<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>
2661
	 * 	<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>
2662
	 * 	<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>
2663
	 * 	<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>
2664
	 * @return string The file URL, with authentication and/or torrent parameters if requested.
2665
	 * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/S3_QSAuth.html Using Query String Authentication
2666
	 */
2667
	public function get_object_url($bucket, $filename, $preauth = 0, $opt = null)
2668
	{
2669
		// Add this to our request
2670
		if (!$opt) $opt = array();
2671
		$opt['verb'] = isset($opt['method']) ? $opt['method'] : 'GET';
2672
		$opt['resource'] = $filename;
2673
		$opt['preauth'] = $preauth;
2674
 
2675
		if (isset($opt['torrent']) && $opt['torrent'])
2676
		{
2677
			$opt['sub_resource'] = 'torrent';
2678
			unset($opt['torrent']);
2679
		}
2680
 
2681
		// GET responses
2682
		if (isset($opt['response']))
2683
		{
2684
			foreach ($opt['response'] as $key => $value)
2685
			{
2686
				$opt['response-' . $key] = $value;
2687
				unset($opt['response'][$key]);
2688
			}
2689
		}
2690
 
2691
		// Authenticate to S3
2692
		$current_ssl_setting = $this->use_ssl;
2693
		$this->use_ssl = false;
2694
		$response = $this->authenticate($bucket, $opt);
2695
		$this->use_ssl = $current_ssl_setting;
2696
 
2697
		return $response;
2698
	}
2699
 
2700
	/**
2701
	 * Gets the web-accessible URL to a torrent of the Amazon S3 object. The Amazon S3 object's access
2702
	 * control list settings (ACL) MUST be set to <ACL_PUBLIC> for a valid URL to be returned.
2703
	 *
2704
	 * @param string $bucket (Required) The name of the bucket to use.
2705
	 * @param string $filename (Required) The file name for the object.
2706
	 * @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()>.
2707
	 * @return string The torrent URL, with authentication parameters if requested.
2708
	 * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/index.html?S3TorrentRetrieve.html Using BitTorrent to Retrieve Objects Stored in Amazon S3
2709
	 */
2710
	public function get_torrent_url($bucket, $filename, $preauth = 0)
2711
	{
2712
		return $this->get_object_url($bucket, $filename, $preauth, array(
2713
			'torrent' => true
2714
		));
2715
	}
2716
 
2717
 
2718
	/*%******************************************************************************************%*/
2719
	// VERSIONING
2720
 
2721
	/**
2722
	 * Enables versioning support for the specified Amazon S3 bucket.
2723
	 *
2724
	 * @param string $bucket (Required) The name of the bucket to use.
2725
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
2726
	 * 	<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>
2727
	 * 	<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>
2728
	 * 	<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>
2729
	 * 	<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>
2730
	 * 	<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>
2731
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
2732
	 * @link http://aws.amazon.com/mfa/ Multi-Factor Authentication
2733
	 */
2734
	public function enable_versioning($bucket, $opt = null)
2735
	{
2736
		if (!$opt) $opt = array();
2737
 
2738
		// Add this to our request
2739
		$opt['verb'] = 'PUT';
2740
		$opt['sub_resource'] = 'versioning';
2741
		$opt['headers'] = array(
2742
			'Content-Type' => 'application/xml'
2743
		);
2744
 
2745
		$xml = simplexml_load_string($this->base_versioning_xml);
2746
		$xml->addChild('Status', 'Enabled');
2747
 
2748
		// Enable MFA delete?
2749
		// @codeCoverageIgnoreStart
2750
		if (isset($opt['MFASerial']) && isset($opt['MFAToken']) && isset($opt['MFAStatus']))
2751
		{
2752
			$xml->addChild('MfaDelete', $opt['MFAStatus']);
2753
			$opt['headers']['x-amz-mfa'] = ($opt['MFASerial'] . ' ' . $opt['MFAToken']);
2754
		}
2755
		// @codeCoverageIgnoreEnd
2756
 
2757
		$opt['body'] = $xml->asXML();
2758
 
2759
		// Authenticate to S3
2760
		return $this->authenticate($bucket, $opt);
2761
	}
2762
 
2763
	/**
2764
	 * Disables versioning support for the specified Amazon S3 bucket.
2765
	 *
2766
	 * @param string $bucket (Required) The name of the bucket to use.
2767
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
2768
	 * 	<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>
2769
	 * 	<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>
2770
	 * 	<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>
2771
	 * 	<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>
2772
	 * 	<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>
2773
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
2774
	 * @link http://aws.amazon.com/mfa/ Multi-Factor Authentication
2775
	 */
2776
	public function disable_versioning($bucket, $opt = null)
2777
	{
2778
		if (!$opt) $opt = array();
2779
 
2780
		// Add this to our request
2781
		$opt['verb'] = 'PUT';
2782
		$opt['sub_resource'] = 'versioning';
2783
		$opt['headers'] = array(
2784
			'Content-Type' => 'application/xml'
2785
		);
2786
 
2787
		$xml = simplexml_load_string($this->base_versioning_xml);
2788
		$xml->addChild('Status', 'Suspended');
2789
 
2790
		// Enable MFA delete?
2791
		// @codeCoverageIgnoreStart
2792
		if (isset($opt['MFASerial']) && isset($opt['MFAToken']) && isset($opt['MFAStatus']))
2793
		{
2794
			$xml->addChild('MfaDelete', $opt['MFAStatus']);
2795
			$opt['headers']['x-amz-mfa'] = ($opt['MFASerial'] . ' ' . $opt['MFAToken']);
2796
		}
2797
		// @codeCoverageIgnoreEnd
2798
 
2799
		$opt['body'] = $xml->asXML();
2800
 
2801
		// Authenticate to S3
2802
		return $this->authenticate($bucket, $opt);
2803
	}
2804
 
2805
	/**
2806
	 * Gets an Amazon S3 bucket's versioning status.
2807
	 *
2808
	 * @param string $bucket (Required) The name of the bucket to use.
2809
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
2810
	 * 	<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>
2811
	 * 	<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>
2812
	 * 	<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>
2813
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
2814
	 */
2815
	public function get_versioning_status($bucket, $opt = null)
2816
	{
2817
		if (!$opt) $opt = array();
2818
		$opt['verb'] = 'GET';
2819
		$opt['sub_resource'] = 'versioning';
2820
 
2821
		// Authenticate to S3
2822
		return $this->authenticate($bucket, $opt);
2823
	}
2824
 
2825
	/**
2826
	 * Gets a list of all the versions of Amazon S3 objects in the specified bucket.
2827
	 *
2828
	 * @param string $bucket (Required) The name of the bucket to use.
2829
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
2830
	 * 	<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>
2831
	 * 	<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>
2832
	 * 	<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>
2833
	 * 	<li><code>prefix</code> - <code>string</code> - Optional - Restricts the response to only contain results that begin with the specified prefix.</li>
2834
	 * 	<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>
2835
	 * 	<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>
2836
	 * 	<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>
2837
	 * 	<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>
2838
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
2839
	 */
2840
	public function list_bucket_object_versions($bucket, $opt = null)
2841
	{
2842
		if (!$opt) $opt = array();
2843
		$opt['verb'] = 'GET';
2844
		$opt['sub_resource'] = 'versions';
2845
 
2846
		foreach (array('delimiter', 'key-marker', 'max-keys', 'prefix', 'version-id-marker') as $param)
2847
		{
2848
			if (isset($opt[$param]))
2849
			{
2850
				$opt['query_string'][$param] = $opt[$param];
2851
				unset($opt[$param]);
2852
			}
2853
		}
2854
 
2855
		// Authenticate to S3
2856
		return $this->authenticate($bucket, $opt);
2857
	}
2858
 
2859
 
2860
	/*%******************************************************************************************%*/
2861
	// BUCKET POLICIES
2862
 
2863
	/**
2864
	 * Sets the policy sub-resource for the specified Amazon S3 bucket. The specified policy replaces any
2865
	 * policy the bucket already has.
2866
	 *
2867
	 * To perform this operation, the caller must be authorized to set a policy for the bucket and have
2868
	 * PutPolicy permissions. If the caller does not have PutPolicy permissions for the bucket, Amazon S3
2869
	 * returns a `403 Access Denied` error. If the caller has the correct permissions but has not been
2870
	 * authorized by the bucket owner, Amazon S3 returns a `405 Method Not Allowed` error.
2871
	 *
2872
	 * @param string $bucket (Required) The name of the bucket to use.
2873
	 * @param CFPolicy $policy (Required) The JSON policy to use.
2874
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
2875
	 * 	<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>
2876
	 * 	<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>
2877
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
2878
	 * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/AccessPolicyLanguage.html Appendix: The Access Policy Language
2879
	 */
2880
	public function set_bucket_policy($bucket, CFPolicy $policy, $opt = null)
2881
	{
2882
		if (!$opt) $opt = array();
2883
		$opt['verb'] = 'PUT';
2884
		$opt['sub_resource'] = 'policy';
2885
		$opt['body'] = $policy->get_json();
2886
 
2887
		// Authenticate to S3
2888
		return $this->authenticate($bucket, $opt);
2889
	}
2890
 
2891
	/**
2892
	 * Gets the policy of the specified Amazon S3 bucket.
2893
	 *
2894
	 * To use this operation, the caller must have GetPolicy permissions for the specified bucket and must be
2895
	 * the bucket owner. If the caller does not have GetPolicy permissions, this method will generate a
2896
	 * `403 Access Denied` error. If the caller has the correct permissions but is not the bucket owner, this
2897
	 * method will generate a `405 Method Not Allowed` error. If the bucket does not have a policy defined for
2898
	 * it, this method will generate a `404 Policy Not Found` error.
2899
	 *
2900
	 * @param string $bucket (Required) The name of the bucket to use.
2901
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
2902
	 * 	<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>
2903
	 * 	<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>
2904
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
2905
	 */
2906
	public function get_bucket_policy($bucket, $opt = null)
2907
	{
2908
		if (!$opt) $opt = array();
2909
		$opt['verb'] = 'GET';
2910
		$opt['sub_resource'] = 'policy';
2911
 
2912
		// Authenticate to S3
2913
		return $this->authenticate($bucket, $opt);
2914
	}
2915
 
2916
	/**
2917
	 * Deletes the bucket policy for the specified Amazon S3 bucket. To delete the policy, the caller must
2918
	 * be the bucket owner and have `DeletePolicy` permissions for the specified bucket.
2919
	 *
2920
	 * @param string $bucket (Required) The name of the bucket to use.
2921
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
2922
	 * 	<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>
2923
	 * 	<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>
2924
	 * @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.
2925
	 */
2926
	public function delete_bucket_policy($bucket, $opt = null)
2927
	{
2928
		if (!$opt) $opt = array();
2929
		$opt['verb'] = 'DELETE';
2930
		$opt['sub_resource'] = 'policy';
2931
 
2932
		// Authenticate to S3
2933
		return $this->authenticate($bucket, $opt);
2934
	}
2935
 
2936
 
2937
	/*%******************************************************************************************%*/
2938
	// BUCKET NOTIFICATIONS
2939
 
2940
	/**
2941
	 * Enables notifications of specified events for an Amazon S3 bucket. Currently, the
2942
	 * `s3:ReducedRedundancyLostObject` event is the only event supported for notifications. The
2943
	 * `s3:ReducedRedundancyLostObject` event is triggered when Amazon S3 detects that it has lost all
2944
	 * copies of an Amazon S3 object and can no longer service requests for that object.
2945
	 *
2946
	 * If the bucket owner and Amazon SNS topic owner are the same, the bucket owner has permission to
2947
	 * publish notifications to the topic by default. Otherwise, the owner of the topic must create a
2948
	 * policy to enable the bucket owner to publish to the topic.
2949
	 *
2950
	 * By default, only the bucket owner can configure notifications on a bucket. However, bucket owners
2951
	 * can use bucket policies to grant permission to other users to set this configuration with the
2952
	 * `s3:PutBucketNotification` permission.
2953
	 *
2954
	 * After a PUT operation is called to configure notifications on a bucket, Amazon S3 publishes a test
2955
	 * notification to ensure that the topic exists and that the bucket owner has permission to publish
2956
	 * to the specified topic. If the notification is successfully published to the SNS topic, the PUT
2957
	 * operation updates the bucket configuration and returns the 200 OK responses with a
2958
	 * `x-amz-sns-test-message-id` header containing the message ID of the test notification sent to topic.
2959
	 *
2960
	 * @param string $bucket (Required) The name of the bucket to create bucket notifications for.
2961
	 * @param string $topic_arn (Required) The SNS topic ARN to send notifications to.
2962
	 * @param string $event (Required) The event type to listen for.
2963
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
2964
	 * 	<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>
2965
	 * 	<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>
2966
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
2967
	 * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/NotificationHowTo.html Setting Up Notification of Bucket Events
2968
	 */
2969
	public function create_bucket_notification($bucket, $topic_arn, $event, $opt = null)
2970
	{
2971
		if (!$opt) $opt = array();
2972
		$opt['verb'] = 'PUT';
2973
		$opt['sub_resource'] = 'notification';
2974
		$opt['headers'] = array(
2975
			'Content-Type' => 'application/xml'
2976
		);
2977
 
2978
		$xml = simplexml_load_string($this->base_notification_xml);
2979
		$topic_config = $xml->addChild('TopicConfiguration');
2980
		$topic_config->addChild('Topic', $topic_arn);
2981
		$topic_config->addChild('Event', $event);
2982
 
2983
		$opt['body'] = $xml->asXML();
2984
 
2985
		// Authenticate to S3
2986
		return $this->authenticate($bucket, $opt);
2987
	}
2988
 
2989
	/**
2990
	 * Gets the notification configuration of a bucket. Currently, the `s3:ReducedRedundancyLostObject` event
2991
	 * is the only event supported for notifications. The `s3:ReducedRedundancyLostObject` event is triggered
2992
	 * when Amazon S3 detects that it has lost all replicas of a Reduced Redundancy Storage object and can no
2993
	 * longer service requests for that object.
2994
	 *
2995
	 * If notifications are not enabled on the bucket, the operation returns an empty
2996
	 * `NotificatonConfiguration` element.
2997
	 *
2998
	 * By default, you must be the bucket owner to read the notification configuration of a bucket. However,
2999
	 * the bucket owner can use a bucket policy to grant permission to other users to read this configuration
3000
	 * with the `s3:GetBucketNotification` permission.
3001
	 *
3002
	 * @param string $bucket (Required) The name of the bucket to use.
3003
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
3004
	 * 	<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>
3005
	 * 	<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>
3006
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
3007
	 * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/NotificationHowTo.html Setting Up Notification of Bucket Events
3008
	 */
3009
	public function get_bucket_notifications($bucket, $opt = null)
3010
	{
3011
		if (!$opt) $opt = array();
3012
		$opt['verb'] = 'GET';
3013
		$opt['sub_resource'] = 'notification';
3014
 
3015
		// Authenticate to S3
3016
		return $this->authenticate($bucket, $opt);
3017
	}
3018
 
3019
	/**
3020
	 * Empties the list of SNS topics to send notifications to.
3021
	 *
3022
	 * @param string $bucket (Required) The name of the bucket to use.
3023
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
3024
	 * 	<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>
3025
	 * 	<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>
3026
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
3027
	 * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/NotificationHowTo.html Setting Up Notification of Bucket Events
3028
	 */
3029
	public function delete_bucket_notification($bucket, $opt = null)
3030
	{
3031
		if (!$opt) $opt = array();
3032
		$opt['verb'] = 'PUT';
3033
		$opt['sub_resource'] = 'notification';
3034
		$opt['body'] = $this->base_notification_xml;
3035
 
3036
		// Authenticate to S3
3037
		return $this->authenticate($bucket, $opt);
3038
	}
3039
 
3040
 
3041
	/*%******************************************************************************************%*/
3042
	// MULTIPART UPLOAD
3043
 
3044
	/**
3045
	 * Calculates the correct values for sequentially reading a file for multipart upload. This method should
3046
	 * be used in conjunction with <upload_part()>.
3047
	 *
3048
	 * @param integer $filesize (Required) The size in bytes of the entire file.
3049
	 * @param integer $part_size (Required) The size in bytes of the part of the file to send.
3050
	 * @return array An array containing key-value pairs. The keys are `seekTo` and `length`.
3051
	 */
3052
	public function get_multipart_counts($filesize, $part_size)
3053
	{
3054
		$i = 0;
3055
		$sizecount = $filesize;
3056
		$values = array();
3057
 
3058
		while ($sizecount > 0)
3059
		{
3060
			$sizecount -= $part_size;
3061
			$values[] = array(
3062
				'seekTo' => ($part_size * $i),
3063
				'length' => (($sizecount > 0) ? $part_size : ($sizecount + $part_size)),
3064
			);
3065
			$i++;
3066
		}
3067
 
3068
		return $values;
3069
	}
3070
 
3071
	/**
3072
	 * Initiates a multipart upload and returns an `UploadId`.
3073
	 *
3074
	 * @param string $bucket (Required) The name of the bucket to use.
3075
	 * @param string $filename (Required) The file name for the object.
3076
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
3077
	 * 	<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>
3078
	 * 	<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>
3079
	 * 	<li><code>encryption</code> - <code>string</code> - Optional - The algorithm to use for encrypting the object. [Allowed values: <code>AES256</code>]</li>
3080
	 * 	<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>
3081
	 * 	<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>
3082
	 * 	<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>
3083
	 * 	<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>
3084
	 * 	<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>
3085
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
3086
	 * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAccessPolicy.html REST Access Control Policy
3087
	 */
3088
	public function initiate_multipart_upload($bucket, $filename, $opt = null)
3089
	{
3090
		if (!$opt) $opt = array();
3091
 
3092
		// Add this to our request
3093
		$opt['verb'] = 'POST';
3094
		$opt['resource'] = $filename;
3095
		$opt['sub_resource'] = 'uploads';
3096
		$opt['body'] = '';
3097
 
3098
		// Handle content type. Can also be passed as an HTTP header.
3099
		if (isset($opt['contentType']))
3100
		{
3101
			$opt['headers']['Content-Type'] = $opt['contentType'];
3102
			unset($opt['contentType']);
3103
		}
3104
 
3105
		// Set a default content type.
3106
		if (!isset($opt['headers']['Content-Type']))
3107
		{
3108
			$opt['headers']['Content-Type'] = 'application/octet-stream';
3109
		}
3110
 
3111
		// Handle Access Control Lists. Can also be passed as an HTTP header.
3112
		if (isset($opt['acl']))
3113
		{
3114
			$opt['headers']['x-amz-acl'] = $opt['acl'];
3115
			unset($opt['acl']);
3116
		}
3117
 
3118
		// Handle storage settings. Can also be passed as an HTTP header.
3119
		if (isset($opt['storage']))
3120
		{
3121
			$opt['headers']['x-amz-storage-class'] = $opt['storage'];
3122
			unset($opt['storage']);
3123
		}
3124
 
3125
		// Handle encryption settings. Can also be passed as an HTTP header.
3126
		if (isset($opt['encryption']))
3127
		{
3128
			$opt['headers']['x-amz-server-side-encryption'] = $opt['encryption'];
3129
			unset($opt['encryption']);
3130
		}
3131
 
3132
		// Handle meta tags. Can also be passed as an HTTP header.
3133
		if (isset($opt['meta']))
3134
		{
3135
			foreach ($opt['meta'] as $meta_key => $meta_value)
3136
			{
3137
				// e.g., `My Meta Header` is converted to `x-amz-meta-my-meta-header`.
3138
				$opt['headers']['x-amz-meta-' . strtolower(str_replace(' ', '-', $meta_key))] = $meta_value;
3139
			}
3140
			unset($opt['meta']);
3141
		}
3142
 
3143
		// Authenticate to S3
3144
		return $this->authenticate($bucket, $opt);
3145
	}
3146
 
3147
	/**
3148
	 * Uploads a single part of a multipart upload. The part size cannot be smaller than 5 MB
3149
	 * or larger than 5 TB. A multipart upload can have no more than 10,000 parts.
3150
	 *
3151
	 * Amazon S3 charges for storage as well as requests to the service. Smaller part sizes (and more
3152
	 * requests) allow for faster failures and better upload reliability. Larger part sizes (and fewer
3153
	 * requests) costs slightly less but has lower upload reliability.
3154
	 *
3155
	 * @param string $bucket (Required) The name of the bucket to use.
3156
	 * @param string $filename (Required) The file name for the object.
3157
	 * @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()>.
3158
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
3159
	 * 	<li><code>fileUpload</code> - <code>string|resource</code> - Required - The URL/path for the file to upload or an open resource.</li>
3160
	 * 	<li><code>partNumber</code> - <code>integer</code> - Required - The part number order of the multipart upload.</li>
3161
	 * 	<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>
3162
	 * 	<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>
3163
	 * 	<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>
3164
	 * 	<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>
3165
	 * 	<li><code>seekTo</code> - <code>integer</code> - Optional - The starting position in bytes for the piece of the file/stream to upload.</li>
3166
	 * 	<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>
3167
	 * 	<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>
3168
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
3169
	 */
3170
	public function upload_part($bucket, $filename, $upload_id, $opt = null)
3171
	{
3172
		if (!$opt) $opt = array();
3173
 
3174
		// Add this to our request
3175
		$opt['verb'] = 'PUT';
3176
		$opt['resource'] = $filename;
3177
		$opt['uploadId'] = $upload_id;
3178
 
3179
		if (!isset($opt['fileUpload']) || !isset($opt['partNumber']))
3180
		{
3181
			throw new S3_Exception('The `fileUpload` and `partNumber` options are both required in ' . __FUNCTION__ . '().');
3182
		}
3183
 
3184
		// Handle expectation. Can also be passed as an HTTP header.
3185
		if (isset($opt['expect']))
3186
		{
3187
			$opt['headers']['Expect'] = $opt['expect'];
3188
			unset($opt['expect']);
3189
		}
3190
 
3191
		// Handle content length. Can also be passed as an HTTP header.
3192
		if (isset($opt['length']))
3193
		{
3194
			$opt['headers']['Content-Length'] = $opt['length'];
3195
			unset($opt['length']);
3196
		}
3197
 
3198
		// Handle content md5. Can also be passed as an HTTP header.
3199
		if (isset($opt['md5']))
3200
		{
3201
			$opt['headers']['Content-MD5'] = $opt['md5'];
3202
			unset($opt['md5']);
3203
		}
3204
 
3205
		// Authenticate to S3
3206
		return $this->authenticate($bucket, $opt);
3207
	}
3208
 
3209
	/**
3210
	 * Lists the completed parts of an in-progress multipart upload.
3211
	 *
3212
	 * @param string $bucket (Required) The name of the bucket to use.
3213
	 * @param string $filename (Required) The file name for the object.
3214
	 * @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()>.
3215
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
3216
	 * 	<li><code>max-parts</code> - <code>integer</code> - Optional - The maximum number of parts to return in the response body.</li>
3217
	 * 	<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>
3218
	 * 	<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>
3219
	 * 	<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>
3220
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
3221
	 */
3222
	public function list_parts($bucket, $filename, $upload_id, $opt = null)
3223
	{
3224
		if (!$opt) $opt = array();
3225
 
3226
		// Add this to our request
3227
		$opt['verb'] = 'GET';
3228
		$opt['resource'] = $filename;
3229
		$opt['uploadId'] = $upload_id;
3230
		$opt['query_string'] = array();
3231
 
3232
		foreach (array('max-parts', 'part-number-marker') as $param)
3233
		{
3234
			if (isset($opt[$param]))
3235
			{
3236
				$opt['query_string'][$param] = $opt[$param];
3237
				unset($opt[$param]);
3238
			}
3239
		}
3240
 
3241
		// Authenticate to S3
3242
		return $this->authenticate($bucket, $opt);
3243
	}
3244
 
3245
	/**
3246
	 * Aborts an in-progress multipart upload. This operation cannot be reversed.
3247
	 *
3248
	 * @param string $bucket (Required) The name of the bucket to use.
3249
	 * @param string $filename (Required) The file name for the object.
3250
	 * @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()>.
3251
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
3252
	 * 	<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>
3253
	 * 	<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>
3254
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
3255
	 */
3256
	public function abort_multipart_upload($bucket, $filename, $upload_id, $opt = null)
3257
	{
3258
		if (!$opt) $opt = array();
3259
 
3260
		// Add this to our request
3261
		$opt['verb'] = 'DELETE';
3262
		$opt['resource'] = $filename;
3263
		$opt['uploadId'] = $upload_id;
3264
 
3265
		// Authenticate to S3
3266
		return $this->authenticate($bucket, $opt);
3267
	}
3268
 
3269
	/**
3270
	 * Completes an in-progress multipart upload. A multipart upload is completed by describing the part
3271
	 * numbers and corresponding ETag values in order, and submitting that data to Amazon S3 as an XML document.
3272
	 *
3273
	 * @param string $bucket (Required) The name of the bucket to use.
3274
	 * @param string $filename (Required) The file name for the object.
3275
	 * @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()>.
3276
	 * @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()>.
3277
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
3278
	 * 	<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>
3279
	 * 	<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>
3280
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
3281
	 */
3282
	public function complete_multipart_upload($bucket, $filename, $upload_id, $parts, $opt = null)
3283
	{
3284
		if (!$opt) $opt = array();
3285
 
3286
		// Add this to our request
3287
		$opt['verb'] = 'POST';
3288
		$opt['resource'] = $filename;
3289
		$opt['uploadId'] = $upload_id;
3290
		$opt['headers'] = array(
3291
			'Content-Type' => 'application/xml'
3292
		);
3293
 
3294
		// Disable Content-MD5 calculation for this operation
3295
		$opt['NoContentMD5'] = true;
3296
 
3297
		if (is_string($parts))
3298
		{
3299
			// Assume it's the intended XML.
3300
			$opt['body'] = $parts;
3301
		}
3302
		elseif ($parts instanceof SimpleXMLElement)
3303
		{
3304
			// Assume it's a SimpleXMLElement object representing the XML.
3305
			$opt['body'] = $parts->asXML();
3306
		}
3307
		elseif (is_array($parts) || $parts instanceof CFResponse)
3308
		{
3309
			$xml = simplexml_load_string($this->complete_mpu_xml);
3310
 
3311
			if (is_array($parts))
3312
			{
3313
				// Generate the appropriate XML.
3314
				foreach ($parts as $node)
3315
				{
3316
					$part = $xml->addChild('Part');
3317
					$part->addChild('PartNumber', $node['PartNumber']);
3318
					$part->addChild('ETag', $node['ETag']);
3319
				}
3320
 
3321
			}
3322
			elseif ($parts instanceof CFResponse)
3323
			{
3324
				// Assume it's a response from list_parts().
3325
				foreach ($parts->body->Part as $node)
3326
				{
3327
					$part = $xml->addChild('Part');
3328
					$part->addChild('PartNumber', (string) $node->PartNumber);
3329
					$part->addChild('ETag', (string) $node->ETag);
3330
				}
3331
			}
3332
 
3333
			$opt['body'] = $xml->asXML();
3334
		}
3335
 
3336
		// Authenticate to S3
3337
		return $this->authenticate($bucket, $opt);
3338
	}
3339
 
3340
	/**
3341
	 * Lists the in-progress multipart uploads.
3342
	 *
3343
	 * @param string $bucket (Required) The name of the bucket to use.
3344
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
3345
	 * 	<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>
3346
	 * 	<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>
3347
	 * 	<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>
3348
	 * 	<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>
3349
	 * 	<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>
3350
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
3351
	 */
3352
	public function list_multipart_uploads($bucket, $opt = null)
3353
	{
3354
		if (!$opt) $opt = array();
3355
 
3356
		// Add this to our request
3357
		$opt['verb'] = 'GET';
3358
		$opt['sub_resource'] = 'uploads';
3359
 
3360
		foreach (array('key-marker', 'max-uploads', 'upload-id-marker') as $param)
3361
		{
3362
			if (isset($opt[$param]))
3363
			{
3364
				$opt['query_string'][$param] = $opt[$param];
3365
				unset($opt[$param]);
3366
			}
3367
		}
3368
 
3369
		// Authenticate to S3
3370
		return $this->authenticate($bucket, $opt);
3371
	}
3372
 
3373
	/**
3374
	 * Since Amazon S3's standard <copy_object()> operation only supports copying objects that are smaller than
3375
	 * 5 GB, the ability to copy large objects (greater than 5 GB) requires the use of "Multipart Copy".
3376
	 *
3377
	 * Copying large objects requires the developer to initiate a new multipart "upload", copy pieces of the
3378
	 * large object (specifying a range of bytes up to 5 GB from the large source file), then complete the
3379
	 * multipart "upload".
3380
	 *
3381
	 * NOTE: <strong>This is a synchronous operation</strong>, not an <em>asynchronous</em> operation, which means
3382
	 * that Amazon S3 will not return a response for this operation until the copy has completed across the Amazon
3383
	 * S3 server fleet. Copying objects within a single region will complete more quickly than copying objects
3384
	 * <em>across</em> regions. The synchronous nature of this operation is different from other services where
3385
	 * responses are typically returned immediately, even if the operation itself has not yet been completed on
3386
	 * the server-side.
3387
	 *
3388
	 * @param array $source (Required) The bucket and file name to copy from. The following keys must be set: <ul>
3389
	 * 	<li><code>bucket</code> - <code>string</code> - Required - Specifies the name of the bucket containing the source object.</li>
3390
	 * 	<li><code>filename</code> - <code>string</code> - Required - Specifies the file name of the source object to copy.</li></ul>
3391
	 * @param array $dest (Required) The bucket and file name to copy to. The following keys must be set: <ul>
3392
	 * 	<li><code>bucket</code> - <code>string</code> - Required - Specifies the name of the bucket to copy the object to.</li>
3393
	 * 	<li><code>filename</code> - <code>string</code> - Required - Specifies the file name to copy the object to.</li></ul>
3394
	 * @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()>.
3395
	 * @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.
3396
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
3397
	 * 	<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>
3398
	 * 	<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>
3399
	 * 	<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>
3400
	 * 	<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>
3401
	 * 	<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>
3402
	 * 	<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>
3403
	 * 	<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>
3404
	 * 	<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>
3405
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
3406
	 */
3407
	public function copy_part($source, $dest, $upload_id, $part_number, $opt = null)
3408
	{
3409
		if (!$opt) $opt = array();
3410
 
3411
		// Add this to our request
3412
		$opt['verb'] = 'PUT';
3413
		$opt['resource'] = $dest['filename'];
3414
		$opt['uploadId'] = $upload_id;
3415
		$opt['partNumber'] = $part_number;
3416
 
3417
		// Handle copy source
3418
		if (isset($source['bucket']) && isset($source['filename']))
3419
		{
3420
			$opt['headers']['x-amz-copy-source'] = '/' . $source['bucket'] . '/' . rawurlencode($source['filename'])
3421
				. (isset($opt['versionId']) ? ('?' . 'versionId=' . rawurlencode($opt['versionId'])) : ''); // Append the versionId to copy, if available
3422
			unset($opt['versionId']);
3423
		}
3424
 
3425
		// Handle conditional-copy parameters
3426
		if (isset($opt['ifMatch']))
3427
		{
3428
			$opt['headers']['x-amz-copy-source-if-match'] = $opt['ifMatch'];
3429
			unset($opt['ifMatch']);
3430
		}
3431
		if (isset($opt['ifNoneMatch']))
3432
		{
3433
			$opt['headers']['x-amz-copy-source-if-none-match'] = $opt['ifNoneMatch'];
3434
			unset($opt['ifNoneMatch']);
3435
		}
3436
		if (isset($opt['ifUnmodifiedSince']))
3437
		{
3438
			$opt['headers']['x-amz-copy-source-if-unmodified-since'] = $opt['ifUnmodifiedSince'];
3439
			unset($opt['ifUnmodifiedSince']);
3440
		}
3441
		if (isset($opt['ifModifiedSince']))
3442
		{
3443
			$opt['headers']['x-amz-copy-source-if-modified-since'] = $opt['ifModifiedSince'];
3444
			unset($opt['ifModifiedSince']);
3445
		}
3446
 
3447
		// Partial content range
3448
		if (isset($opt['range']))
3449
		{
3450
			$opt['headers']['x-amz-copy-source-range'] = 'bytes=' . $opt['range'];
3451
		}
3452
 
3453
		// Authenticate to S3
3454
		return $this->authenticate($dest['bucket'], $opt);
3455
	}
3456
 
3457
	/**
3458
	 * Creates an Amazon S3 object using the multipart upload APIs. It is analogous to <create_object()>.
3459
	 *
3460
	 * While each individual part of a multipart upload can hold up to 5 GB of data, this method limits the
3461
	 * part size to a maximum of 500 MB. The combined size of all parts can not exceed 5 TB of data. When an
3462
	 * object is stored in Amazon S3, the data is streamed to multiple storage servers in multiple data
3463
	 * centers. This ensures the data remains available in the event of internal network or hardware failure.
3464
	 *
3465
	 * Amazon S3 charges for storage as well as requests to the service. Smaller part sizes (and more
3466
	 * requests) allow for faster failures and better upload reliability. Larger part sizes (and fewer
3467
	 * requests) costs slightly less but has lower upload reliability.
3468
	 *
3469
	 * In certain cases with large objects, it's possible for this method to attempt to open more file system
3470
	 * connections than allowed by the OS. In this case, either
3471
	 * <a href="https://forums.aws.amazon.com/thread.jspa?threadID=70216">increase the number of connections
3472
	 * allowed</a> or increase the value of the <code>partSize</code> parameter to use a larger part size.
3473
	 *
3474
	 * @param string $bucket (Required) The name of the bucket to use.
3475
	 * @param string $filename (Required) The file name for the object.
3476
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
3477
	 * 	<li><code>fileUpload</code> - <code>string|resource</code> - Required - The URL/path for the file to upload, or an open resource.</li>
3478
	 * 	<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>
3479
	 * 	<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>
3480
	 * 	<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>
3481
	 * 	<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>
3482
	 * 	<li><code>limit</code> - <code>integer</code> - Optional - The maximum number of concurrent uploads done by cURL. Gets passed to <code>CFBatchRequest</code>.</li>
3483
	 * 	<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>
3484
	 * 	<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>
3485
	 * 	<li><code>seekTo</code> - <code>integer</code> - Optional - The starting position in bytes for the first piece of the file/stream to upload.</li>
3486
	 * 	<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>
3487
	 * 	<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>
3488
	 * 	<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>
3489
	 * 	<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>
3490
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
3491
	 * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAccessPolicy.html REST Access Control Policy
3492
	 */
3493
	public function create_mpu_object($bucket, $filename, $opt = null)
3494
	{
3495
		if ($this->use_batch_flow)
3496
		{
3497
			throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested');
3498
		}
3499
 
3500
		if (!$opt) $opt = array();
3501
 
3502
		// Handle content length. Can also be passed as an HTTP header.
3503
		if (isset($opt['length']))
3504
		{
3505
			$opt['headers']['Content-Length'] = $opt['length'];
3506
			unset($opt['length']);
3507
		}
3508
 
3509
		if (!isset($opt['fileUpload']))
3510
		{
3511
			throw new S3_Exception('The `fileUpload` option is required in ' . __FUNCTION__ . '().');
3512
		}
3513
		elseif (is_resource($opt['fileUpload']))
3514
		{
3515
			$opt['limit'] = 1; // We can only read from this one resource.
3516
			$upload_position = isset($opt['seekTo']) ? (integer) $opt['seekTo'] : ftell($opt['fileUpload']);
3517
			$upload_filesize = isset($opt['headers']['Content-Length']) ? (integer) $opt['headers']['Content-Length'] : null;
3518
 
3519
			if (!isset($upload_filesize) && $upload_position !== false)
3520
			{
3521
				$stats = fstat($opt['fileUpload']);
3522
 
3523
				if ($stats && $stats['size'] >= 0)
3524
				{
3525
					$upload_filesize = $stats['size'] - $upload_position;
3526
				}
3527
			}
3528
		}
3529
		else
3530
		{
3531
			$upload_position = isset($opt['seekTo']) ? (integer) $opt['seekTo'] : 0;
3532
 
3533
			if (isset($opt['headers']['Content-Length']))
3534
			{
3535
				$upload_filesize = (integer) $opt['headers']['Content-Length'];
3536
			}
3537
			else
3538
			{
3539
				$upload_filesize = filesize($opt['fileUpload']);
3540
 
3541
				if ($upload_filesize !== false)
3542
				{
3543
					$upload_filesize -= $upload_position;
3544
				}
3545
			}
3546
		}
3547
 
3548
		if ($upload_position === false || !isset($upload_filesize) || $upload_filesize === false || $upload_filesize < 0)
3549
		{
3550
			throw new S3_Exception('The size of `fileUpload` cannot be determined in ' . __FUNCTION__ . '().');
3551
		}
3552
 
3553
		// Handle part size
3554
		if (isset($opt['partSize']))
3555
		{
3556
			// If less that 5 MB...
3557
			if ((integer) $opt['partSize'] < 5242880)
3558
			{
3559
				$opt['partSize'] = 5242880; // 5 MB
3560
			}
3561
			// If more than 500 MB...
3562
			elseif ((integer) $opt['partSize'] > 524288000)
3563
			{
3564
				$opt['partSize'] = 524288000; // 500 MB
3565
			}
3566
		}
3567
		else
3568
		{
3569
			$opt['partSize'] = 52428800; // 50 MB
3570
		}
3571
 
3572
		// If the upload size is smaller than the piece size, failover to create_object().
3573
		if ($upload_filesize < $opt['partSize'] && !isset($opt['uploadId']))
3574
		{
3575
			return $this->create_object($bucket, $filename, $opt);
3576
		}
3577
 
3578
		// Initiate multipart upload
3579
		if (isset($opt['uploadId']))
3580
		{
3581
			$upload_id = $opt['uploadId'];
3582
		}
3583
		else
3584
		{
3585
			// Compose options for initiate_multipart_upload().
3586
			$_opt = array();
3587
			foreach (array('contentType', 'acl', 'storage', 'headers', 'meta') as $param)
3588
			{
3589
				if (isset($opt[$param]))
3590
				{
3591
					$_opt[$param] = $opt[$param];
3592
				}
3593
			}
3594
 
3595
			$upload = $this->initiate_multipart_upload($bucket, $filename, $_opt);
3596
			if (!$upload->isOK())
3597
			{
3598
				return $upload;
3599
			}
3600
 
3601
			// Fetch the UploadId
3602
			$upload_id = (string) $upload->body->UploadId;
3603
		}
3604
 
3605
		// Get the list of pieces
3606
		$pieces = $this->get_multipart_counts($upload_filesize, (integer) $opt['partSize']);
3607
 
3608
		// Queue batch requests
3609
		$batch = new CFBatchRequest(isset($opt['limit']) ? (integer) $opt['limit'] : null);
3610
		foreach ($pieces as $i => $piece)
3611
		{
3612
			$this->batch($batch)->upload_part($bucket, $filename, $upload_id, array(
3613
				'expect' => '100-continue',
3614
				'fileUpload' => $opt['fileUpload'],
3615
				'partNumber' => ($i + 1),
3616
				'seekTo' => $upload_position + (integer) $piece['seekTo'],
3617
				'length' => (integer) $piece['length'],
3618
			));
3619
		}
3620
 
3621
		// Send batch requests
3622
		$batch_responses = $this->batch($batch)->send();
3623
		if (!$batch_responses->areOK())
3624
		{
3625
			return $batch_responses;
3626
		}
3627
 
3628
		// Compose completion XML
3629
		$parts = array();
3630
		foreach ($batch_responses as $i => $response)
3631
		{
3632
			$parts[] = array('PartNumber' => ($i + 1), 'ETag' => $response->header['etag']);
3633
		}
3634
 
3635
		return $this->complete_multipart_upload($bucket, $filename, $upload_id, $parts);
3636
	}
3637
 
3638
	/**
3639
	 * Aborts all multipart uploads initiated before the specified date. This operation cannot be reversed.
3640
	 *
3641
	 * @param string $bucket (Required) The name of the bucket to use.
3642
	 * @param string|integer $when (Optional) The time and date to use for comparison. Accepts any value that <php:strtotime()> understands.
3643
	 * @return CFArray A <CFArray> containing a series of 0 or more <CFResponse> objects, containing a parsed HTTP response.
3644
	 */
3645
	public function abort_multipart_uploads_by_date($bucket, $when = null)
3646
	{
3647
		if ($this->use_batch_flow)
3648
		{
3649
			// @codeCoverageIgnoreStart
3650
			throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested');
3651
			// @codeCoverageIgnoreEnd
3652
		}
3653
 
3654
		$when = $when ? $when : time();
3655
		$handles = array();
3656
		$data = $this->list_multipart_uploads($bucket)->body;
3657
		$when = is_int($when) ? $when : strtotime($when);
3658
 
3659
		if (!($data instanceof CFSimpleXML))
3660
		{
3661
			return false;
3662
		}
3663
 
3664
		$list = $data->query('descendant-or-self::Upload/Initiated');
3665
 
3666
		if (count($list) > 0)
3667
		{
3668
			foreach ($list as $node)
3669
			{
3670
				if (strtotime((string) $node) < $when)
3671
				{
3672
					$q = new CFBatchRequest();
3673
					$parent = $node->parent();
3674
 
3675
					$upload_id = $parent
3676
						->query('descendant-or-self::UploadId')
3677
						->first()
3678
						->to_string();
3679
 
3680
					$filename = $parent
3681
						->query('descendant-or-self::Key')
3682
						->first()
3683
						->to_string();
3684
 
3685
					$handles[] = $this->abort_multipart_upload($bucket, $filename, $upload_id, array(
3686
						'returnCurlHandle' => true
3687
					));
3688
				}
3689
			}
3690
 
3691
			$http = new CFRequest();
3692
			$responses = $http->send_multi_request($handles);
3693
 
3694
			if (is_array($responses) && count($responses) > 0)
3695
			{
3696
				return new CFArray($responses);
3697
			}
3698
		}
3699
 
3700
		return new CFArray();
3701
	}
3702
 
3703
 
3704
	/*%******************************************************************************************%*/
3705
	// WEBSITE CONFIGURATION
3706
 
3707
	/**
3708
	 * Enables and configures an Amazon S3 website using the corresponding bucket as the content source.
3709
	 * The website will have one default domain name associated with it, which is the bucket name. If you
3710
	 * attempt to configure an Amazon S3 website for a bucket whose name is not compatible with DNS,
3711
	 * Amazon S3 returns an <code>InvalidBucketName</code> error. For more information on bucket names and DNS,
3712
	 * refer to <a href="http://docs.amazonwebservices.com/AmazonS3/latest/dev/BucketRestrictions.html">Bucket Restrictions and Limitations.</a>
3713
	 *
3714
	 * To visit the bucket as a website a new endpoint is created in the following pattern:
3715
	 * <code>http://&lt;bucketName&gt;.s3-website-&lt;region&gt;.amazonaws.com</code>. This is a sample URL
3716
	 * for a bucket called <code>example-bucket</code> in the <code>us-east-1</code> region.
3717
	 * (e.g., <code>http://example-bucket.s3-website-us-east-1.amazonaws.com</code>)
3718
	 *
3719
	 * @param string $bucket (Required) The name of the bucket to use.
3720
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
3721
	 * 	<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>
3722
	 * 	<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>
3723
	 * 	<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>
3724
	 * 	<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>
3725
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
3726
	 */
3727
	public function create_website_config($bucket, $opt = null)
3728
	{
3729
		if (!$opt) $opt = array();
3730
		$opt['verb'] = 'PUT';
3731
		$opt['sub_resource'] = 'website';
3732
 
3733
		$xml = simplexml_load_string($this->website_config_xml);
3734
		if (isset($opt['indexDocument']))
3735
		{
3736
			$xml->IndexDocument->Suffix = $opt['indexDocument'];
3737
		}
3738
		if (isset($opt['errorDocument']))
3739
		{
3740
			$xml->ErrorDocument->Key = $opt['errorDocument'];
3741
		}
3742
 
3743
		$opt['body'] = $xml->asXML();
3744
 
3745
		// Authenticate to S3
3746
		return $this->authenticate($bucket, $opt);
3747
	}
3748
 
3749
	/**
3750
	 * Retrieves the website configuration for a bucket. The contents of this response are identical to the
3751
	 * content submitted by the user during the website creation operation. If a website configuration has
3752
	 * never been set, Amazon S3 will return a 404 error.
3753
	 *
3754
	 * @param string $bucket (Required) The name of the bucket to use.
3755
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
3756
	 * 	<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>
3757
	 * 	<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>
3758
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
3759
	 */
3760
	public function get_website_config($bucket, $opt = null)
3761
	{
3762
		if (!$opt) $opt = array();
3763
		$opt['verb'] = 'GET';
3764
		$opt['sub_resource'] = 'website';
3765
		$opt['headers'] = array(
3766
			'Content-Type' => 'application/xml'
3767
		);
3768
 
3769
		// Authenticate to S3
3770
		return $this->authenticate($bucket, $opt);
3771
	}
3772
 
3773
	/**
3774
	 * Removes the website configuration for a bucket.
3775
	 *
3776
	 * @param string $bucket (Required) The name of the bucket to use.
3777
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
3778
	 * 	<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>
3779
	 * 	<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>
3780
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
3781
	 */
3782
	public function delete_website_config($bucket, $opt = null)
3783
	{
3784
		if (!$opt) $opt = array();
3785
		$opt['verb'] = 'DELETE';
3786
		$opt['sub_resource'] = 'website';
3787
 
3788
		// Authenticate to S3
3789
		return $this->authenticate($bucket, $opt);
3790
	}
3791
 
3792
 
3793
	/*%******************************************************************************************%*/
3794
	// OBJECT EXPIRATION
3795
 
3796
	/**
3797
	 * Enables the ability to specify an expiry period for objects when an object should be deleted,
3798
	 * measured as number of days from creation time. Amazon S3 guarantees that the object will be
3799
	 * deleted when the expiration time is passed.
3800
	 *
3801
	 * @param string $bucket (Required) The name of the bucket to use.
3802
	 * @param array $opt (Optional) An associative array of parameters that can have the following keys: <ul>
3803
	 * 	<li><code>rules</code> - <code>string</code> - Required - The object expiration rule-sets to apply to the bucket. <ul>
3804
	 * 		<li><code>x</code> - <code>array</code> - Required - This represents a simple array index. <ul>
3805
	 * 			<li><code>prefix</code> - <code>string</code> - Required - The Amazon S3 object prefix which targets the file(s) for expiration.</li>
3806
	 * 			<li><code>expiration</code> - <code>array</code> - Required - The container for the unit of measurement by which the expiration time is calculated. <ul>
3807
	 * 				<li><code>days</code> - <code>integer</code> - Required - The number of days until the targetted objects expire from the bucket.</li>
3808
	 * 			</ul></li>
3809
	 * 			<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>
3810
	 * 		</ul></li>
3811
	 * 	</ul></li>
3812
	 * 	<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>
3813
	 * 	<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>
3814
	 * @return CFResponse A <CFResponse> object containing a parsed HTTP response.
3815
	 */
3816
	public function create_object_expiration_config($bucket, $opt = null)
3817
	{
3818
		if (!$opt) $opt = array();
3819
		$opt['verb'] = 'PUT';
3820
		$opt['sub_resource'] = 'lifecycle';
3821
		$opt['headers'] = array(
3822
			'Content-Type' => 'application/xml'
3823
		);
3824
 
3825
		$xml = simplexml_load_string($this->object_expiration_xml);
3826
 
3827
		if (isset($opt['rules']) && is_array($opt['rules']) && count($opt['rules']))
3828
		{
3829
			foreach ($opt['rules'] as $rule)
3830
			{
3831
				$xrule = $xml->addChild('Rule');
3832
 
3833
				// Prefix
3834
				if (isset($rule['prefix']))
3835
				{
3836
					$xrule->addChild('Prefix', $rule['prefix']);
3837
				}
3838
				else
3839
				{
3840
					throw new S3_Exception('The each rule requires a "prefix" in the ' . __FUNCTION__ . ' method.');
3841
				}
3842
 
3843
				// Status
3844
				$enabled = 'Enabled';
3845
				if (isset($rule['enabled']))
3846
				{
3847
					if (is_bool($rule['enabled'])) // Boolean
3848
					{
3849
						$enabled = $rule['enabled'] ? 'Enabled' : 'Disabled';
3850
					}
3851
					elseif (is_string($rule['enabled'])) // String
3852
					{
3853
						$enabled = (strtolower($rule['enabled']) === 'true') ? 'Enabled' : 'Disabled';
3854
					}
3855
 
3856
					$xrule->addChild('Status', $enabled);
3857
				}
3858
				else
3859
				{
3860
					$xrule->addChild('Status', 'Enabled');
3861
				}
3862
 
3863
				// Expiration
3864
				if (isset($rule['expiration']))
3865
				{
3866
					$xexpiration = $xrule->addChild('Expiration');
3867
 
3868
					if (isset($rule['expiration']['days']))
3869
					{
3870
						$xexpiration->addChild('Days', $rule['expiration']['days']);
3871
					}
3872
				}
3873
				else
3874
				{
3875
					throw new S3_Exception('The each rule requires a "expiration" in the ' . __FUNCTION__ . ' method.');
3876
				}
3877
			}
3878
		}
3879
 
3880
		$opt['body'] = $xml->asXML();
3881
 
3882
		// Authenticate to S3
3883
		return $this->authenticate($bucket, $opt);
3884
	}
3885
 
3886
	public function get_object_expiration_config($bucket, $opt = null)
3887
	{
3888
		if (!$opt) $opt = array();
3889
		$opt['verb'] = 'GET';
3890
		$opt['sub_resource'] = 'lifecycle';
3891
 
3892
		// Authenticate to S3
3893
		return $this->authenticate($bucket, $opt);
3894
	}
3895
 
3896
	public function delete_object_expiration_config($bucket, $opt = null)
3897
	{
3898
		if (!$opt) $opt = array();
3899
		$opt['verb'] = 'DELETE';
3900
		$opt['sub_resource'] = 'lifecycle';
3901
 
3902
		// Authenticate to S3
3903
		return $this->authenticate($bucket, $opt);
3904
	}
3905
 
3906
 
3907
	/*%******************************************************************************************%*/
3908
	// MISCELLANEOUS
3909
 
3910
	/**
3911
	 * Gets the canonical user ID and display name from the Amazon S3 server.
3912
	 *
3913
	 * @return array An associative array containing the `id` and `display_name` values.
3914
	 */
3915
	public function get_canonical_user_id()
3916
	{
3917
		if ($this->use_batch_flow)
3918
		{
3919
			throw new S3_Exception(__FUNCTION__ . '() cannot be batch requested');
3920
		}
3921
 
3922
		$id = $this->list_buckets();
3923
 
3924
		return array(
3925
			'id' => (string) $id->body->Owner->ID,
3926
			'display_name' => (string) $id->body->Owner->DisplayName
3927
		);
3928
	}
3929
 
3930
	/**
3931
	 * Loads and registers the S3StreamWrapper class as a stream wrapper.
3932
	 *
3933
	 * @param string $protocol (Optional) The name of the protocol to register.
3934
	 * @return boolean Whether or not the registration succeeded.
3935
	 */
3936
	public function register_stream_wrapper($protocol = 's3')
3937
	{
3938
		require_once dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'extensions'
3939
			. DIRECTORY_SEPARATOR . 's3streamwrapper.class.php';
3940
 
3941
		return S3StreamWrapper::register($this, $protocol);
3942
	}
3943
}