Moodle  2.2.1
http://www.collinsharper.com
C:/xampp/htdocs/moodle/lib/zend/Zend/Service/Amazon/S3.php
Go to the documentation of this file.
00001 <?php
00026 require_once 'Zend/Service/Amazon/Abstract.php';
00027 
00031 require_once 'Zend/Crypt/Hmac.php';
00032 
00043 class Zend_Service_Amazon_S3 extends Zend_Service_Amazon_Abstract
00044 {
00050     protected static $_wrapperClients = array();
00051 
00057     protected $_endpoint;
00058 
00059     const S3_ENDPOINT = 's3.amazonaws.com';
00060 
00061     const S3_ACL_PRIVATE = 'private';
00062     const S3_ACL_PUBLIC_READ = 'public-read';
00063     const S3_ACL_PUBLIC_WRITE = 'public-read-write';
00064     const S3_ACL_AUTH_READ = 'authenticated-read';
00065 
00066     const S3_REQUESTPAY_HEADER = 'x-amz-request-payer';
00067     const S3_ACL_HEADER = 'x-amz-acl';
00068     const S3_CONTENT_TYPE_HEADER = 'Content-Type';
00069 
00076     public function setEndpoint($endpoint)
00077     {
00078         if (!($endpoint instanceof Zend_Uri_Http)) {
00079             $endpoint = Zend_Uri::factory($endpoint);
00080         }
00081         if (!$endpoint->valid()) {
00085             require_once 'Zend/Service/Amazon/S3/Exception.php';
00086             throw new Zend_Service_Amazon_S3_Exception('Invalid endpoint supplied');
00087         }
00088         $this->_endpoint = $endpoint;
00089         return $this;
00090     }
00091 
00097     public function getEndpoint()
00098     {
00099         return $this->_endpoint;
00100     }
00101 
00109     public function __construct($accessKey=null, $secretKey=null, $region=null)
00110     {
00111         parent::__construct($accessKey, $secretKey, $region);
00112 
00113         $this->setEndpoint('http://'.self::S3_ENDPOINT);
00114     }
00115 
00122     public function _validBucketName($bucket)
00123     {
00124         $len = strlen($bucket);
00125         if ($len < 3 || $len > 255) {
00129             require_once 'Zend/Service/Amazon/S3/Exception.php';
00130             throw new Zend_Service_Amazon_S3_Exception("Bucket name \"$bucket\" must be between 3 and 255 characters long");
00131         }
00132 
00133         if (preg_match('/[^a-z0-9\._-]/', $bucket)) {
00137             require_once 'Zend/Service/Amazon/S3/Exception.php';
00138             throw new Zend_Service_Amazon_S3_Exception("Bucket name \"$bucket\" contains invalid characters");
00139         }
00140 
00141         if (preg_match('/(\d){1,3}\.(\d){1,3}\.(\d){1,3}\.(\d){1,3}/', $bucket)) {
00145             require_once 'Zend/Service/Amazon/S3/Exception.php';
00146             throw new Zend_Service_Amazon_S3_Exception("Bucket name \"$bucket\" cannot be an IP address");
00147         }
00148         return true;
00149     }
00150 
00157     public function createBucket($bucket, $location = null)
00158     {
00159         $this->_validBucketName($bucket);
00160 
00161         if($location) {
00162             $data = '<CreateBucketConfiguration><LocationConstraint>'.$location.'</LocationConstraint></CreateBucketConfiguration>';
00163         }
00164         else {
00165             $data = null;
00166         }
00167         $response = $this->_makeRequest('PUT', $bucket, null, array(), $data);
00168 
00169         return ($response->getStatus() == 200);
00170     }
00171 
00178     public function isBucketAvailable($bucket)
00179     {
00180         $response = $this->_makeRequest('HEAD', $bucket, array('max-keys'=>0));
00181 
00182         return ($response->getStatus() != 404);
00183     }
00184 
00191     public function isObjectAvailable($object)
00192     {
00193         $response = $this->_makeRequest('HEAD', $object);
00194 
00195         return ($response->getStatus() == 200);
00196     }
00197 
00205     public function removeBucket($bucket)
00206     {
00207         $response = $this->_makeRequest('DELETE', $bucket);
00208 
00209         // Look for a 204 No Content response
00210         return ($response->getStatus() == 204);
00211     }
00212 
00219     public function getInfo($object)
00220     {
00221         $info = array();
00222 
00223         $object = $this->_fixupObjectName($object);
00224         $response = $this->_makeRequest('HEAD', $object);
00225 
00226         if ($response->getStatus() == 200) {
00227             $info['type'] = $response->getHeader('Content-type');
00228             $info['size'] = $response->getHeader('Content-length');
00229             $info['mtime'] = strtotime($response->getHeader('Last-modified'));
00230             $info['etag'] = $response->getHeader('ETag');
00231         }
00232         else {
00233             return false;
00234         }
00235 
00236         return $info;
00237     }
00238 
00244     public function getBuckets()
00245     {
00246         $response = $this->_makeRequest('GET');
00247 
00248         if ($response->getStatus() != 200) {
00249             return false;
00250         }
00251 
00252         $xml = new SimpleXMLElement($response->getBody());
00253 
00254         $buckets = array();
00255         foreach ($xml->Buckets->Bucket as $bucket) {
00256             $buckets[] = (string)$bucket->Name;
00257         }
00258 
00259         return $buckets;
00260     }
00261 
00268     public function cleanBucket($bucket)
00269     {
00270         $objects = $this->getObjectsByBucket($bucket);
00271         if (!$objects) {
00272             return false;
00273         }
00274 
00275         foreach ($objects as $object) {
00276             $this->removeObject("$bucket/$object");
00277         }
00278         return true;
00279     }
00280 
00294     public function getObjectsByBucket($bucket, $params = array())
00295     {
00296         $response = $this->_makeRequest('GET', $bucket, $params);
00297 
00298         if ($response->getStatus() != 200) {
00299             return false;
00300         }
00301 
00302         $xml = new SimpleXMLElement($response->getBody());
00303 
00304         $objects = array();
00305         if (isset($xml->Contents)) {
00306             foreach ($xml->Contents as $contents) {
00307                 foreach ($contents->Key as $object) {
00308                     $objects[] = (string)$object;
00309                 }
00310             }
00311         }
00312 
00313         return $objects;
00314     }
00315 
00322     protected function _fixupObjectName($object)
00323     {
00324         $nameparts = explode('/', $object);
00325 
00326         $this->_validBucketName($nameparts[0]);
00327 
00328         $firstpart = array_shift($nameparts);
00329         if (count($nameparts) == 0) {
00330             return $firstpart;
00331         }
00332 
00333         return $firstpart.'/'.join('/', array_map('rawurlencode', $nameparts));
00334     }
00335 
00343     public function getObject($object, $paidobject=false)
00344     {
00345         $object = $this->_fixupObjectName($object);
00346         if ($paidobject) {
00347             $response = $this->_makeRequest('GET', $object, null, array(self::S3_REQUESTPAY_HEADER => 'requester'));
00348         }
00349         else {
00350             $response = $this->_makeRequest('GET', $object);
00351         }
00352 
00353         if ($response->getStatus() != 200) {
00354             return false;
00355         }
00356 
00357         return $response->getBody();
00358     }
00359 
00370     public function getObjectStream($object, $streamfile = null, $paidobject=false)
00371     {
00372         $object = $this->_fixupObjectName($object);
00373         self::getHttpClient()->setStream($streamfile?$streamfile:true);
00374         if ($paidobject) {
00375             $response = $this->_makeRequest('GET', $object, null, array(self::S3_REQUESTPAY_HEADER => 'requester'));
00376         }
00377         else {
00378             $response = $this->_makeRequest('GET', $object);
00379         }
00380         self::getHttpClient()->setStream(null);
00381 
00382         if ($response->getStatus() != 200 || !($response instanceof Zend_Http_Response_Stream)) {
00383             return false;
00384         }
00385 
00386         return $response;
00387     }
00388 
00397     public function putObject($object, $data, $meta=null)
00398     {
00399         $object = $this->_fixupObjectName($object);
00400         $headers = (is_array($meta)) ? $meta : array();
00401 
00402         if(!is_resource($data)) {
00403             $headers['Content-MD5'] = base64_encode(md5($data, true));
00404         }
00405         $headers['Expect'] = '100-continue';
00406 
00407         if (!isset($headers[self::S3_CONTENT_TYPE_HEADER])) {
00408             $headers[self::S3_CONTENT_TYPE_HEADER] = self::getMimeType($object);
00409         }
00410 
00411         $response = $this->_makeRequest('PUT', $object, null, $headers, $data);
00412 
00413         // Check the MD5 Etag returned by S3 against and MD5 of the buffer
00414         if ($response->getStatus() == 200) {
00415             // It is escaped by double quotes for some reason
00416             $etag = str_replace('"', '', $response->getHeader('Etag'));
00417 
00418             if (is_resource($data) || $etag == md5($data)) {
00419                 return true;
00420             }
00421         }
00422 
00423         return false;
00424     }
00425 
00434     public function putFile($path, $object, $meta=null)
00435     {
00436         $data = @file_get_contents($path);
00437         if ($data === false) {
00441             require_once 'Zend/Service/Amazon/S3/Exception.php';
00442             throw new Zend_Service_Amazon_S3_Exception("Cannot read file $path");
00443         }
00444 
00445         if (!is_array($meta)) {
00446             $meta = array();
00447         }
00448 
00449         if (!isset($meta[self::S3_CONTENT_TYPE_HEADER])) {
00450            $meta[self::S3_CONTENT_TYPE_HEADER] = self::getMimeType($path);
00451         }
00452 
00453         return $this->putObject($object, $data, $meta);
00454     }
00455 
00464     public function putFileStream($path, $object, $meta=null)
00465     {
00466         $data = @fopen($path, "rb");
00467         if ($data === false) {
00471             require_once 'Zend/Service/Amazon/S3/Exception.php';
00472             throw new Zend_Service_Amazon_S3_Exception("Cannot open file $path");
00473         }
00474 
00475         if (!is_array($meta)) {
00476             $meta = array();
00477         }
00478 
00479         if (!isset($meta[self::S3_CONTENT_TYPE_HEADER])) {
00480            $meta[self::S3_CONTENT_TYPE_HEADER] = self::getMimeType($path);
00481         }
00482 
00483         if(!isset($meta['Content-MD5'])) {
00484             $headers['Content-MD5'] = base64_encode(md5_file($path, true));
00485         }
00486 
00487         return $this->putObject($object, $data, $meta);
00488     }
00489 
00496     public function removeObject($object)
00497     {
00498         $object = $this->_fixupObjectName($object);
00499         $response = $this->_makeRequest('DELETE', $object);
00500 
00501         // Look for a 204 No Content response
00502         return ($response->getStatus() == 204);
00503     }
00504 
00515     public function _makeRequest($method, $path='', $params=null, $headers=array(), $data=null)
00516     {
00517         $retry_count = 0;
00518 
00519         if (!is_array($headers)) {
00520             $headers = array($headers);
00521         }
00522 
00523         $headers['Date'] = gmdate(DATE_RFC1123, time());
00524 
00525         if(is_resource($data) && $method != 'PUT') {
00529             require_once 'Zend/Service/Amazon/S3/Exception.php';
00530             throw new Zend_Service_Amazon_S3_Exception("Only PUT request supports stream data");
00531         }
00532 
00533         // build the end point out
00534         $parts = explode('/', $path, 2);
00535         $endpoint = clone($this->_endpoint);
00536         if ($parts[0]) {
00537             // prepend bucket name to the hostname
00538             $endpoint->setHost($parts[0].'.'.$endpoint->getHost());
00539         }
00540         if (!empty($parts[1])) {
00541             $endpoint->setPath('/'.$parts[1]);
00542         }
00543         else {
00544             $endpoint->setPath('/');
00545             if ($parts[0]) {
00546                 $path = $parts[0].'/';
00547             }
00548         }
00549 
00550         self::addSignature($method, $path, $headers);
00551 
00552         $client = self::getHttpClient();
00553 
00554         $client->resetParameters();
00555         $client->setUri($endpoint);
00556         $client->setAuth(false);
00557         // Work around buglet in HTTP client - it doesn't clean headers
00558         // Remove when ZHC is fixed
00559         $client->setHeaders(array('Content-MD5' => null,
00560                                   'Expect'      => null,
00561                                   'Range'       => null,
00562                                   'x-amz-acl'   => null));
00563 
00564         $client->setHeaders($headers);
00565 
00566         if (is_array($params)) {
00567             foreach ($params as $name=>$value) {
00568                 $client->setParameterGet($name, $value);
00569             }
00570          }
00571 
00572          if (($method == 'PUT') && ($data !== null)) {
00573              if (!isset($headers['Content-type'])) {
00574                  $headers['Content-type'] = self::getMimeType($path);
00575              }
00576              $client->setRawData($data, $headers['Content-type']);
00577          }
00578          do {
00579             $retry = false;
00580 
00581             $response = $client->request($method);
00582             $response_code = $response->getStatus();
00583 
00584             // Some 5xx errors are expected, so retry automatically
00585             if ($response_code >= 500 && $response_code < 600 && $retry_count <= 5) {
00586                 $retry = true;
00587                 $retry_count++;
00588                 sleep($retry_count / 4 * $retry_count);
00589             }
00590             else if ($response_code == 307) {
00591                 // Need to redirect, new S3 endpoint given
00592                 // This should never happen as Zend_Http_Client will redirect automatically
00593             }
00594             else if ($response_code == 100) {
00595                 // echo 'OK to Continue';
00596             }
00597         } while ($retry);
00598 
00599         return $response;
00600     }
00601 
00610     protected function addSignature($method, $path, &$headers)
00611     {
00612         if (!is_array($headers)) {
00613             $headers = array($headers);
00614         }
00615 
00616         $type = $md5 = $date = '';
00617 
00618         // Search for the Content-type, Content-MD5 and Date headers
00619         foreach ($headers as $key=>$val) {
00620             if (strcasecmp($key, 'content-type') == 0) {
00621                 $type = $val;
00622             }
00623             else if (strcasecmp($key, 'content-md5') == 0) {
00624                 $md5 = $val;
00625             }
00626             else if (strcasecmp($key, 'date') == 0) {
00627                 $date = $val;
00628             }
00629         }
00630 
00631         // If we have an x-amz-date header, use that instead of the normal Date
00632         if (isset($headers['x-amz-date']) && isset($date)) {
00633             $date = '';
00634         }
00635 
00636         $sig_str = "$method\n$md5\n$type\n$date\n";
00637         // For x-amz- headers, combine like keys, lowercase them, sort them
00638         // alphabetically and remove excess spaces around values
00639         $amz_headers = array();
00640         foreach ($headers as $key=>$val) {
00641             $key = strtolower($key);
00642             if (substr($key, 0, 6) == 'x-amz-') {
00643                 if (is_array($val)) {
00644                     $amz_headers[$key] = $val;
00645                 }
00646                 else {
00647                     $amz_headers[$key][] = preg_replace('/\s+/', ' ', $val);
00648                 }
00649             }
00650         }
00651         if (!empty($amz_headers)) {
00652             ksort($amz_headers);
00653             foreach ($amz_headers as $key=>$val) {
00654                 $sig_str .= $key.':'.implode(',', $val)."\n";
00655             }
00656         }
00657 
00658         $sig_str .= '/'.parse_url($path, PHP_URL_PATH);
00659         if (strpos($path, '?location') !== false) {
00660             $sig_str .= '?location';
00661         }
00662         else if (strpos($path, '?acl') !== false) {
00663             $sig_str .= '?acl';
00664         }
00665         else if (strpos($path, '?torrent') !== false) {
00666             $sig_str .= '?torrent';
00667         }
00668 
00669         $signature = base64_encode(Zend_Crypt_Hmac::compute($this->_getSecretKey(), 'sha1', utf8_encode($sig_str), Zend_Crypt_Hmac::BINARY));
00670         $headers['Authorization'] = 'AWS '.$this->_getAccessKey().':'.$signature;
00671 
00672         return $sig_str;
00673     }
00674 
00681     public static function getMimeType($path)
00682     {
00683         $ext = substr(strrchr($path, '.'), 1);
00684 
00685         if(!$ext) {
00686             // shortcut
00687             return 'binary/octet-stream';
00688         }
00689 
00690         switch (strtolower($ext)) {
00691             case 'xls':
00692                 $content_type = 'application/excel';
00693                 break;
00694             case 'hqx':
00695                 $content_type = 'application/macbinhex40';
00696                 break;
00697             case 'doc':
00698             case 'dot':
00699             case 'wrd':
00700                 $content_type = 'application/msword';
00701                 break;
00702             case 'pdf':
00703                 $content_type = 'application/pdf';
00704                 break;
00705             case 'pgp':
00706                 $content_type = 'application/pgp';
00707                 break;
00708             case 'ps':
00709             case 'eps':
00710             case 'ai':
00711                 $content_type = 'application/postscript';
00712                 break;
00713             case 'ppt':
00714                 $content_type = 'application/powerpoint';
00715                 break;
00716             case 'rtf':
00717                 $content_type = 'application/rtf';
00718                 break;
00719             case 'tgz':
00720             case 'gtar':
00721                 $content_type = 'application/x-gtar';
00722                 break;
00723             case 'gz':
00724                 $content_type = 'application/x-gzip';
00725                 break;
00726             case 'php':
00727             case 'php3':
00728             case 'php4':
00729                 $content_type = 'application/x-httpd-php';
00730                 break;
00731             case 'js':
00732                 $content_type = 'application/x-javascript';
00733                 break;
00734             case 'ppd':
00735             case 'psd':
00736                 $content_type = 'application/x-photoshop';
00737                 break;
00738             case 'swf':
00739             case 'swc':
00740             case 'rf':
00741                 $content_type = 'application/x-shockwave-flash';
00742                 break;
00743             case 'tar':
00744                 $content_type = 'application/x-tar';
00745                 break;
00746             case 'zip':
00747                 $content_type = 'application/zip';
00748                 break;
00749             case 'mid':
00750             case 'midi':
00751             case 'kar':
00752                 $content_type = 'audio/midi';
00753                 break;
00754             case 'mp2':
00755             case 'mp3':
00756             case 'mpga':
00757                 $content_type = 'audio/mpeg';
00758                 break;
00759             case 'ra':
00760                 $content_type = 'audio/x-realaudio';
00761                 break;
00762             case 'wav':
00763                 $content_type = 'audio/wav';
00764                 break;
00765             case 'bmp':
00766                 $content_type = 'image/bitmap';
00767                 break;
00768             case 'gif':
00769                 $content_type = 'image/gif';
00770                 break;
00771             case 'iff':
00772                 $content_type = 'image/iff';
00773                 break;
00774             case 'jb2':
00775                 $content_type = 'image/jb2';
00776                 break;
00777             case 'jpg':
00778             case 'jpe':
00779             case 'jpeg':
00780                 $content_type = 'image/jpeg';
00781                 break;
00782             case 'jpx':
00783                 $content_type = 'image/jpx';
00784                 break;
00785             case 'png':
00786                 $content_type = 'image/png';
00787                 break;
00788             case 'tif':
00789             case 'tiff':
00790                 $content_type = 'image/tiff';
00791                 break;
00792             case 'wbmp':
00793                 $content_type = 'image/vnd.wap.wbmp';
00794                 break;
00795             case 'xbm':
00796                 $content_type = 'image/xbm';
00797                 break;
00798             case 'css':
00799                 $content_type = 'text/css';
00800                 break;
00801             case 'txt':
00802                 $content_type = 'text/plain';
00803                 break;
00804             case 'htm':
00805             case 'html':
00806                 $content_type = 'text/html';
00807                 break;
00808             case 'xml':
00809                 $content_type = 'text/xml';
00810                 break;
00811             case 'xsl':
00812                 $content_type = 'text/xsl';
00813                 break;
00814             case 'mpg':
00815             case 'mpe':
00816             case 'mpeg':
00817                 $content_type = 'video/mpeg';
00818                 break;
00819             case 'qt':
00820             case 'mov':
00821                 $content_type = 'video/quicktime';
00822                 break;
00823             case 'avi':
00824                 $content_type = 'video/x-ms-video';
00825                 break;
00826             case 'eml':
00827                 $content_type = 'message/rfc822';
00828                 break;
00829             default:
00830                 $content_type = 'binary/octet-stream';
00831                 break;
00832         }
00833 
00834         return $content_type;
00835     }
00836 
00843     public function registerAsClient($name)
00844     {
00845         self::$_wrapperClients[$name] = $this;
00846         return $this;
00847     }
00848 
00855     public function unregisterAsClient($name)
00856     {
00857         unset(self::$_wrapperClients[$name]);
00858         return $this;
00859     }
00860 
00867     public static function getWrapperClient($name)
00868     {
00869         return self::$_wrapperClients[$name];
00870     }
00871 
00878     public function registerStreamWrapper($name='s3')
00879     {
00883         require_once 'Zend/Service/Amazon/S3/Stream.php';
00884 
00885         stream_register_wrapper($name, 'Zend_Service_Amazon_S3_Stream');
00886         $this->registerAsClient($name);
00887     }
00888 
00895     public function unregisterStreamWrapper($name='s3')
00896     {
00897         stream_wrapper_unregister($name);
00898         $this->unregisterAsClient($name);
00899     }
00900 }
 All Data Structures Namespaces Files Functions Variables Enumerations