13 * to license@zend.com so we can send you a copy immediately. |
13 * to license@zend.com so we can send you a copy immediately. |
14 * |
14 * |
15 * @category Zend |
15 * @category Zend |
16 * @package Zend_Service |
16 * @package Zend_Service |
17 * @subpackage Amazon_S3 |
17 * @subpackage Amazon_S3 |
18 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
18 * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) |
19 * @license http://framework.zend.com/license/new-bsd New BSD License |
19 * @license http://framework.zend.com/license/new-bsd New BSD License |
20 * @version $Id: S3.php 23224 2010-10-22 13:45:57Z matthew $ |
20 * @version $Id: S3.php 24593 2012-01-05 20:35:02Z matthew $ |
21 */ |
21 */ |
22 |
22 |
23 /** |
23 /** |
24 * @see Zend_Service_Amazon_Abstract |
24 * @see Zend_Service_Amazon_Abstract |
25 */ |
25 */ |
34 * Amazon S3 PHP connection class |
34 * Amazon S3 PHP connection class |
35 * |
35 * |
36 * @category Zend |
36 * @category Zend |
37 * @package Zend_Service |
37 * @package Zend_Service |
38 * @subpackage Amazon_S3 |
38 * @subpackage Amazon_S3 |
39 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
39 * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) |
40 * @license http://framework.zend.com/license/new-bsd New BSD License |
40 * @license http://framework.zend.com/license/new-bsd New BSD License |
41 * @see http://docs.amazonwebservices.com/AmazonS3/2006-03-01/ |
41 * @see http://docs.amazonwebservices.com/AmazonS3/2006-03-01/ |
42 */ |
42 */ |
43 class Zend_Service_Amazon_S3 extends Zend_Service_Amazon_Abstract |
43 class Zend_Service_Amazon_S3 extends Zend_Service_Amazon_Abstract |
44 { |
44 { |
155 * @return boolean |
155 * @return boolean |
156 */ |
156 */ |
157 public function createBucket($bucket, $location = null) |
157 public function createBucket($bucket, $location = null) |
158 { |
158 { |
159 $this->_validBucketName($bucket); |
159 $this->_validBucketName($bucket); |
160 |
160 $headers=array(); |
161 if($location) { |
161 if($location) { |
162 $data = '<CreateBucketConfiguration><LocationConstraint>'.$location.'</LocationConstraint></CreateBucketConfiguration>'; |
162 $data = '<CreateBucketConfiguration><LocationConstraint>'.$location.'</LocationConstraint></CreateBucketConfiguration>'; |
163 } |
163 $headers[self::S3_CONTENT_TYPE_HEADER]= 'text/plain'; |
164 else { |
164 $headers['Content-size']= strlen($data); |
|
165 } else { |
165 $data = null; |
166 $data = null; |
166 } |
167 } |
167 $response = $this->_makeRequest('PUT', $bucket, null, array(), $data); |
168 $response = $this->_makeRequest('PUT', $bucket, null, $headers, $data); |
168 |
169 |
169 return ($response->getStatus() == 200); |
170 return ($response->getStatus() == 200); |
170 } |
171 } |
171 |
172 |
172 /** |
173 /** |
271 $objects = $this->getObjectsByBucket($bucket); |
272 $objects = $this->getObjectsByBucket($bucket); |
272 if (!$objects) { |
273 if (!$objects) { |
273 return false; |
274 return false; |
274 } |
275 } |
275 |
276 |
276 foreach ($objects as $object) { |
277 while (!empty($objects)) { |
277 $this->removeObject("$bucket/$object"); |
278 foreach ($objects as $object) { |
278 } |
279 $this->removeObject("$bucket/$object"); |
|
280 } |
|
281 $params= array ( |
|
282 'marker' => $objects[count($objects)-1] |
|
283 ); |
|
284 $objects = $this->getObjectsByBucket($bucket,$params); |
|
285 } |
|
286 |
279 return true; |
287 return true; |
280 } |
288 } |
281 |
289 |
282 /** |
290 /** |
283 * List the objects in a bucket. |
291 * List the objects in a bucket. |
311 } |
319 } |
312 } |
320 } |
313 |
321 |
314 return $objects; |
322 return $objects; |
315 } |
323 } |
316 |
324 /** |
|
325 * List the objects and common prefixes in a bucket. |
|
326 * |
|
327 * Provides the list of object keys and common prefixes that are contained in the bucket. Valid params include the following. |
|
328 * prefix - Limits the response to keys which begin with the indicated prefix. You can use prefixes to separate a bucket into different sets of keys in a way similar to how a file system uses folders. |
|
329 * marker - Indicates where in the bucket to begin listing. The list will only include keys that occur lexicographically after marker. This is convenient for pagination: To get the next page of results use the last key of the current page as the marker. |
|
330 * max-keys - The maximum number of keys you'd like to see in the response body. The server might return fewer than this many keys, but will not return more. |
|
331 * delimiter - Causes keys that contain the same string between the prefix and the first occurrence of the delimiter to be rolled up into a single result element in the CommonPrefixes collection. These rolled-up keys are not returned elsewhere in the response. |
|
332 * |
|
333 * @see ZF-11401 |
|
334 * @param string $bucket |
|
335 * @param array $params S3 GET Bucket Paramater |
|
336 * @return array|false |
|
337 */ |
|
338 public function getObjectsAndPrefixesByBucket($bucket, $params = array()) |
|
339 { |
|
340 $response = $this->_makeRequest('GET', $bucket, $params); |
|
341 |
|
342 if ($response->getStatus() != 200) { |
|
343 return false; |
|
344 } |
|
345 |
|
346 $xml = new SimpleXMLElement($response->getBody()); |
|
347 |
|
348 $objects = array(); |
|
349 if (isset($xml->Contents)) { |
|
350 foreach ($xml->Contents as $contents) { |
|
351 foreach ($contents->Key as $object) { |
|
352 $objects[] = (string)$object; |
|
353 } |
|
354 } |
|
355 } |
|
356 $prefixes = array(); |
|
357 if (isset($xml->CommonPrefixes)) { |
|
358 foreach ($xml->CommonPrefixes as $prefix) { |
|
359 foreach ($prefix->Prefix as $object) { |
|
360 $prefixes[] = (string)$object; |
|
361 } |
|
362 } |
|
363 } |
|
364 |
|
365 return array( |
|
366 'objects' => $objects, |
|
367 'prefixes' => $prefixes |
|
368 ); |
|
369 } |
317 /** |
370 /** |
318 * Make sure the object name is valid |
371 * Make sure the object name is valid |
319 * |
372 * |
320 * @param string $object |
373 * @param string $object |
321 * @return string |
374 * @return string |
480 if (!isset($meta[self::S3_CONTENT_TYPE_HEADER])) { |
533 if (!isset($meta[self::S3_CONTENT_TYPE_HEADER])) { |
481 $meta[self::S3_CONTENT_TYPE_HEADER] = self::getMimeType($path); |
534 $meta[self::S3_CONTENT_TYPE_HEADER] = self::getMimeType($path); |
482 } |
535 } |
483 |
536 |
484 if(!isset($meta['Content-MD5'])) { |
537 if(!isset($meta['Content-MD5'])) { |
485 $headers['Content-MD5'] = base64_encode(md5_file($path, true)); |
538 $meta['Content-MD5'] = base64_encode(md5_file($path, true)); |
486 } |
539 } |
487 |
540 |
488 return $this->putObject($object, $data, $meta); |
541 return $this->putObject($object, $data, $meta); |
489 } |
542 } |
490 |
543 |
555 } |
608 } |
556 |
609 |
557 /** |
610 /** |
558 * Make a request to Amazon S3 |
611 * Make a request to Amazon S3 |
559 * |
612 * |
560 * @param string $method Request method |
613 * @param string $method Request method |
561 * @param string $path Path to requested object |
614 * @param string $path Path to requested object |
562 * @param array $params Request parameters |
615 * @param array $params Request parameters |
563 * @param array $headers HTTP headers |
616 * @param array $headers HTTP headers |
564 * @param string|resource $data Request data |
617 * @param string|resource $data Request data |
565 * @return Zend_Http_Response |
618 * @return Zend_Http_Response |
566 */ |
619 */ |
567 public function _makeRequest($method, $path='', $params=null, $headers=array(), $data=null) |
620 public function _makeRequest($method, $path='', $params=null, $headers=array(), $data=null) |
568 { |
621 { |
569 $retry_count = 0; |
622 $retry_count = 0; |
588 if ($parts[0]) { |
641 if ($parts[0]) { |
589 // prepend bucket name to the hostname |
642 // prepend bucket name to the hostname |
590 $endpoint->setHost($parts[0].'.'.$endpoint->getHost()); |
643 $endpoint->setHost($parts[0].'.'.$endpoint->getHost()); |
591 } |
644 } |
592 if (!empty($parts[1])) { |
645 if (!empty($parts[1])) { |
593 $endpoint->setPath('/'.$parts[1]); |
646 // ZF-10218, ZF-10122 |
|
647 $pathparts = explode('?',$parts[1]); |
|
648 $endpath = $pathparts[0]; |
|
649 $endpoint->setPath('/'.$endpath); |
|
650 |
594 } |
651 } |
595 else { |
652 else { |
596 $endpoint->setPath('/'); |
653 $endpoint->setPath('/'); |
597 if ($parts[0]) { |
654 if ($parts[0]) { |
598 $path = $parts[0].'/'; |
655 $path = $parts[0].'/'; |
599 } |
656 } |
600 } |
657 } |
601 |
|
602 self::addSignature($method, $path, $headers); |
658 self::addSignature($method, $path, $headers); |
603 |
659 |
604 $client = self::getHttpClient(); |
660 $client = self::getHttpClient(); |
605 |
661 |
606 $client->resetParameters(); |
662 $client->resetParameters(true); |
607 $client->setUri($endpoint); |
663 $client->setUri($endpoint); |
608 $client->setAuth(false); |
664 $client->setAuth(false); |
609 // Work around buglet in HTTP client - it doesn't clean headers |
665 // Work around buglet in HTTP client - it doesn't clean headers |
610 // Remove when ZHC is fixed |
666 // Remove when ZHC is fixed |
|
667 /* |
611 $client->setHeaders(array('Content-MD5' => null, |
668 $client->setHeaders(array('Content-MD5' => null, |
612 'Content-Encoding' => null, |
669 'Content-Encoding' => null, |
613 'Expect' => null, |
670 'Expect' => null, |
614 'Range' => null, |
671 'Range' => null, |
615 'x-amz-acl' => null, |
672 'x-amz-acl' => null, |
616 'x-amz-copy-source' => null, |
673 'x-amz-copy-source' => null, |
617 'x-amz-metadata-directive' => null)); |
674 'x-amz-metadata-directive' => null)); |
618 |
675 */ |
619 $client->setHeaders($headers); |
676 $client->setHeaders($headers); |
620 |
677 |
621 if (is_array($params)) { |
678 if (is_array($params)) { |
622 foreach ($params as $name=>$value) { |
679 foreach ($params as $name=>$value) { |
623 $client->setParameterGet($name, $value); |
680 $client->setParameterGet($name, $value); |
627 if (($method == 'PUT') && ($data !== null)) { |
684 if (($method == 'PUT') && ($data !== null)) { |
628 if (!isset($headers['Content-type'])) { |
685 if (!isset($headers['Content-type'])) { |
629 $headers['Content-type'] = self::getMimeType($path); |
686 $headers['Content-type'] = self::getMimeType($path); |
630 } |
687 } |
631 $client->setRawData($data, $headers['Content-type']); |
688 $client->setRawData($data, $headers['Content-type']); |
632 } |
689 } |
633 do { |
690 do { |
634 $retry = false; |
691 $retry = false; |
635 |
692 |
636 $response = $client->request($method); |
693 $response = $client->request($method); |
637 $response_code = $response->getStatus(); |
694 $response_code = $response->getStatus(); |
718 $sig_str .= '?acl'; |
775 $sig_str .= '?acl'; |
719 } |
776 } |
720 else if (strpos($path, '?torrent') !== false) { |
777 else if (strpos($path, '?torrent') !== false) { |
721 $sig_str .= '?torrent'; |
778 $sig_str .= '?torrent'; |
722 } |
779 } |
|
780 else if (strpos($path, '?versions') !== false) { |
|
781 $sig_str .= '?versions'; |
|
782 } |
723 |
783 |
724 $signature = base64_encode(Zend_Crypt_Hmac::compute($this->_getSecretKey(), 'sha1', utf8_encode($sig_str), Zend_Crypt_Hmac::BINARY)); |
784 $signature = base64_encode(Zend_Crypt_Hmac::compute($this->_getSecretKey(), 'sha1', utf8_encode($sig_str), Zend_Crypt_Hmac::BINARY)); |
725 $headers['Authorization'] = 'AWS '.$this->_getAccessKey().':'.$signature; |
785 $headers['Authorization'] = 'AWS '.$this->_getAccessKey().':'.$signature; |
726 |
786 |
727 return $sig_str; |
787 return $sig_str; |