PageRenderTime 72ms CodeModel.GetById 35ms RepoModel.GetById 1ms app.codeStats 0ms

/class.s3.php

https://github.com/tomellis/php-aws
PHP | 331 lines | 255 code | 60 blank | 16 comment | 38 complexity | 07ca76b1d341c533bec462c570771c6b MD5 | raw file
  1. <?PHP
  2. // This is a fresh rewrite of the previous S3 class using PHP 5.
  3. // All transfers are done using PHP's native curl extension rather
  4. // than piping everything to the command line as before. (That was
  5. // a dirty hack in hindsight.) Copying S3 objects is now supported
  6. // as well. If you'd like to access the previous version, you may do
  7. // so here: http://code.google.com/p/php-aws/source/browse/branches/original-stable/
  8. class S3
  9. {
  10. private $key;
  11. private $privateKey;
  12. private $host;
  13. private $date;
  14. private $curlInfo;
  15. public function __construct($key, $private_key, $host = 's3.amazonaws.com')
  16. {
  17. $this->key = $key;
  18. $this->privateKey = $private_key;
  19. $this->host = $host;
  20. $this->date = gmdate('D, d M Y H:i:s T');
  21. return true;
  22. }
  23. public function listBuckets()
  24. {
  25. $request = array('verb' => 'GET', 'resource' => '/services/Walrus' . '/');
  26. $result = $this->sendRequest($request);
  27. $xml = simplexml_load_string($result);
  28. if($xml === false || !isset($xml->Buckets->Bucket))
  29. return false;
  30. $buckets = array();
  31. foreach($xml->Buckets->Bucket as $bucket)
  32. $buckets[] = (string) $bucket->Name;
  33. return $buckets;
  34. }
  35. public function createBucket($name)
  36. {
  37. $request = array('verb' => 'PUT', 'resource' => "/$name/");
  38. $result = $this->sendRequest($request);
  39. return $this->curlInfo['http_code'] == '200';
  40. }
  41. public function deleteBucket($name)
  42. {
  43. $request = array('verb' => 'DELETE', 'resource' => "/$name/");
  44. $result = $this->sendRequest($request);
  45. return $this->curlInfo['http_code'] == '204';
  46. }
  47. public function getBucketLocation($name)
  48. {
  49. $request = array('verb' => 'GET', 'resource' => "/$name/?location");
  50. $result = $this->sendRequest($request);
  51. $xml = simplexml_load_string($result);
  52. if($xml === false)
  53. return false;
  54. return (string) $xml->LocationConstraint;
  55. }
  56. public function getBucketContents($name, $prefix = null, $marker = null, $delimeter = null, $max_keys = null)
  57. {
  58. $contents = array();
  59. do
  60. {
  61. $q = array();
  62. if(!is_null($prefix)) $q[] = 'prefix=' . $prefix;
  63. if(!is_null($marker)) $q[] = 'marker=' . $marker;
  64. if(!is_null($delimeter)) $q[] = 'delimeter=' . $delimeter;
  65. if(!is_null($max_keys)) $q[] = 'max-keys=' . $max_keys;
  66. $q = implode('&', $q);
  67. if(strlen($q) > 0)
  68. $q = '?' . $q;
  69. $request = array('verb' => 'GET', 'resource' => "/$name/$q");
  70. $result = $this->sendRequest($request);
  71. $xml = simplexml_load_string($result);
  72. if($xml === false)
  73. return false;
  74. foreach($xml->Contents as $item)
  75. $contents[(string) $item->Key] = array('LastModified' => (string) $item->LastModified, 'ETag' => (string) $item->ETag, 'Size' => (string) $item->Size);
  76. $marker = (string) $xml->Marker;
  77. }
  78. while((string) $xml->IsTruncated == 'true' && is_null($max_keys));
  79. return $contents;
  80. }
  81. public function uploadFile($bucket_name, $s3_path, $fs_path, $web_accessible = false, $headers = null)
  82. {
  83. // Some useful headers you can set manually by passing in an associative array...
  84. // Cache-Control
  85. // Content-Type
  86. // Content-Disposition (alternate filename to present during web download)
  87. // Content-Encoding
  88. // x-amz-meta-*
  89. // x-amz-acl (private, public-read, public-read-write, authenticated-read)
  90. $request = array('verb' => 'PUT',
  91. 'resource' => "/$bucket_name/$s3_path",
  92. 'content-md5' => $this->base64(md5_file($fs_path)));
  93. $fh = fopen($fs_path, 'r');
  94. $curl_opts = array('CURLOPT_PUT' => true,
  95. 'CURLOPT_INFILE' => $fh,
  96. 'CURLOPT_INFILESIZE' => filesize($fs_path),
  97. 'CURLOPT_CUSTOMREQUEST' => 'PUT');
  98. if(is_null($headers))
  99. $headers = array();
  100. $headers['Content-MD5'] = $request['content-md5'];
  101. if($web_accessible === true && !isset($headers['x-amz-acl']))
  102. $headers['x-amz-acl'] = 'public-read';
  103. if(!isset($headers['Content-Type']))
  104. {
  105. $ext = strtolower(pathinfo($fs_path, PATHINFO_EXTENSION));
  106. $headers['Content-Type'] = isset($this->mimeTypes[$ext]) ? $this->mimeTypes[$ext] : 'application/octet-stream';
  107. }
  108. $request['content-type'] = $headers['Content-Type'];
  109. $result = $this->sendRequest($request, $headers, $curl_opts);
  110. fclose($fh);
  111. return $this->curlInfo['http_code'] == '200';
  112. }
  113. public function deleteObject($bucket_name, $s3_path)
  114. {
  115. $request = array('verb' => 'DELETE', 'resource' => "/$bucket_name/$s3_path");
  116. $result = $this->sendRequest($request);
  117. return $this->curlInfo['http_code'] == '204';
  118. }
  119. public function copyObject($bucket_name, $s3_path, $dest_bucket_name, $dest_s3_path)
  120. {
  121. $request = array('verb' => 'PUT', 'resource' => "/$dest_bucket_name/$dest_s3_path");
  122. $headers = array('x-amz-copy-source' => "/$bucket_name/$s3_path");
  123. $result = $this->sendRequest($request, $headers);
  124. if($this->curlInfo['http_code'] != '200')
  125. return false;
  126. $xml = simplexml_load_string($result);
  127. if($xml === false)
  128. return false;
  129. return isset($xml->LastModified);
  130. }
  131. public function getObjectInfo($bucket_name, $s3_path)
  132. {
  133. $request = array('verb' => 'HEAD', 'resource' => "/$bucket_name/$s3_path");
  134. $curl_opts = array('CURLOPT_HEADER' => true, 'CURLOPT_NOBODY' => true);
  135. $result = $this->sendRequest($request, null, $curl_opts);
  136. $xml = @simplexml_load_string($result);
  137. if($xml !== false)
  138. return false;
  139. preg_match_all('/^(\S*?): (.*?)$/ms', $result, $matches);
  140. $info = array();
  141. for($i = 0; $i < count($matches[1]); $i++)
  142. $info[$matches[1][$i]] = $matches[2][$i];
  143. if(!isset($info['Last-Modified']))
  144. return false;
  145. return $info;
  146. }
  147. public function downloadFile($bucket_name, $s3_path, $fs_path)
  148. {
  149. $request = array('verb' => 'GET', 'resource' => "/$bucket_name/$s3_path");
  150. $fh = fopen($fs_path, 'w');
  151. $curl_opts = array('CURLOPT_FILE' => $fh);
  152. if(is_null($headers))
  153. $headers = array();
  154. $result = $this->sendRequest($request, $headers, $curl_opts);
  155. fclose($fh);
  156. return $this->curlInfo['http_code'] == '200';
  157. }
  158. public function getAuthenticatedURLRelative($bucket_name, $s3_path, $seconds_till_expires = 3600)
  159. {
  160. return $this->getAuthenticatedURL($bucket_name, $s3_path, gmmktime() + $seconds_till_expires);
  161. }
  162. public function getAuthenticatedURL($bucket_name, $s3_path, $expires_on)
  163. {
  164. // $expires_on must be a GMT Unix timestamp
  165. $request = array('verb' => 'GET', 'resource' => "/$bucket_name/$s3_path", 'date' => $expires_on);
  166. $signature = urlencode($this->signature($request));
  167. $url = sprintf("http://%s.s3.amazonaws.com/%s?AWSAccessKeyId=%s&Expires=%s&Signature=%s",
  168. $bucket_name,
  169. $s3_path,
  170. $this->key,
  171. $expires_on,
  172. $signature);
  173. return $url;
  174. }
  175. private function sendRequest($request, $headers = null, $curl_opts = null)
  176. {
  177. if(is_null($headers))
  178. $headers = array();
  179. $headers['Date'] = $this->date;
  180. $headers['Authorization'] = 'AWS ' . $this->key . ':' . $this->signature($request, $headers);
  181. foreach($headers as $k => $v)
  182. $headers[$k] = "$k: $v";
  183. $uri = 'http://' . $this->host . $request['resource'];
  184. $ch = curl_init();
  185. curl_setopt($ch, CURLOPT_URL, $uri);
  186. curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $request['verb']);
  187. curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
  188. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  189. // curl_setopt($ch, CURLOPT_VERBOSE, true);
  190. if(is_array($curl_opts))
  191. {
  192. foreach($curl_opts as $k => $v)
  193. curl_setopt($ch, constant($k), $v);
  194. }
  195. $result = curl_exec($ch);
  196. $this->curlInfo = curl_getinfo($ch);
  197. curl_close($ch);
  198. return $result;
  199. }
  200. private function signature($request, $headers = null)
  201. {
  202. if(is_null($headers))
  203. $headers = array();
  204. $CanonicalizedAmzHeadersArr = array();
  205. $CanonicalizedAmzHeadersStr = '';
  206. foreach($headers as $k => $v)
  207. {
  208. $k = strtolower($k);
  209. if(substr($k, 0, 5) != 'x-amz') continue;
  210. if(isset($CanonicalizedAmzHeadersArr[$k]))
  211. $CanonicalizedAmzHeadersArr[$k] .= ',' . trim($v);
  212. else
  213. $CanonicalizedAmzHeadersArr[$k] = trim($v);
  214. }
  215. ksort($CanonicalizedAmzHeadersArr);
  216. foreach($CanonicalizedAmzHeadersArr as $k => $v)
  217. $CanonicalizedAmzHeadersStr .= "$k:$v\n";
  218. $str = $request['verb'] . "\n";
  219. $str .= isset($request['content-md5']) ? $request['content-md5'] . "\n" : "\n";
  220. $str .= isset($request['content-type']) ? $request['content-type'] . "\n" : "\n";
  221. $str .= isset($request['date']) ? $request['date'] . "\n" : $this->date . "\n";
  222. $str .= $CanonicalizedAmzHeadersStr . preg_replace('/\?.*/', '', $request['resource']);
  223. $sha1 = $this->hasher($str);
  224. return $this->base64($sha1);
  225. }
  226. // Algorithm adapted (stolen) from http://pear.php.net/package/Crypt_HMAC/)
  227. private function hasher($data)
  228. {
  229. $key = $this->privateKey;
  230. if(strlen($key) > 64)
  231. $key = pack('H40', sha1($key));
  232. if(strlen($key) < 64)
  233. $key = str_pad($key, 64, chr(0));
  234. $ipad = (substr($key, 0, 64) ^ str_repeat(chr(0x36), 64));
  235. $opad = (substr($key, 0, 64) ^ str_repeat(chr(0x5C), 64));
  236. return sha1($opad . pack('H40', sha1($ipad . $data)));
  237. }
  238. private function base64($str)
  239. {
  240. $ret = '';
  241. for($i = 0; $i < strlen($str); $i += 2)
  242. $ret .= chr(hexdec(substr($str, $i, 2)));
  243. return base64_encode($ret);
  244. }
  245. private function match($regex, $str, $i = 0)
  246. {
  247. if(preg_match($regex, $str, $match) == 1)
  248. return $match[$i];
  249. else
  250. return false;
  251. }
  252. private $mimeTypes = array("323" => "text/h323", "acx" => "application/internet-property-stream", "ai" => "application/postscript", "aif" => "audio/x-aiff", "aifc" => "audio/x-aiff", "aiff" => "audio/x-aiff",
  253. "asf" => "video/x-ms-asf", "asr" => "video/x-ms-asf", "asx" => "video/x-ms-asf", "au" => "audio/basic", "avi" => "video/quicktime", "axs" => "application/olescript", "bas" => "text/plain", "bcpio" => "application/x-bcpio", "bin" => "application/octet-stream", "bmp" => "image/bmp",
  254. "c" => "text/plain", "cat" => "application/vnd.ms-pkiseccat", "cdf" => "application/x-cdf", "cer" => "application/x-x509-ca-cert", "class" => "application/octet-stream", "clp" => "application/x-msclip", "cmx" => "image/x-cmx", "cod" => "image/cis-cod", "cpio" => "application/x-cpio", "crd" => "application/x-mscardfile",
  255. "crl" => "application/pkix-crl", "crt" => "application/x-x509-ca-cert", "csh" => "application/x-csh", "css" => "text/css", "dcr" => "application/x-director", "der" => "application/x-x509-ca-cert", "dir" => "application/x-director", "dll" => "application/x-msdownload", "dms" => "application/octet-stream", "doc" => "application/msword",
  256. "dot" => "application/msword", "dvi" => "application/x-dvi", "dxr" => "application/x-director", "eps" => "application/postscript", "etx" => "text/x-setext", "evy" => "application/envoy", "exe" => "application/octet-stream", "fif" => "application/fractals", "flr" => "x-world/x-vrml", "gif" => "image/gif",
  257. "gtar" => "application/x-gtar", "gz" => "application/x-gzip", "h" => "text/plain", "hdf" => "application/x-hdf", "hlp" => "application/winhlp", "hqx" => "application/mac-binhex40", "hta" => "application/hta", "htc" => "text/x-component", "htm" => "text/html", "html" => "text/html",
  258. "htt" => "text/webviewhtml", "ico" => "image/x-icon", "ief" => "image/ief", "iii" => "application/x-iphone", "ins" => "application/x-internet-signup", "isp" => "application/x-internet-signup", "jfif" => "image/pipeg", "jpe" => "image/jpeg", "jpeg" => "image/jpeg", "jpg" => "image/jpeg",
  259. "js" => "application/x-javascript", "latex" => "application/x-latex", "lha" => "application/octet-stream", "lsf" => "video/x-la-asf", "lsx" => "video/x-la-asf", "lzh" => "application/octet-stream", "m13" => "application/x-msmediaview", "m14" => "application/x-msmediaview", "m3u" => "audio/x-mpegurl", "man" => "application/x-troff-man",
  260. "mdb" => "application/x-msaccess", "me" => "application/x-troff-me", "mht" => "message/rfc822", "mhtml" => "message/rfc822", "mid" => "audio/mid", "mny" => "application/x-msmoney", "mov" => "video/quicktime", "movie" => "video/x-sgi-movie", "mp2" => "video/mpeg", "mp3" => "audio/mpeg",
  261. "mpa" => "video/mpeg", "mpe" => "video/mpeg", "mpeg" => "video/mpeg", "mpg" => "video/mpeg", "mpp" => "application/vnd.ms-project", "mpv2" => "video/mpeg", "ms" => "application/x-troff-ms", "mvb" => "application/x-msmediaview", "nws" => "message/rfc822", "oda" => "application/oda",
  262. "p10" => "application/pkcs10", "p12" => "application/x-pkcs12", "p7b" => "application/x-pkcs7-certificates", "p7c" => "application/x-pkcs7-mime", "p7m" => "application/x-pkcs7-mime", "p7r" => "application/x-pkcs7-certreqresp", "p7s" => "application/x-pkcs7-signature", "pbm" => "image/x-portable-bitmap", "pdf" => "application/pdf", "pfx" => "application/x-pkcs12",
  263. "pgm" => "image/x-portable-graymap", "pko" => "application/ynd.ms-pkipko", "pma" => "application/x-perfmon", "pmc" => "application/x-perfmon", "pml" => "application/x-perfmon", "pmr" => "application/x-perfmon", "pmw" => "application/x-perfmon", "png" => "image/png", "pnm" => "image/x-portable-anymap", "pot" => "application/vnd.ms-powerpoint", "ppm" => "image/x-portable-pixmap",
  264. "pps" => "application/vnd.ms-powerpoint", "ppt" => "application/vnd.ms-powerpoint", "prf" => "application/pics-rules", "ps" => "application/postscript", "pub" => "application/x-mspublisher", "qt" => "video/quicktime", "ra" => "audio/x-pn-realaudio", "ram" => "audio/x-pn-realaudio", "ras" => "image/x-cmu-raster", "rgb" => "image/x-rgb",
  265. "rmi" => "audio/mid", "roff" => "application/x-troff", "rtf" => "application/rtf", "rtx" => "text/richtext", "scd" => "application/x-msschedule", "sct" => "text/scriptlet", "setpay" => "application/set-payment-initiation", "setreg" => "application/set-registration-initiation", "sh" => "application/x-sh", "shar" => "application/x-shar",
  266. "sit" => "application/x-stuffit", "snd" => "audio/basic", "spc" => "application/x-pkcs7-certificates", "spl" => "application/futuresplash", "src" => "application/x-wais-source", "sst" => "application/vnd.ms-pkicertstore", "stl" => "application/vnd.ms-pkistl", "stm" => "text/html", "svg" => "image/svg+xml", "sv4cpio" => "application/x-sv4cpio",
  267. "sv4crc" => "application/x-sv4crc", "t" => "application/x-troff", "tar" => "application/x-tar", "tcl" => "application/x-tcl", "tex" => "application/x-tex", "texi" => "application/x-texinfo", "texinfo" => "application/x-texinfo", "tgz" => "application/x-compressed", "tif" => "image/tiff", "tiff" => "image/tiff",
  268. "tr" => "application/x-troff", "trm" => "application/x-msterminal", "tsv" => "text/tab-separated-values", "txt" => "text/plain", "uls" => "text/iuls", "ustar" => "application/x-ustar", "vcf" => "text/x-vcard", "vrml" => "x-world/x-vrml", "wav" => "audio/x-wav", "wcm" => "application/vnd.ms-works",
  269. "wdb" => "application/vnd.ms-works", "wks" => "application/vnd.ms-works", "wmf" => "application/x-msmetafile", "wps" => "application/vnd.ms-works", "wri" => "application/x-mswrite", "wrl" => "x-world/x-vrml", "wrz" => "x-world/x-vrml", "xaf" => "x-world/x-vrml", "xbm" => "image/x-xbitmap", "xla" => "application/vnd.ms-excel",
  270. "xlc" => "application/vnd.ms-excel", "xlm" => "application/vnd.ms-excel", "xls" => "application/vnd.ms-excel", "xlt" => "application/vnd.ms-excel", "xlw" => "application/vnd.ms-excel", "xof" => "x-world/x-vrml", "xpm" => "image/x-xpixmap", "xwd" => "image/x-xwindowdump", "z" => "application/x-compress", "zip" => "application/zip");
  271. }