Open-Source PHP Framework - Designed for rapid development of performance-oriented scalable applications

/packages/zendframework/s3.php

[return to app]
1 <?php
2 set_include_path
(get_include_path() . PATH_SEPARATOR get::$config->packagesPath() . 'zendframework');
3
4 require 
'uri.php';
5 require 
'http.php';
6
7
/**
8  * Zend Framework Amazon S3 class isolated from ZF dependencies to work in Vork
9  *
10  * LICENSE
11  *
12  * This source file is subject to the new BSD license that is bundled
13  * with this package in the file LICENSE.txt.
14  * It is also available through the world-wide-web at this URL:
15  * http://framework.zend.com/license/new-bsd
16  * If you did not receive a copy of the license and are unable to
17  * obtain it through the world-wide-web, please send an email
18  * to license@zend.com so we can send you a copy immediately.
19  *
20  * @category   Zend
21  * @package    Zend_Service
22  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
23  * @license    http://framework.zend.com/license/new-bsd     New BSD License
24  * @version    $Id: Abstract.php 20096 2010-01-06 02:05:09Z bkarwin $
25  */
26
abstract class Zend_Service_Abstract {
27     
/**
28      * HTTP Client used to query all web services
29      *
30      * @var Zend_Http_Client
31      */
32     
protected static $_httpClient null;
33
34     
/**
35      * Sets the HTTP client object to use for retrieving the feeds.  If none
36      * is set, the default Zend_Http_Client will be used.
37      *
38      * @param Zend_Http_Client $httpClient
39      */
40     
final public static function setHttpClient(Zend_Http_Client $httpClient) {
41         
self::$_httpClient $httpClient;
42     }
43
44     
/**
45      * Gets the HTTP client object.
46      *
47      * @return Zend_Http_Client
48      */
49     
final public static function getHttpClient() {
50         if (!
self::$_httpClient instanceof Zend_Http_Client) {
51             
self::$_httpClient = new Zend_Http_Client();
52         }
53
54         return 
self::$_httpClient;
55     }
56 }
57
58
/**
59  * Abstract Amazon class that handles the credentials for any of the Web Services that
60  * Amazon offers
61  *
62  * @category   Zend
63  * @package    Zend_Service
64  * @subpackage Amazon
65  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
66  * @license    http://framework.zend.com/license/new-bsd     New BSD License
67  * @version    $Id: Abstract.php 20096 2010-01-06 02:05:09Z bkarwin $
68  */
69
abstract class Zend_Service_Amazon_Abstract extends Zend_Service_Abstract {
70     
/**
71      * @var string Amazon Access Key
72      */
73     
protected static $_defaultAccessKey null;
74
75     
/**
76      * @var string Amazon Secret Key
77      */
78     
protected static $_defaultSecretKey null;
79
80     
/**
81      * @var string Amazon Secret Key
82      */
83     
protected $_secretKey;
84
85     
/**
86      * @var string Amazon Access Key
87      */
88     
protected $_accessKey;
89
90
91     
/**
92      * Set the keys to use when accessing SQS.
93      *
94      * @param  string $access_key       Set the default Access Key
95      * @param  string $secret_key       Set the default Secret Key
96      * @return void
97      */
98     
public static function setKeys($accessKey$secretKey) {
99         
self::$_defaultAccessKey $accessKey;
100         
self::$_defaultSecretKey $secretKey;
101     }
102
103     
/**
104      * Create Amazon client.
105      *
106      * @param  string $access_key       Override the default Access Key
107      * @param  string $secret_key       Override the default Secret Key
108      * @return void
109      */
110     
public function __construct($accessKey null$secretKey null) {
111         if(!
$accessKey) {
112             
$accessKey self::$_defaultAccessKey;
113         }
114         if(!
$secretKey) {
115             
$secretKey self::$_defaultSecretKey;
116         }
117
118         if(!
$accessKey || !$secretKey) {
119             if (!
class_exists('Zend_Exception')) require 'exception.php';
120             throw new 
Zend_Service_Amazon_Exception("AWS keys were not supplied");
121         }
122         
$this->_accessKey $accessKey;
123         
$this->_secretKey $secretKey;
124     }
125
126
127
128     
/**
129      * Method to fetch the Access Key
130      *
131      * @return string
132      */
133     
protected function _getAccessKey() {
134         return 
$this->_accessKey;
135     }
136
137     
/**
138      * Method to fetch the Secret AWS Key
139      *
140      * @return string
141      */
142     
protected function _getSecretKey() {
143         return 
$this->_secretKey;
144     }
145 }
146
147
/**
148  * @see Zend_Crypt_Hmac
149  */
150
require 'hmac.php';
151
152
/**
153  * Amazon S3 PHP connection class
154  *
155  * @category   Zend
156  * @package    Zend_Service
157  * @subpackage Amazon_S3
158  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
159  * @license    http://framework.zend.com/license/new-bsd     New BSD License
160  * @see        http://docs.amazonwebservices.com/AmazonS3/2006-03-01/
161  * @version    $Id: S3.php 20096 2010-01-06 02:05:09Z bkarwin $
162  */
163
class Zend_Service_Amazon_S3 extends Zend_Service_Amazon_Abstract {
164     
/**
165      * Store for stream wrapper clients
166      *
167      * @var array
168      */
169     
protected static $_wrapperClients = array();
170
171     
/**
172      * Endpoint for the service
173      *
174      * @var Zend_Uri_Http
175      */
176     
protected $_endpoint;
177
178     const 
S3_ENDPOINT 's3.amazonaws.com';
179
180     const 
S3_ACL_PRIVATE 'private';
181     const 
S3_ACL_PUBLIC_READ 'public-read';
182     const 
S3_ACL_PUBLIC_WRITE 'public-read-write';
183     const 
S3_ACL_AUTH_READ 'authenticated-read';
184
185     const 
S3_REQUESTPAY_HEADER 'x-amz-request-payer';
186     const 
S3_ACL_HEADER 'x-amz-acl';
187     const 
S3_CONTENT_TYPE_HEADER 'Content-Type';
188
189     
/**
190      * Set S3 endpoint to use
191      *
192      * @param string|Zend_Uri_Http $endpoint
193      * @return Zend_Service_Amazon_S3
194      */
195     
public function setEndpoint($endpoint) {
196         if (!(
$endpoint instanceof Zend_Uri_Http)) {
197             
$endpoint Zend_Uri::factory($endpoint);
198         }
199         if (!
$endpoint->valid()) {
200             
/**
201              * @see Zend_Service_Amazon_S3_Exception
202              */
203             
if (!class_exists('Zend_Exception')) require 'exception.php';
204             throw new 
Zend_Service_Amazon_S3_Exception('Invalid endpoint supplied');
205         }
206         
$this->_endpoint $endpoint;
207         return 
$this;
208     }
209
210     
/**
211      * Get current S3 endpoint
212      *
213      * @return Zend_Uri_Http
214      */
215     
public function getEndpoint() {
216         return 
$this->_endpoint;
217     }
218
219     
/**
220      * Constructor
221      *
222      * @param string $accessKey
223      * @param string $secretKey
224      * @param string $region
225      */
226     
public function __construct($accessKey null$secretKey null$region null) {
227         
parent::__construct($accessKey$secretKey$region);
228
229         
$this->setEndpoint('http://'.self::S3_ENDPOINT);
230     }
231
232     
/**
233      * Verify if the bucket name is valid
234      *
235      * @param string $bucket
236      * @return boolean
237      */
238     
public function _validBucketName($bucket) {
239         
$len strlen($bucket);
240         if (
$len || $len 255) {
241             
/**
242              * @see Zend_Service_Amazon_S3_Exception
243              */
244             
if (!class_exists('Zend_Exception')) require 'exception.php';
245             throw new 
Zend_Service_Amazon_S3_Exception("Bucket name \"$bucket\" must be between 3 and 255
 characters long"
);
246         }
247
248         if (
preg_match('/[^a-z0-9\._-]/'$bucket)) {
249             
/**
250              * @see Zend_Service_Amazon_S3_Exception
251              */
252             
if (!class_exists('Zend_Exception')) require 'exception.php';
253             throw new 
Zend_Service_Amazon_S3_Exception("Bucket name \"$bucket\" contains invalid characters");
254         }
255
256         if (
preg_match('/(\d){1,3}\.(\d){1,3}\.(\d){1,3}\.(\d){1,3}/'$bucket)) {
257             
/**
258              * @see Zend_Service_Amazon_S3_Exception
259              */
260             
if (!class_exists('Zend_Exception')) require 'exception.php';
261             throw new 
Zend_Service_Amazon_S3_Exception("Bucket name \"$bucket\" cannot be an IP address");
262         }
263         return 
true;
264     }
265
266     
/**
267      * Add a new bucket
268      *
269      * @param  string $bucket
270      * @return boolean
271      */
272     
public function createBucket($bucket$location null) {
273         
$this->_validBucketName($bucket);
274
275         if(
$location) {
276             
$data '<CreateBucketConfiguration><LocationConstraint>' $location
277                   
'</LocationConstraint></CreateBucketConfiguration>';
278         }
279         else {
280             
$data null;
281         }
282         
$response $this->_makeRequest('PUT'$bucketnull, array(), $data);
283
284         return (
$response->getStatus() == 200);
285     }
286
287     
/**
288      * Checks if a given bucket name is available
289      *
290      * @param  string $bucket
291      * @return boolean
292      */
293     
public function isBucketAvailable($bucket) {
294         
$response $this->_makeRequest('HEAD'$bucket, array('max-keys' => 0));
295
296         return (
$response->getStatus() != 404);
297     }
298
299     
/**
300      * Checks if a given object exists
301      *
302      * @param  string $object
303      * @return boolean
304      */
305     
public function isObjectAvailable($object) {
306         
$response $this->_makeRequest('HEAD'$object);
307
308         return (
$response->getStatus() == 200);
309     }
310
311     
/**
312      * Remove a given bucket. All objects in the bucket must be removed prior
313      * to removing the bucket.
314      *
315      * @param  string $bucket
316      * @return boolean
317      */
318     
public function removeBucket($bucket) {
319         
$response $this->_makeRequest('DELETE'$bucket);
320
321         
// Look for a 204 No Content response
322         
return ($response->getStatus() == 204);
323     }
324
325     
/**
326      * Get metadata information for a given object
327      *
328      * @param  string $object
329      * @return array|false
330      */
331     
public function getInfo($object) {
332         
$info = array();
333
334         
$object $this->_fixupObjectName($object);
335         
$response $this->_makeRequest('HEAD'$object);
336
337         if (
$response->getStatus() == 200) {
338             
$info['type'] = $response->getHeader('Content-type');
339             
$info['size'] = $response->getHeader('Content-length');
340             
$info['mtime'] = strtotime($response->getHeader('Last-modified'));
341             
$info['etag'] = $response->getHeader('ETag');
342         }
343         else {
344             return 
false;
345         }
346
347         return 
$info;
348     }
349
350     
/**
351      * List the S3 buckets
352      *
353      * @return array|false
354      */
355     
public function getBuckets() {
356         
$response $this->_makeRequest('GET');
357
358         if (
$response->getStatus() != 200) {
359             return 
false;
360         }
361
362         
$xml = new SimpleXMLElement($response->getBody());
363
364         
$buckets = array();
365         foreach (
$xml->Buckets->Bucket as $bucket) {
366             
$buckets[] = (string)$bucket->Name;
367         }
368
369         return 
$buckets;
370     }
371
372     
/**
373      * Remove all objects in the bucket.
374      *
375      * @param string $bucket
376      * @return boolean
377      */
378     
public function cleanBucket($bucket) {
379         
$objects $this->getObjectsByBucket($bucket);
380         if (!
$objects) {
381             return 
false;
382         }
383
384         foreach (
$objects as $object) {
385             
$this->removeObject("$bucket/$object");
386         }
387         return 
true;
388     }
389
390     
/**
391      * List the objects in a bucket.
392      *
393      * Provides the list of object keys that are contained in the bucket.  Valid params include the following.
394      * prefix - Limits the response to keys which begin with the indicated prefix. You can use prefixes to
 
separate a
395      
bucket into different sets of keys in a way similar to how a file system uses folders.
396      * 
marker Indicates where in the bucket to begin listingThe list will only include keys that occur
397      
lexicographically after markerThis is convenient for paginationTo get the next page of results use the
398      
last key of the current page as the marker.
399      * 
max-keys The maximum number of keys you'd like to see in the response body. The server might return
 fewer
400      * than this many keys, but will not return more.
401      * delimiter - Causes keys that contain the same string between the prefix and the first occurrence of the
402      * delimiter to be rolled up into a single result element in the CommonPrefixes collection. These rolled-up
 keys
403      * are not returned elsewhere in the response.
404      *
405      * @param  string $bucket
406      * @param array $params S3 GET Bucket Paramater
407      * @return array|false
408      */
409     public function getObjectsByBucket($bucket, $params = array()) {
410         $response = $this->_makeRequest('
GET', $bucket, $params);
411
412         if ($response->getStatus() != 200) {
413             return false;
414         }
415
416         $xml = new SimpleXMLElement($response->getBody());
417
418         $objects = array();
419         if (isset($xml->Contents)) {
420             foreach ($xml->Contents as $contents) {
421                 foreach ($contents->Key as $object) {
422                     $objects[] = (string)$object;
423                 }
424             }
425         }
426
427         return $objects;
428     }
429
430     /**
431      * Make sure the object name is valid
432      *
433      * @param  string $object
434      * @return string
435      */
436     protected function _fixupObjectName($object) {
437         $nameparts = explode('
/', $object);
438
439         $this->_validBucketName($nameparts[0]);
440
441         $firstpart = array_shift($nameparts);
442         if (count($nameparts) == 0) {
443             return $firstpart;
444         }
445
446         return $firstpart.'
/'.join('/', array_map('rawurlencode', $nameparts));
447     }
448
449     /**
450      * Get an object
451      *
452      * @param  string $object
453      * @param  bool   $paidobject This is "requestor pays" object
454      * @return string|false
455      */
456     public function getObject($object, $paidobject = false) {
457         $object = $this->_fixupObjectName($object);
458         if ($paidobject) {
459             $response = $this->_makeRequest('
GET', $object, null, array(self::S3_REQUESTPAY_HEADER =>
 '
requester'));
460         }
461         else {
462             $response = $this->_makeRequest('
GET', $object);
463         }
464
465         if ($response->getStatus() != 200) {
466             return false;
467         }
468
469         return $response->getBody();
470     }
471
472     /**
473      * Get an object using streaming
474      *
475      * Can use either provided filename for storage or create a temp file if none provided.
476      *
477      * @param  string $object Object path
478      * @param  string $streamfile File to write the stream to
479      * @param  bool   $paidobject This is "requestor pays" object
480      * @return Zend_Http_Response_Stream|false
481      */
482     public function getObjectStream($object, $streamfile = null, $paidobject = false) {
483         $object = $this->_fixupObjectName($object);
484         self::getHttpClient()->setStream($streamfile?$streamfile:true);
485         if ($paidobject) {
486             $response = $this->_makeRequest('
GET', $object, null, array(self::S3_REQUESTPAY_HEADER =>
 '
requester'));
487         }
488         else {
489             $response = $this->_makeRequest('
GET', $object);
490         }
491         self::getHttpClient()->setStream(null);
492
493         if ($response->getStatus() != 200 || !($response instanceof Zend_Http_Response_Stream)) {
494             return false;
495         }
496
497         return $response;
498     }
499
500     /**
501      * Upload an object by a PHP string
502      *
503      * @param  string $object Object name
504      * @param  string|resource $data   Object data (can be string or stream)
505      * @param  array  $meta   Metadata
506      * @return boolean
507      */
508     public function putObject($object, $data, $meta = null) {
509         $object = $this->_fixupObjectName($object);
510         $headers = (is_array($meta)) ? $meta : array();
511
512         if(!is_resource($data)) {
513             $headers['
Content-MD5'] = base64_encode(md5($data, true));
514         }
515         $headers['
Expect'] = '100-continue';
516
517         if (!isset($headers[self::S3_CONTENT_TYPE_HEADER])) {
518             $headers[self::S3_CONTENT_TYPE_HEADER] = self::getMimeType($object);
519         }
520
521         $response = $this->_makeRequest('
PUT', $object, null, $headers, $data);
522
523         // Check the MD5 Etag returned by S3 against and MD5 of the buffer
524         if ($response->getStatus() == 200) {
525             // It is escaped by double quotes for some reason
526             $etag = str_replace('"', '', 
$response->getHeader('Etag'));
527
528             if (is_resource(
$data) || $etag == md5($data)) {
529                 return true;
530             }
531         }
532
533         return false;
534     }
535
536     /**
537      * Put file to S3 as object
538      *
539      * @param string 
$path   File name
540      * @param string 
$object Object name
541      * @param array  
$meta   Metadata
542      * @return boolean
543      */
544     public function putFile(
$path$object$meta = null) {
545         
$data = @file_get_contents($path);
546         if (
$data === false) {
547             /**
548              * @see Zend_Service_Amazon_S3_Exception
549              */
550             if (!class_exists('Zend_Exception')) require 'exception.php';
551             throw new Zend_Service_Amazon_S3_Exception("
Cannot read file $path");
552         }
553
554         if (!is_array(
$meta)) {
555             
$meta = array();
556         }
557
558         if (!isset(
$meta[self::S3_CONTENT_TYPE_HEADER])) {
559            
$meta[self::S3_CONTENT_TYPE_HEADER] = self::getMimeType($path);
560         }
561
562         return 
$this->putObject($object$data$meta);
563     }
564
565     /**
566      * Put file to S3 as object, using streaming
567      *
568      * @param string 
$path   File name
569      * @param string 
$object Object name
570      * @param array  
$meta   Metadata
571      * @return boolean
572      */
573     public function putFileStream(
$path$object$meta = null) {
574         
$data = @fopen($path, "rb");
575         if (
$data === false) {
576             /**
577              * @see Zend_Service_Amazon_S3_Exception
578              */
579             if (!class_exists('Zend_Exception')) require 'exception.php';
580             throw new Zend_Service_Amazon_S3_Exception("
Cannot open file $path");
581         }
582
583         if (!is_array(
$meta)) {
584             
$meta = array();
585         }
586
587         if (!isset(
$meta[self::S3_CONTENT_TYPE_HEADER])) {
588            
$meta[self::S3_CONTENT_TYPE_HEADER] = self::getMimeType($path);
589         }
590
591         if(!isset(
$meta['Content-MD5'])) {
592             
$headers['Content-MD5'] = base64_encode(md5_file($path, true));
593         }
594
595         return 
$this->putObject($object$data$meta);
596     }
597
598     /**
599      * Remove a given object
600      *
601      * @param  string 
$object
602      * @return boolean
603      */
604     public function removeObject(
$object) {
605         
$object = $this->_fixupObjectName($object);
606         
$response = $this->_makeRequest('DELETE', $object);
607
608         // Look for a 204 No Content response
609         return (
$response->getStatus() == 204);
610     }
611
612     /**
613      * Make a request to Amazon S3
614      *
615      * @param  string 
$method    Request method
616      * @param  string 
$path        Path to requested object
617      * @param  array  
$params    Request parameters
618      * @param  array  
$headers    HTTP headers
619      * @param  string|resource 
$data        Request data
620      * @return Zend_Http_Response
621      */
622     public function _makeRequest(
$method$path = '', $params = null, $headers = array(), $data = null) {
623         
$retry_count = 0;
624
625         if (!is_array(
$headers)) {
626             
$headers = array($headers);
627         }
628
629         
$headers['Date'] = gmdate(DATE_RFC1123, time());
630
631         if(is_resource(
$data) && $method != 'PUT') {
632             /**
633              * @see Zend_Service_Amazon_S3_Exception
634              */
635             if (!class_exists('Zend_Exception')) require 'exception.php';
636             throw new Zend_Service_Amazon_S3_Exception("
Only PUT request supports stream data");
637         }
638
639         // build the end point out
640         
$parts = explode('/', $path, 2);
641         
$endpoint = clone($this->_endpoint);
642         if (
$parts[0]) {
643             // prepend bucket name to the hostname
644             
$endpoint->setHost($parts[0].'.'.$endpoint->getHost());
645         }
646         if (!empty(
$parts[1])) {
647             
$endpoint->setPath('/'.$parts[1]);
648         }
649         else {
650             
$endpoint->setPath('/');
651             if (
$parts[0]) {
652                 
$path = $parts[0].'/';
653             }
654         }
655
656         self::addSignature(
$method$path$headers);
657
658         
$client = self::getHttpClient();
659
660         
$client->resetParameters();
661         
$client->setUri($endpoint);
662         
$client->setAuth(false);
663         // Work around buglet in HTTP client - it doesn't clean headers
664         // Remove when ZHC is fixed
665         
$client->setHeaders(array('Content-MD5' => null,
666                                   'Expect'      => null,
667                                   'Range'       => null,
668                                   'x-amz-acl'   => null));
669
670         
$client->setHeaders($headers);
671
672         if (is_array(
$params)) {
673             foreach (
$params as $name => $value) {
674                 
$client->setParameterGet($name$value);
675             }
676          }
677
678          if ((
$method == 'PUT') && ($data !== null)) {
679              if (!isset(
$headers['Content-type'])) {
680                  
$headers['Content-type'] = self::getMimeType($path);
681              }
682              
$client->setRawData($data$headers['Content-type']);
683          }
684          do {
685             
$retry = false;
686
687             
$response = $client->request($method);
688             
$response_code = $response->getStatus();
689
690             // Some 5xx errors are expected, so retry automatically
691             if (
$response_code >= 500 && $response_code < 600 && $retry_count <= 5) {
692                 
$retry = true;
693                 
$retry_count++;
694                 sleep(
$retry_count / 4 * $retry_count);
695             }
696             else if (
$response_code == 307) {
697                 // Need to redirect, new S3 endpoint given
698                 // This should never happen as Zend_Http_Client will redirect automatically
699             }
700             else if (
$response_code == 100) {
701                 // echo 'OK to Continue';
702             }
703         } while (
$retry);
704
705         return 
$response;
706     }
707
708     /**
709      * Add the S3 Authorization signature to the request headers
710      *
711      * @param  string 
$method
712      * @param  string 
$path
713      * @param  array &
$headers
714      * @return string
715      */
716     protected function addSignature(
$method$path, &$headers) {
717         if (!is_array(
$headers)) {
718             
$headers = array($headers);
719         }
720
721         
$type = $md5 = $date = '';
722
723         // Search for the Content-type, Content-MD5 and Date headers
724         foreach (
$headers as $key => $val) {
725             if (strcasecmp(
$key, 'content-type') == 0) {
726                 
$type = $val;
727             }
728             else if (strcasecmp(
$key, 'content-md5') == 0) {
729                 
$md5 = $val;
730             }
731             else if (strcasecmp(
$key, 'date') == 0) {
732                 
$date = $val;
733             }
734         }
735
736         // If we have an x-amz-date header, use that instead of the normal Date
737         if (isset(
$headers['x-amz-date']) && isset($date)) {
738             
$date = '';
739         }
740
741         
$sig_str = "$method\n$md5\n$type\n$date\n";
742         // For x-amz- headers, combine like keys, lowercase them, sort them
743         // alphabetically and remove excess spaces around values
744         
$amz_headers = array();
745         foreach (
$headers as $key => $val) {
746             
$key = strtolower($key);
747             if (substr(
$key, 0, 6) == 'x-amz-') {
748                 if (is_array(
$val)) {
749                     
$amz_headers[$key] = $val;
750                 }
751                 else {
752                     
$amz_headers[$key][] = preg_replace('/\s+/', ' ', $val);
753                 }
754             }
755         }
756         if (!empty(
$amz_headers)) {
757             ksort(
$amz_headers);
758             foreach (
$amz_headers as $key => $val) {
759                 
$sig_str .= $key.':'.implode(',', $val)."\n";
760             }
761         }
762
763         
$sig_str .= '/'.parse_url($path, PHP_URL_PATH);
764         if (strpos(
$path, '?location') !== false) {
765             
$sig_str .= '?location';
766         }
767         else if (strpos(
$path, '?acl') !== false) {
768             
$sig_str .= '?acl';
769         }
770         else if (strpos(
$path, '?torrent') !== false) {
771             
$sig_str .= '?torrent';
772         }
773
774         
$signature = base64_encode(Zend_Crypt_Hmac::compute($this->_getSecretKey(), 'sha1', utf8_encode($sig_str),
 Zend_Crypt_Hmac::BINARY));
775         
$headers['Authorization'] = 'AWS '.$this->_getAccessKey().':'.$signature;
776
777         return 
$sig_str;
778     }
779
780     /**
781      * Attempt to get the content-type of a file based on the extension
782      *
783      * @param  string 
$path
784      * @return string
785      */
786     public static function getMimeType(
$path) {
787         
$ext = substr(strrchr($path, '.'), 1);
788
789         if(!
$ext) {
790             // shortcut
791             return 'binary/octet-stream';
792         }
793
794         switch (strtolower(
$ext)) {
795             case 'xls':
796                 
$content_type = 'application/excel';
797                 break;
798             case 'hqx':
799                 
$content_type = 'application/macbinhex40';
800                 break;
801             case 'doc':
802             case 'dot':
803             case 'wrd':
804                 
$content_type = 'application/msword';
805                 break;
806             case 'pdf':
807                 
$content_type = 'application/pdf';
808                 break;
809             case 'pgp':
810                 
$content_type = 'application/pgp';
811                 break;
812             case 'ps':
813             case 'eps':
814             case 'ai':
815                 
$content_type = 'application/postscript';
816                 break;
817             case 'ppt':
818                 
$content_type = 'application/powerpoint';
819                 break;
820             case 'rtf':
821                 
$content_type = 'application/rtf';
822                 break;
823             case 'tgz':
824             case 'gtar':
825                 
$content_type = 'application/x-gtar';
826                 break;
827             case 'gz':
828                 
$content_type = 'application/x-gzip';
829                 break;
830             case 'php':
831             case 'php3':
832             case 'php4':
833                 
$content_type = 'application/x-httpd-php';
834                 break;
835             case 'js':
836                 
$content_type = 'application/x-javascript';
837                 break;
838             case 'ppd':
839             case 'psd':
840                 
$content_type = 'application/x-photoshop';
841                 break;
842             case 'swf':
843             case 'swc':
844             case 'rf':
845                 
$content_type = 'application/x-shockwave-flash';
846                 break;
847             case 'tar':
848                 
$content_type = 'application/x-tar';
849                 break;
850             case 'zip':
851                 
$content_type = 'application/zip';
852                 break;
853             case 'mid':
854             case 'midi':
855             case 'kar':
856                 
$content_type = 'audio/midi';
857                 break;
858             case 'mp2':
859             case 'mp3':
860             case 'mpga':
861                 
$content_type = 'audio/mpeg';
862                 break;
863             case 'ra':
864                 
$content_type = 'audio/x-realaudio';
865                 break;
866             case 'wav':
867                 
$content_type = 'audio/wav';
868                 break;
869             case 'bmp':
870                 
$content_type = 'image/bitmap';
871                 break;
872             case 'gif':
873                 
$content_type = 'image/gif';
874                 break;
875             case 'iff':
876                 
$content_type = 'image/iff';
877                 break;
878             case 'jb2':
879                 
$content_type = 'image/jb2';
880                 break;
881             case 'jpg':
882             case 'jpe':
883             case 'jpeg':
884                 
$content_type = 'image/jpeg';
885                 break;
886             case 'jpx':
887                 
$content_type = 'image/jpx';
888                 break;
889             case 'png':
890                 
$content_type = 'image/png';
891                 break;
892             case 'tif':
893             case 'tiff':
894                 
$content_type = 'image/tiff';
895                 break;
896             case 'wbmp':
897                 
$content_type = 'image/vnd.wap.wbmp';
898                 break;
899             case 'xbm':
900                 
$content_type = 'image/xbm';
901                 break;
902             case 'css':
903                 
$content_type = 'text/css';
904                 break;
905             case 'txt':
906                 
$content_type = 'text/plain';
907                 break;
908             case 'htm':
909             case 'html':
910                 
$content_type = 'text/html';
911                 break;
912             case 'xml':
913                 
$content_type = 'text/xml';
914                 break;
915             case 'xsl':
916                 
$content_type = 'text/xsl';
917                 break;
918             case 'mpg':
919             case 'mpe':
920             case 'mpeg':
921                 
$content_type = 'video/mpeg';
922                 break;
923             case 'qt':
924             case 'mov':
925                 
$content_type = 'video/quicktime';
926                 break;
927             case 'avi':
928                 
$content_type = 'video/x-ms-video';
929                 break;
930             case 'eml':
931                 
$content_type = 'message/rfc822';
932                 break;
933             default:
934                 
$content_type = 'binary/octet-stream';
935                 break;
936         }
937
938         return 
$content_type;
939     }
940
941     /**
942      * Register this object as stream wrapper client
943      *
944      * @param  string 
$name
945      * @return Zend_Service_Amazon_S3
946      */
947     public function registerAsClient(
$name) {
948         self::
$_wrapperClients[$name] = $this;
949         return 
$this;
950     }
951
952     /**
953      * Unregister this object as stream wrapper client
954      *
955      * @param  string 
$name
956      * @return Zend_Service_Amazon_S3
957      */
958     public function unregisterAsClient(
$name) {
959         unset(self::
$_wrapperClients[$name]);
960         return 
$this;
961     }
962
963     /**
964      * Get wrapper client for stream type
965      *
966      * @param  string 
$name
967      * @return Zend_Service_Amazon_S3
968      */
969     public static function getWrapperClient(
$name) {
970         return self::
$_wrapperClients[$name];
971     }
972
973     /**
974      * Register this object as stream wrapper
975      *
976      * @param  string 
$name
977      * @return Zend_Service_Amazon_S3
978      */
979     public function registerStreamWrapper(
$name = 's3') {
980         /**
981          * @see Zend_Service_Amazon_S3_Stream
982          */
983         if (!class_exists('Zend_Service_Amazon_S3_Stream')) {
984             require 'stream.php';
985         }
986
987         stream_register_wrapper(
$name, 'Zend_Service_Amazon_S3_Stream');
988         
$this->registerAsClient($name);
989     }
990
991     /**
992      * Unregister this object as stream wrapper
993      *
994      * @param  string 
$name
995      * @return Zend_Service_Amazon_S3
996      */
997     public function unregisterStreamWrapper(
$name = 's3') {
998         stream_wrapper_unregister(
$name);
999         
$this->unregisterAsClient($name);
1000     }
1001 }