PageRenderTime 33ms CodeModel.GetById 2ms app.highlight 22ms RepoModel.GetById 1ms app.codeStats 0ms

/library/Zend/Service/Amazon/S3.php

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