File indexing completed on 2024-12-22 05:37:02
0001 <?php 0002 /** 0003 * Zend Framework 0004 * 0005 * LICENSE 0006 * 0007 * This source file is subject to the new BSD license that is bundled 0008 * with this package in the file LICENSE.txt. 0009 * It is also available through the world-wide-web at this URL: 0010 * http://framework.zend.com/license/new-bsd 0011 * If you did not receive a copy of the license and are unable to 0012 * obtain it through the world-wide-web, please send an email 0013 * to license@zend.com so we can send you a copy immediately. 0014 * 0015 * @category Zend 0016 * @package Zend_Service 0017 * @subpackage Amazon_S3 0018 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0019 * @license http://framework.zend.com/license/new-bsd New BSD License 0020 * @version $Id$ 0021 */ 0022 0023 /** 0024 * @see Zend_Service_Amazon_Abstract 0025 */ 0026 // require_once 'Zend/Service/Amazon/Abstract.php'; 0027 0028 /** 0029 * @see Zend_Crypt_Hmac 0030 */ 0031 // require_once 'Zend/Crypt/Hmac.php'; 0032 0033 /** 0034 * Amazon S3 PHP connection class 0035 * 0036 * @category Zend 0037 * @package Zend_Service 0038 * @subpackage Amazon_S3 0039 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0040 * @license http://framework.zend.com/license/new-bsd New BSD License 0041 * @see http://docs.amazonwebservices.com/AmazonS3/2006-03-01/ 0042 */ 0043 class Zend_Service_Amazon_S3 extends Zend_Service_Amazon_Abstract 0044 { 0045 /** 0046 * Store for stream wrapper clients 0047 * 0048 * @var array 0049 */ 0050 protected static $_wrapperClients = array(); 0051 0052 /** 0053 * Endpoint for the service 0054 * 0055 * @var Zend_Uri_Http 0056 */ 0057 protected $_endpoint; 0058 0059 const S3_ENDPOINT = 's3.amazonaws.com'; 0060 0061 const S3_ACL_PRIVATE = 'private'; 0062 const S3_ACL_PUBLIC_READ = 'public-read'; 0063 const S3_ACL_PUBLIC_WRITE = 'public-read-write'; 0064 const S3_ACL_AUTH_READ = 'authenticated-read'; 0065 0066 const S3_REQUESTPAY_HEADER = 'x-amz-request-payer'; 0067 const S3_ACL_HEADER = 'x-amz-acl'; 0068 const S3_CONTENT_TYPE_HEADER = 'Content-Type'; 0069 0070 /** 0071 * Set S3 endpoint to use 0072 * 0073 * @param string|Zend_Uri_Http $endpoint 0074 * @return Zend_Service_Amazon_S3 0075 */ 0076 public function setEndpoint($endpoint) 0077 { 0078 if (!($endpoint instanceof Zend_Uri_Http)) { 0079 $endpoint = Zend_Uri::factory($endpoint); 0080 } 0081 if (!$endpoint->valid()) { 0082 /** 0083 * @see Zend_Service_Amazon_S3_Exception 0084 */ 0085 // require_once 'Zend/Service/Amazon/S3/Exception.php'; 0086 throw new Zend_Service_Amazon_S3_Exception('Invalid endpoint supplied'); 0087 } 0088 $this->_endpoint = $endpoint; 0089 return $this; 0090 } 0091 0092 /** 0093 * Get current S3 endpoint 0094 * 0095 * @return Zend_Uri_Http 0096 */ 0097 public function getEndpoint() 0098 { 0099 return $this->_endpoint; 0100 } 0101 0102 /** 0103 * Constructor 0104 * 0105 * @param string $accessKey 0106 * @param string $secretKey 0107 * @param string $region 0108 */ 0109 public function __construct($accessKey=null, $secretKey=null, $region=null) 0110 { 0111 parent::__construct($accessKey, $secretKey, $region); 0112 0113 $this->setEndpoint('http://'.self::S3_ENDPOINT); 0114 } 0115 0116 /** 0117 * Verify if the bucket name is valid 0118 * 0119 * @param string $bucket 0120 * @return boolean 0121 */ 0122 public function _validBucketName($bucket) 0123 { 0124 $len = strlen($bucket); 0125 if ($len < 3 || $len > 255) { 0126 /** 0127 * @see Zend_Service_Amazon_S3_Exception 0128 */ 0129 // require_once 'Zend/Service/Amazon/S3/Exception.php'; 0130 throw new Zend_Service_Amazon_S3_Exception("Bucket name \"$bucket\" must be between 3 and 255 characters long"); 0131 } 0132 0133 if (preg_match('/[^a-z0-9\._-]/', $bucket)) { 0134 /** 0135 * @see Zend_Service_Amazon_S3_Exception 0136 */ 0137 // require_once 'Zend/Service/Amazon/S3/Exception.php'; 0138 throw new Zend_Service_Amazon_S3_Exception("Bucket name \"$bucket\" contains invalid characters"); 0139 } 0140 0141 if (preg_match('/(\d){1,3}\.(\d){1,3}\.(\d){1,3}\.(\d){1,3}/', $bucket)) { 0142 /** 0143 * @see Zend_Service_Amazon_S3_Exception 0144 */ 0145 // require_once 'Zend/Service/Amazon/S3/Exception.php'; 0146 throw new Zend_Service_Amazon_S3_Exception("Bucket name \"$bucket\" cannot be an IP address"); 0147 } 0148 return true; 0149 } 0150 0151 /** 0152 * Add a new bucket 0153 * 0154 * @param string $bucket 0155 * @return boolean 0156 */ 0157 public function createBucket($bucket, $location = null) 0158 { 0159 $this->_validBucketName($bucket); 0160 $headers=array(); 0161 if($location) { 0162 $data = '<CreateBucketConfiguration><LocationConstraint>'.$location.'</LocationConstraint></CreateBucketConfiguration>'; 0163 $headers[self::S3_CONTENT_TYPE_HEADER]= 'text/plain'; 0164 $headers['Content-size']= strlen($data); 0165 } else { 0166 $data = null; 0167 } 0168 $response = $this->_makeRequest('PUT', $bucket, null, $headers, $data); 0169 0170 return ($response->getStatus() == 200); 0171 } 0172 0173 /** 0174 * Checks if a given bucket name is available 0175 * 0176 * @param string $bucket 0177 * @return boolean 0178 */ 0179 public function isBucketAvailable($bucket) 0180 { 0181 $response = $this->_makeRequest('HEAD', $bucket, array('max-keys'=>0)); 0182 0183 return ($response->getStatus() != 404); 0184 } 0185 0186 /** 0187 * Checks if a given object exists 0188 * 0189 * @param string $object 0190 * @return boolean 0191 */ 0192 public function isObjectAvailable($object) 0193 { 0194 $object = $this->_fixupObjectName($object); 0195 $response = $this->_makeRequest('HEAD', $object); 0196 0197 return ($response->getStatus() == 200); 0198 } 0199 0200 /** 0201 * Remove a given bucket. All objects in the bucket must be removed prior 0202 * to removing the bucket. 0203 * 0204 * @param string $bucket 0205 * @return boolean 0206 */ 0207 public function removeBucket($bucket) 0208 { 0209 $response = $this->_makeRequest('DELETE', $bucket); 0210 0211 // Look for a 204 No Content response 0212 return ($response->getStatus() == 204); 0213 } 0214 0215 /** 0216 * Get metadata information for a given object 0217 * 0218 * @param string $object 0219 * @return array|false 0220 */ 0221 public function getInfo($object) 0222 { 0223 $info = array(); 0224 0225 $object = $this->_fixupObjectName($object); 0226 $response = $this->_makeRequest('HEAD', $object); 0227 0228 if ($response->getStatus() == 200) { 0229 $info['type'] = $response->getHeader('Content-type'); 0230 $info['size'] = $response->getHeader('Content-length'); 0231 $info['mtime'] = strtotime($response->getHeader('Last-modified')); 0232 $info['etag'] = $response->getHeader('ETag'); 0233 } 0234 else { 0235 return false; 0236 } 0237 0238 return $info; 0239 } 0240 0241 /** 0242 * List the S3 buckets 0243 * 0244 * @return array|false 0245 */ 0246 public function getBuckets() 0247 { 0248 $response = $this->_makeRequest('GET'); 0249 0250 if ($response->getStatus() != 200) { 0251 return false; 0252 } 0253 0254 $xml = new SimpleXMLElement($response->getBody()); 0255 0256 $buckets = array(); 0257 foreach ($xml->Buckets->Bucket as $bucket) { 0258 $buckets[] = (string)$bucket->Name; 0259 } 0260 0261 return $buckets; 0262 } 0263 0264 /** 0265 * Remove all objects in the bucket. 0266 * 0267 * @param string $bucket 0268 * @return boolean 0269 */ 0270 public function cleanBucket($bucket) 0271 { 0272 $objects = $this->getObjectsByBucket($bucket); 0273 if (!$objects) { 0274 return false; 0275 } 0276 0277 while (!empty($objects)) { 0278 foreach ($objects as $object) { 0279 $this->removeObject("$bucket/$object"); 0280 } 0281 $params= array ( 0282 'marker' => $objects[count($objects)-1] 0283 ); 0284 $objects = $this->getObjectsByBucket($bucket,$params); 0285 } 0286 0287 return true; 0288 } 0289 0290 /** 0291 * List the objects in a bucket. 0292 * 0293 * Provides the list of object keys that are contained in the bucket. Valid params include the following. 0294 * 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. 0295 * 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. 0296 * 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. 0297 * 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. 0298 * 0299 * @param string $bucket 0300 * @param array $params S3 GET Bucket Paramater 0301 * @return array|false 0302 */ 0303 public function getObjectsByBucket($bucket, $params = array()) 0304 { 0305 $response = $this->_makeRequest('GET', $bucket, $params); 0306 0307 if ($response->getStatus() != 200) { 0308 return false; 0309 } 0310 0311 $xml = new SimpleXMLElement($response->getBody()); 0312 0313 $objects = array(); 0314 if (isset($xml->Contents)) { 0315 foreach ($xml->Contents as $contents) { 0316 foreach ($contents->Key as $object) { 0317 $objects[] = (string)$object; 0318 } 0319 } 0320 } 0321 0322 return $objects; 0323 } 0324 /** 0325 * List the objects and common prefixes in a bucket. 0326 * 0327 * Provides the list of object keys and common prefixes that are contained in the bucket. Valid params include the following. 0328 * 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. 0329 * 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. 0330 * 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. 0331 * 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. 0332 * 0333 * @see ZF-11401 0334 * @param string $bucket 0335 * @param array $params S3 GET Bucket Paramater 0336 * @return array|false 0337 */ 0338 public function getObjectsAndPrefixesByBucket($bucket, $params = array()) 0339 { 0340 $response = $this->_makeRequest('GET', $bucket, $params); 0341 0342 if ($response->getStatus() != 200) { 0343 return false; 0344 } 0345 0346 $xml = new SimpleXMLElement($response->getBody()); 0347 0348 $objects = array(); 0349 if (isset($xml->Contents)) { 0350 foreach ($xml->Contents as $contents) { 0351 foreach ($contents->Key as $object) { 0352 $objects[] = (string)$object; 0353 } 0354 } 0355 } 0356 $prefixes = array(); 0357 if (isset($xml->CommonPrefixes)) { 0358 foreach ($xml->CommonPrefixes as $prefix) { 0359 foreach ($prefix->Prefix as $object) { 0360 $prefixes[] = (string)$object; 0361 } 0362 } 0363 } 0364 0365 return array( 0366 'objects' => $objects, 0367 'prefixes' => $prefixes 0368 ); 0369 } 0370 /** 0371 * Make sure the object name is valid 0372 * 0373 * @param string $object 0374 * @return string 0375 */ 0376 protected function _fixupObjectName($object) 0377 { 0378 $nameparts = explode('/', $object); 0379 0380 $this->_validBucketName($nameparts[0]); 0381 0382 $firstpart = array_shift($nameparts); 0383 if (count($nameparts) == 0) { 0384 return $firstpart; 0385 } 0386 0387 return $firstpart.'/'.join('/', array_map('rawurlencode', $nameparts)); 0388 } 0389 0390 /** 0391 * Get an object 0392 * 0393 * @param string $object 0394 * @param bool $paidobject This is "requestor pays" object 0395 * @return string|false 0396 */ 0397 public function getObject($object, $paidobject=false) 0398 { 0399 $object = $this->_fixupObjectName($object); 0400 if ($paidobject) { 0401 $response = $this->_makeRequest('GET', $object, null, array(self::S3_REQUESTPAY_HEADER => 'requester')); 0402 } 0403 else { 0404 $response = $this->_makeRequest('GET', $object); 0405 } 0406 0407 if ($response->getStatus() != 200) { 0408 return false; 0409 } 0410 0411 return $response->getBody(); 0412 } 0413 0414 /** 0415 * Get an object using streaming 0416 * 0417 * Can use either provided filename for storage or create a temp file if none provided. 0418 * 0419 * @param string $object Object path 0420 * @param string $streamfile File to write the stream to 0421 * @param bool $paidobject This is "requestor pays" object 0422 * @return Zend_Http_Response_Stream|false 0423 */ 0424 public function getObjectStream($object, $streamfile = null, $paidobject=false) 0425 { 0426 $object = $this->_fixupObjectName($object); 0427 self::getHttpClient()->setStream($streamfile?$streamfile:true); 0428 if ($paidobject) { 0429 $response = $this->_makeRequest('GET', $object, null, array(self::S3_REQUESTPAY_HEADER => 'requester')); 0430 } 0431 else { 0432 $response = $this->_makeRequest('GET', $object); 0433 } 0434 self::getHttpClient()->setStream(null); 0435 0436 if ($response->getStatus() != 200 || !($response instanceof Zend_Http_Response_Stream)) { 0437 return false; 0438 } 0439 0440 return $response; 0441 } 0442 0443 /** 0444 * Upload an object by a PHP string 0445 * 0446 * @param string $object Object name 0447 * @param string|resource $data Object data (can be string or stream) 0448 * @param array $meta Metadata 0449 * @return boolean 0450 */ 0451 public function putObject($object, $data, $meta=null) 0452 { 0453 $object = $this->_fixupObjectName($object); 0454 $headers = (is_array($meta)) ? $meta : array(); 0455 0456 if(!is_resource($data)) { 0457 $headers['Content-MD5'] = base64_encode(md5($data, true)); 0458 } 0459 $headers['Expect'] = '100-continue'; 0460 0461 if (!isset($headers[self::S3_CONTENT_TYPE_HEADER])) { 0462 $headers[self::S3_CONTENT_TYPE_HEADER] = self::getMimeType($object); 0463 } 0464 0465 $response = $this->_makeRequest('PUT', $object, null, $headers, $data); 0466 0467 // Check the MD5 Etag returned by S3 against and MD5 of the buffer 0468 if ($response->getStatus() == 200) { 0469 // It is escaped by double quotes for some reason 0470 $etag = str_replace('"', '', $response->getHeader('Etag')); 0471 0472 if (is_resource($data) || $etag == md5($data)) { 0473 return true; 0474 } 0475 } 0476 0477 return false; 0478 } 0479 0480 /** 0481 * Put file to S3 as object 0482 * 0483 * @param string $path File name 0484 * @param string $object Object name 0485 * @param array $meta Metadata 0486 * @return boolean 0487 */ 0488 public function putFile($path, $object, $meta=null) 0489 { 0490 $data = @file_get_contents($path); 0491 if ($data === false) { 0492 /** 0493 * @see Zend_Service_Amazon_S3_Exception 0494 */ 0495 // require_once 'Zend/Service/Amazon/S3/Exception.php'; 0496 throw new Zend_Service_Amazon_S3_Exception("Cannot read file $path"); 0497 } 0498 0499 if (!is_array($meta)) { 0500 $meta = array(); 0501 } 0502 0503 if (!isset($meta[self::S3_CONTENT_TYPE_HEADER])) { 0504 $meta[self::S3_CONTENT_TYPE_HEADER] = self::getMimeType($path); 0505 } 0506 0507 return $this->putObject($object, $data, $meta); 0508 } 0509 0510 /** 0511 * Put file to S3 as object, using streaming 0512 * 0513 * @param string $path File name 0514 * @param string $object Object name 0515 * @param array $meta Metadata 0516 * @return boolean 0517 */ 0518 public function putFileStream($path, $object, $meta=null) 0519 { 0520 $data = @fopen($path, "rb"); 0521 if ($data === false) { 0522 /** 0523 * @see Zend_Service_Amazon_S3_Exception 0524 */ 0525 // require_once 'Zend/Service/Amazon/S3/Exception.php'; 0526 throw new Zend_Service_Amazon_S3_Exception("Cannot open file $path"); 0527 } 0528 0529 if (!is_array($meta)) { 0530 $meta = array(); 0531 } 0532 0533 if (!isset($meta[self::S3_CONTENT_TYPE_HEADER])) { 0534 $meta[self::S3_CONTENT_TYPE_HEADER] = self::getMimeType($path); 0535 } 0536 0537 if(!isset($meta['Content-MD5'])) { 0538 $meta['Content-MD5'] = base64_encode(md5_file($path, true)); 0539 } 0540 0541 return $this->putObject($object, $data, $meta); 0542 } 0543 0544 /** 0545 * Remove a given object 0546 * 0547 * @param string $object 0548 * @return boolean 0549 */ 0550 public function removeObject($object) 0551 { 0552 $object = $this->_fixupObjectName($object); 0553 $response = $this->_makeRequest('DELETE', $object); 0554 0555 // Look for a 204 No Content response 0556 return ($response->getStatus() == 204); 0557 } 0558 0559 /** 0560 * Copy an object 0561 * 0562 * @param string $sourceObject Source object name 0563 * @param string $destObject Destination object name 0564 * @param array $meta (OPTIONAL) Metadata to apply to desination object. 0565 * Set to null to copy metadata from source object. 0566 * @return boolean 0567 */ 0568 public function copyObject($sourceObject, $destObject, $meta = null) 0569 { 0570 $sourceObject = $this->_fixupObjectName($sourceObject); 0571 $destObject = $this->_fixupObjectName($destObject); 0572 0573 $headers = (is_array($meta)) ? $meta : array(); 0574 $headers['x-amz-copy-source'] = $sourceObject; 0575 $headers['x-amz-metadata-directive'] = $meta === null ? 'COPY' : 'REPLACE'; 0576 0577 $response = $this->_makeRequest('PUT', $destObject, null, $headers); 0578 0579 if ($response->getStatus() == 200 && !stristr($response->getBody(), '<Error>')) { 0580 return true; 0581 } 0582 0583 return false; 0584 } 0585 0586 /** 0587 * Move an object 0588 * 0589 * Performs a copy to dest + verify + remove source 0590 * 0591 * @param string $sourceObject Source object name 0592 * @param string $destObject Destination object name 0593 * @param array $meta (OPTIONAL) Metadata to apply to destination object. 0594 * Set to null to retain existing metadata. 0595 */ 0596 public function moveObject($sourceObject, $destObject, $meta = null) 0597 { 0598 $sourceInfo = $this->getInfo($sourceObject); 0599 0600 $this->copyObject($sourceObject, $destObject, $meta); 0601 $destInfo = $this->getInfo($destObject); 0602 0603 if ($sourceInfo['etag'] === $destInfo['etag']) { 0604 return $this->removeObject($sourceObject); 0605 } else { 0606 return false; 0607 } 0608 } 0609 0610 /** 0611 * Make a request to Amazon S3 0612 * 0613 * @param string $method Request method 0614 * @param string $path Path to requested object 0615 * @param array $params Request parameters 0616 * @param array $headers HTTP headers 0617 * @param string|resource $data Request data 0618 * @return Zend_Http_Response 0619 */ 0620 public function _makeRequest($method, $path='', $params=null, $headers=array(), $data=null) 0621 { 0622 $retry_count = 0; 0623 0624 if (!is_array($headers)) { 0625 $headers = array($headers); 0626 } 0627 0628 $headers['Date'] = gmdate(DATE_RFC1123, time()); 0629 0630 if(is_resource($data) && $method != 'PUT') { 0631 /** 0632 * @see Zend_Service_Amazon_S3_Exception 0633 */ 0634 // require_once 'Zend/Service/Amazon/S3/Exception.php'; 0635 throw new Zend_Service_Amazon_S3_Exception("Only PUT request supports stream data"); 0636 } 0637 0638 // build the end point out 0639 $parts = explode('/', $path, 2); 0640 $endpoint = clone($this->_endpoint); 0641 if ($parts[0]) { 0642 // prepend bucket name to the hostname 0643 $endpoint->setHost($parts[0].'.'.$endpoint->getHost()); 0644 } 0645 if (!empty($parts[1])) { 0646 // ZF-10218, ZF-10122 0647 $pathparts = explode('?',$parts[1]); 0648 $endpath = $pathparts[0]; 0649 $endpoint->setPath('/'.$endpath); 0650 0651 } 0652 else { 0653 $endpoint->setPath('/'); 0654 if ($parts[0]) { 0655 $path = $parts[0].'/'; 0656 } 0657 } 0658 self::addSignature($method, $path, $headers); 0659 0660 $client = self::getHttpClient(); 0661 0662 $client->resetParameters(true); 0663 $client->setUri($endpoint); 0664 $client->setAuth(false); 0665 // Work around buglet in HTTP client - it doesn't clean headers 0666 // Remove when ZHC is fixed 0667 /* 0668 $client->setHeaders(array('Content-MD5' => null, 0669 'Content-Encoding' => null, 0670 'Expect' => null, 0671 'Range' => null, 0672 'x-amz-acl' => null, 0673 'x-amz-copy-source' => null, 0674 'x-amz-metadata-directive' => null)); 0675 */ 0676 $client->setHeaders($headers); 0677 0678 if (is_array($params)) { 0679 foreach ($params as $name=>$value) { 0680 $client->setParameterGet($name, $value); 0681 } 0682 } 0683 0684 if (($method == 'PUT') && ($data !== null)) { 0685 if (!isset($headers['Content-type'])) { 0686 $headers['Content-type'] = self::getMimeType($path); 0687 } 0688 $client->setRawData($data, $headers['Content-type']); 0689 } 0690 do { 0691 $retry = false; 0692 0693 $response = $client->request($method); 0694 $response_code = $response->getStatus(); 0695 0696 // Some 5xx errors are expected, so retry automatically 0697 if ($response_code >= 500 && $response_code < 600 && $retry_count <= 5) { 0698 $retry = true; 0699 $retry_count++; 0700 sleep($retry_count / 4 * $retry_count); 0701 } 0702 else if ($response_code == 307) { 0703 // Need to redirect, new S3 endpoint given 0704 // This should never happen as Zend_Http_Client will redirect automatically 0705 } 0706 else if ($response_code == 100) { 0707 // echo 'OK to Continue'; 0708 } 0709 } while ($retry); 0710 0711 return $response; 0712 } 0713 0714 /** 0715 * Add the S3 Authorization signature to the request headers 0716 * 0717 * @param string $method 0718 * @param string $path 0719 * @param array &$headers 0720 * @return string 0721 */ 0722 protected function addSignature($method, $path, &$headers) 0723 { 0724 if (!is_array($headers)) { 0725 $headers = array($headers); 0726 } 0727 0728 $type = $md5 = $date = ''; 0729 0730 // Search for the Content-type, Content-MD5 and Date headers 0731 foreach ($headers as $key=>$val) { 0732 if (strcasecmp($key, 'content-type') == 0) { 0733 $type = $val; 0734 } 0735 else if (strcasecmp($key, 'content-md5') == 0) { 0736 $md5 = $val; 0737 } 0738 else if (strcasecmp($key, 'date') == 0) { 0739 $date = $val; 0740 } 0741 } 0742 0743 // If we have an x-amz-date header, use that instead of the normal Date 0744 if (isset($headers['x-amz-date']) && isset($date)) { 0745 $date = ''; 0746 } 0747 0748 $sig_str = "$method\n$md5\n$type\n$date\n"; 0749 // For x-amz- headers, combine like keys, lowercase them, sort them 0750 // alphabetically and remove excess spaces around values 0751 $amz_headers = array(); 0752 foreach ($headers as $key=>$val) { 0753 $key = strtolower($key); 0754 if (substr($key, 0, 6) == 'x-amz-') { 0755 if (is_array($val)) { 0756 $amz_headers[$key] = $val; 0757 } 0758 else { 0759 $amz_headers[$key][] = preg_replace('/\s+/', ' ', $val); 0760 } 0761 } 0762 } 0763 if (!empty($amz_headers)) { 0764 ksort($amz_headers); 0765 foreach ($amz_headers as $key=>$val) { 0766 $sig_str .= $key.':'.implode(',', $val)."\n"; 0767 } 0768 } 0769 0770 $sig_str .= '/'.parse_url($path, PHP_URL_PATH); 0771 if (strpos($path, '?location') !== false) { 0772 $sig_str .= '?location'; 0773 } 0774 else if (strpos($path, '?acl') !== false) { 0775 $sig_str .= '?acl'; 0776 } 0777 else if (strpos($path, '?torrent') !== false) { 0778 $sig_str .= '?torrent'; 0779 } 0780 else if (strpos($path, '?versions') !== false) { 0781 $sig_str .= '?versions'; 0782 } 0783 0784 $signature = base64_encode(Zend_Crypt_Hmac::compute($this->_getSecretKey(), 'sha1', utf8_encode($sig_str), Zend_Crypt_Hmac::BINARY)); 0785 $headers['Authorization'] = 'AWS '.$this->_getAccessKey().':'.$signature; 0786 0787 return $sig_str; 0788 } 0789 0790 /** 0791 * Attempt to get the content-type of a file based on the extension 0792 * 0793 * @param string $path 0794 * @return string 0795 */ 0796 public static function getMimeType($path) 0797 { 0798 $ext = substr(strrchr($path, '.'), 1); 0799 0800 if(!$ext) { 0801 // shortcut 0802 return 'binary/octet-stream'; 0803 } 0804 0805 switch (strtolower($ext)) { 0806 case 'xls': 0807 $content_type = 'application/excel'; 0808 break; 0809 case 'hqx': 0810 $content_type = 'application/macbinhex40'; 0811 break; 0812 case 'doc': 0813 case 'dot': 0814 case 'wrd': 0815 $content_type = 'application/msword'; 0816 break; 0817 case 'pdf': 0818 $content_type = 'application/pdf'; 0819 break; 0820 case 'pgp': 0821 $content_type = 'application/pgp'; 0822 break; 0823 case 'ps': 0824 case 'eps': 0825 case 'ai': 0826 $content_type = 'application/postscript'; 0827 break; 0828 case 'ppt': 0829 $content_type = 'application/powerpoint'; 0830 break; 0831 case 'rtf': 0832 $content_type = 'application/rtf'; 0833 break; 0834 case 'tgz': 0835 case 'gtar': 0836 $content_type = 'application/x-gtar'; 0837 break; 0838 case 'gz': 0839 $content_type = 'application/x-gzip'; 0840 break; 0841 case 'php': 0842 case 'php3': 0843 case 'php4': 0844 $content_type = 'application/x-httpd-php'; 0845 break; 0846 case 'js': 0847 $content_type = 'application/x-javascript'; 0848 break; 0849 case 'ppd': 0850 case 'psd': 0851 $content_type = 'application/x-photoshop'; 0852 break; 0853 case 'swf': 0854 case 'swc': 0855 case 'rf': 0856 $content_type = 'application/x-shockwave-flash'; 0857 break; 0858 case 'tar': 0859 $content_type = 'application/x-tar'; 0860 break; 0861 case 'zip': 0862 $content_type = 'application/zip'; 0863 break; 0864 case 'mid': 0865 case 'midi': 0866 case 'kar': 0867 $content_type = 'audio/midi'; 0868 break; 0869 case 'mp2': 0870 case 'mp3': 0871 case 'mpga': 0872 $content_type = 'audio/mpeg'; 0873 break; 0874 case 'ra': 0875 $content_type = 'audio/x-realaudio'; 0876 break; 0877 case 'wav': 0878 $content_type = 'audio/wav'; 0879 break; 0880 case 'bmp': 0881 $content_type = 'image/bitmap'; 0882 break; 0883 case 'gif': 0884 $content_type = 'image/gif'; 0885 break; 0886 case 'iff': 0887 $content_type = 'image/iff'; 0888 break; 0889 case 'jb2': 0890 $content_type = 'image/jb2'; 0891 break; 0892 case 'jpg': 0893 case 'jpe': 0894 case 'jpeg': 0895 $content_type = 'image/jpeg'; 0896 break; 0897 case 'jpx': 0898 $content_type = 'image/jpx'; 0899 break; 0900 case 'png': 0901 $content_type = 'image/png'; 0902 break; 0903 case 'tif': 0904 case 'tiff': 0905 $content_type = 'image/tiff'; 0906 break; 0907 case 'wbmp': 0908 $content_type = 'image/vnd.wap.wbmp'; 0909 break; 0910 case 'xbm': 0911 $content_type = 'image/xbm'; 0912 break; 0913 case 'css': 0914 $content_type = 'text/css'; 0915 break; 0916 case 'txt': 0917 $content_type = 'text/plain'; 0918 break; 0919 case 'htm': 0920 case 'html': 0921 $content_type = 'text/html'; 0922 break; 0923 case 'xml': 0924 $content_type = 'text/xml'; 0925 break; 0926 case 'xsl': 0927 $content_type = 'text/xsl'; 0928 break; 0929 case 'mpg': 0930 case 'mpe': 0931 case 'mpeg': 0932 $content_type = 'video/mpeg'; 0933 break; 0934 case 'qt': 0935 case 'mov': 0936 $content_type = 'video/quicktime'; 0937 break; 0938 case 'avi': 0939 $content_type = 'video/x-ms-video'; 0940 break; 0941 case 'eml': 0942 $content_type = 'message/rfc822'; 0943 break; 0944 default: 0945 $content_type = 'binary/octet-stream'; 0946 break; 0947 } 0948 0949 return $content_type; 0950 } 0951 0952 /** 0953 * Register this object as stream wrapper client 0954 * 0955 * @param string $name 0956 * @return Zend_Service_Amazon_S3 0957 */ 0958 public function registerAsClient($name) 0959 { 0960 self::$_wrapperClients[$name] = $this; 0961 return $this; 0962 } 0963 0964 /** 0965 * Unregister this object as stream wrapper client 0966 * 0967 * @param string $name 0968 * @return Zend_Service_Amazon_S3 0969 */ 0970 public function unregisterAsClient($name) 0971 { 0972 unset(self::$_wrapperClients[$name]); 0973 return $this; 0974 } 0975 0976 /** 0977 * Get wrapper client for stream type 0978 * 0979 * @param string $name 0980 * @return Zend_Service_Amazon_S3 0981 */ 0982 public static function getWrapperClient($name) 0983 { 0984 return self::$_wrapperClients[$name]; 0985 } 0986 0987 /** 0988 * Register this object as stream wrapper 0989 * 0990 * @param string $name 0991 * @return Zend_Service_Amazon_S3 0992 */ 0993 public function registerStreamWrapper($name='s3') 0994 { 0995 /** 0996 * @see Zend_Service_Amazon_S3_Stream 0997 */ 0998 // require_once 'Zend/Service/Amazon/S3/Stream.php'; 0999 1000 stream_register_wrapper($name, 'Zend_Service_Amazon_S3_Stream'); 1001 $this->registerAsClient($name); 1002 } 1003 1004 /** 1005 * Unregister this object as stream wrapper 1006 * 1007 * @param string $name 1008 * @return Zend_Service_Amazon_S3 1009 */ 1010 public function unregisterStreamWrapper($name='s3') 1011 { 1012 stream_wrapper_unregister($name); 1013 $this->unregisterAsClient($name); 1014 } 1015 }