PluginProbe
ManageWP Worker / 3.9.7
ManageWP Worker v3.9.7
4.9.38 4.9.37 4.9.36 4.9.35 4.9.34 3.8.7 3.8.8 3.9.0 3.9.1 3.9.10 3.9.11 3.9.12 3.9.13 3.9.14 3.9.15 3.9.16 3.9.17 3.9.18 3.9.19 3.9.2 3.9.20 3.9.21 3.9.22 3.9.23 3.9.24 All 73 releases
worker / lib / s3.php

s3.php in ManageWP Worker 3.9.7, at lib/s3.php

910 lines 30.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * $Id$
4 *
5 * Copyright (c) 2007, Donovan Schonknecht. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * - Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /**
30 * Amazon S3 PHP class
31 *
32 * @link http://undesigned.org.za/2007/10/22/amazon-s3-php-class
33 * @version 0.2.3
34 */
35 class S3 {
36 // ACL flags
37 const ACL_PRIVATE = 'private';
38 const ACL_PUBLIC_READ = 'public-read';
39 const ACL_PUBLIC_READ_WRITE = 'public-read-write';
40
41 private static $__accessKey; // AWS Access key
42 private static $__secretKey; // AWS Secret key
43
44
45 /**
46 * Constructor, used if you're not calling the class statically
47 *
48 * @param string $accessKey Access key
49 * @param string $secretKey Secret key
50 * @return void
51 */
52 public function __construct($accessKey = null, $secretKey = null) {
53 if ($accessKey !== null && $secretKey !== null)
54 self::setAuth($accessKey, $secretKey);
55 }
56
57
58 /**
59 * Set access information
60 *
61 * @param string $accessKey Access key
62 * @param string $secretKey Secret key
63 * @return void
64 */
65 public static function setAuth($accessKey, $secretKey) {
66 self::$__accessKey = $accessKey;
67 self::$__secretKey = $secretKey;
68 }
69
70
71 /**
72 * Get a list of buckets
73 *
74 * @param boolean $detailed Returns detailed bucket list when true
75 * @return array | false
76 */
77 public static function listBuckets($detailed = false) {
78 $rest = new S3Request('GET', '', '');
79 $rest = $rest->getResponse();
80 if ($rest->error === false && $rest->code !== 200)
81 $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
82 if ($rest->error !== false) {
83 trigger_error(sprintf("S3::listBuckets(): [%s] %s", $rest->error['code'], $rest->error['message']), E_USER_WARNING);
84 return false;
85 }
86 $results = array(); //var_dump($rest->body);
87 if (!isset($rest->body->Buckets)) return $results;
88
89 if ($detailed) {
90 if (isset($rest->body->Owner, $rest->body->Owner->ID, $rest->body->Owner->DisplayName))
91 $results['owner'] = array(
92 'id' => (string)$rest->body->Owner->ID, 'name' => (string)$rest->body->Owner->ID
93 );
94 $results['buckets'] = array();
95 foreach ($rest->body->Buckets->Bucket as $b)
96 $results['buckets'][] = array(
97 'name' => (string)$b->Name, 'time' => strtotime((string)$b->CreationDate)
98 );
99 } else
100 foreach ($rest->body->Buckets->Bucket as $b) $results[] = (string)$b->Name;
101
102 return $results;
103 }
104
105
106 /*
107 * Get contents for a bucket
108 *
109 * If maxKeys is null this method will loop through truncated result sets
110 *
111 * @param string $bucket Bucket name
112 * @param string $prefix Prefix
113 * @param string $marker Marker (last file listed)
114 * @param string $maxKeys Max keys (maximum number of keys to return)
115 * @return array | false
116 */
117 public static function getBucket($bucket, $prefix = null, $marker = null, $maxKeys = null) {
118 $rest = new S3Request('GET', $bucket, '');
119 if ($prefix !== null && $prefix !== '') $rest->setParameter('prefix', $prefix);
120 if ($marker !== null && $prefix !== '') $rest->setParameter('marker', $marker);
121 if ($maxKeys !== null && $prefix !== '') $rest->setParameter('max-keys', $maxKeys);
122 $response = $rest->getResponse();
123 if ($response->error === false && $response->code !== 200)
124 $response->error = array('code' => $response->code, 'message' => 'Unexpected HTTP status');
125 if ($response->error !== false) {
126 trigger_error(sprintf("S3::getBucket(): [%s] %s", $response->error['code'], $response->error['message']), E_USER_WARNING);
127 return false;
128 }
129
130 $results = array();
131
132 $lastMarker = null;
133 if (isset($response->body, $response->body->Contents))
134 foreach ($response->body->Contents as $c) {
135 $results[(string)$c->Key] = array(
136 'name' => (string)$c->Key,
137 'time' => strToTime((string)$c->LastModified),
138 'size' => (int)$c->Size,
139 'hash' => substr((string)$c->ETag, 1, -1)
140 );
141 $lastMarker = (string)$c->Key;
142 //$response->body->IsTruncated = 'true'; break;
143 }
144
145
146 if (isset($response->body->IsTruncated) &&
147 (string)$response->body->IsTruncated == 'false') return $results;
148
149 // Loop through truncated results if maxKeys isn't specified
150 if ($maxKeys == null && $lastMarker !== null && (string)$response->body->IsTruncated == 'true')
151 do {
152 $rest = new S3Request('GET', $bucket, '');
153 if ($prefix !== null) $rest->setParameter('prefix', $prefix);
154 $rest->setParameter('marker', $lastMarker);
155
156 if (($response = $rest->getResponse(true)) == false || $response->code !== 200) break;
157 if (isset($response->body, $response->body->Contents))
158 foreach ($response->body->Contents as $c) {
159 $results[(string)$c->Key] = array(
160 'name' => (string)$c->Key,
161 'time' => strToTime((string)$c->LastModified),
162 'size' => (int)$c->Size,
163 'hash' => substr((string)$c->ETag, 1, -1)
164 );
165 $lastMarker = (string)$c->Key;
166 }
167 } while ($response !== false && (string)$response->body->IsTruncated == 'true');
168
169 return $results;
170 }
171
172
173 /**
174 * Put a bucket
175 *
176 * @param string $bucket Bucket name
177 * @param constant $acl ACL flag
178 * @return boolean
179 */
180 public function putBucket($bucket, $acl = self::ACL_PRIVATE) {
181 $rest = new S3Request('PUT', $bucket, '');
182 $rest->setAmzHeader('x-amz-acl', $acl);
183 $rest = $rest->getResponse();
184 if ($rest->error === false && $rest->code !== 200)
185 $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
186 if ($rest->error !== false) {
187 trigger_error(sprintf("S3::putBucket({$bucket}): [%s] %s",
188 $rest->error['code'], $rest->error['message']), E_USER_WARNING);
189 return false;
190 }
191 return true;
192 }
193
194
195 /**
196 * Delete an empty bucket
197 *
198 * @param string $bucket Bucket name
199 * @return boolean
200 */
201 public function deleteBucket($bucket = '') {
202 $rest = new S3Request('DELETE', $bucket);
203 $rest = $rest->getResponse();
204 if ($rest->error === false && $rest->code !== 204)
205 $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
206 if ($rest->error !== false) {
207 trigger_error(sprintf("S3::deleteBucket({$bucket}): [%s] %s",
208 $rest->error['code'], $rest->error['message']), E_USER_WARNING);
209 return false;
210 }
211 return true;
212 }
213
214
215 /**
216 * Create input info array for putObject()
217 *
218 * @param string $file Input file
219 * @param mixed $md5sum Use MD5 hash (supply a string if you want to use your own)
220 * @return array | false
221 */
222 public static function inputFile($file, $md5sum = true) {
223 if (!file_exists($file) || !is_file($file) || !is_readable($file)) {
224 trigger_error('S3::inputFile(): Unable to open input file: '.$file, E_USER_WARNING);
225 return false;
226 }
227 return array('file' => $file, 'size' => filesize($file),
228 'md5sum' => $md5sum !== false ? (is_string($md5sum) ? $md5sum :
229 base64_encode(md5_file($file, true))) : '');
230 }
231
232
233 /**
234 * Use a resource for input
235 *
236 * @param string $file Input file
237 * @param integer $bufferSize Input byte size
238 * @param string $md5sum MD5 hash to send (optional)
239 * @return array | false
240 */
241 public static function inputResource(&$resource, $bufferSize, $md5sum = '') {
242 if (!is_resource($resource) || $bufferSize <= 0) {
243 trigger_error('S3::inputResource(): Invalid resource or buffer size', E_USER_WARNING);
244 return false;
245 }
246 $input = array('size' => $bufferSize, 'md5sum' => $md5sum);
247 $input['fp'] =& $resource;
248 return $input;
249 }
250
251
252 /**
253 * Put an object
254 *
255 * @param mixed $input Input data
256 * @param string $bucket Bucket name
257 * @param string $uri Object URI
258 * @param constant $acl ACL constant
259 * @param array $metaHeaders Array of x-amz-meta-* headers
260 * @param string $contentType Content type
261 * @return boolean
262 */
263 public static function putObject($input, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $contentType = null) {
264 if ($input == false) return false;
265 $rest = new S3Request('PUT', $bucket, $uri);
266
267 if (is_string($input)) $input = array(
268 'data' => $input, 'size' => strlen($input),
269 'md5sum' => base64_encode(md5($input, true))
270 );
271
272 // Data
273 if (isset($input['fp']))
274 $rest->fp =& $input['fp'];
275 elseif (isset($input['file']))
276 $rest->fp = @fopen($input['file'], 'rb');
277 elseif (isset($input['data']))
278 $rest->data = $input['data'];
279
280 // Content-Length (required)
281 if (isset($input['size']) && $input['size'] > 0)
282 $rest->size = $input['size'];
283 else {
284 if (isset($input['file']))
285 $rest->size = filesize($input['file']);
286 elseif (isset($input['data']))
287 $rest->size = strlen($input['data']);
288 }
289
290 // Content-Type
291 if ($contentType !== null)
292 $input['type'] = $contentType;
293 elseif (!isset($input['type']) && isset($input['file']))
294 $input['type'] = self::__getMimeType($input['file']);
295 else
296 $input['type'] = 'application/octet-stream';
297
298 // We need to post with the content-length and content-type, MD5 is optional
299 if ($rest->size > 0 && ($rest->fp !== false || $rest->data !== false)) {
300 $rest->setHeader('Content-Type', $input['type']);
301 if (isset($input['md5sum'])) $rest->setHeader('Content-MD5', $input['md5sum']);
302
303 $rest->setAmzHeader('x-amz-acl', $acl);
304 foreach ($metaHeaders as $h => $v) $rest->setAmzHeader('x-amz-meta-'.$h, $v);
305 $rest->getResponse();
306 } else
307 $rest->response->error = array('code' => 0, 'message' => 'Missing input parameters');
308
309 if ($rest->response->error === false && $rest->response->code !== 200)
310 $rest->response->error = array('code' => $rest->response->code, 'message' => 'Unexpected HTTP status');
311 if ($rest->response->error !== false) {
312 trigger_error(sprintf("S3::putObject(): [%s] %s", $rest->response->error['code'], $rest->response->error['message']), E_USER_WARNING);
313 return false;
314 }
315 return true;
316 }
317
318
319 /**
320 * Puts an object from a file (legacy function)
321 *
322 * @param string $file Input file path
323 * @param string $bucket Bucket name
324 * @param string $uri Object URI
325 * @param constant $acl ACL constant
326 * @param array $metaHeaders Array of x-amz-meta-* headers
327 * @param string $contentType Content type
328 * @return boolean
329 */
330 public static function putObjectFile($file, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $contentType = null) {
331 return self::putObject(S3::inputFile($file), $bucket, $uri, $acl, $metaHeaders, $contentType);
332 }
333
334
335 /**
336 * Put an object from a string (legacy function)
337 *
338 * @param string $string Input data
339 * @param string $bucket Bucket name
340 * @param string $uri Object URI
341 * @param constant $acl ACL constant
342 * @param array $metaHeaders Array of x-amz-meta-* headers
343 * @param string $contentType Content type
344 * @return boolean
345 */
346 public function putObjectString($string, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $contentType = 'text/plain') {
347 return self::putObject($string, $bucket, $uri, $acl, $metaHeaders, $contentType);
348 }
349
350
351 /**
352 * Get an object
353 *
354 * @param string $bucket Bucket name
355 * @param string $uri Object URI
356 * @param mixed &$saveTo Filename or resource to write to
357 * @return mixed
358 */
359 public static function getObject($bucket = '', $uri = '', $saveTo = false) {
360 $rest = new S3Request('GET', $bucket, $uri);
361 if ($saveTo !== false) {
362 if (is_resource($saveTo))
363 $rest->fp =& $saveTo;
364 else
365 if (($rest->fp = @fopen($saveTo, 'wb')) == false)
366 $rest->response->error = array('code' => 0, 'message' => 'Unable to open save file for writing: '.$saveTo);
367 }
368 if ($rest->response->error === false) $rest->getResponse();
369
370 if ($rest->response->error === false && $rest->response->code !== 200)
371 $rest->response->error = array('code' => $rest->response->code, 'message' => 'Unexpected HTTP status');
372 if ($rest->response->error !== false) {
373 trigger_error(sprintf("S3::getObject({$bucket}, {$uri}): [%s] %s",
374 $rest->response->error['code'], $rest->response->error['message']), E_USER_WARNING);
375 return false;
376 }
377 $rest->file = realpath($saveTo);
378 return $rest->response;
379 }
380
381
382 /**
383 * Get object information
384 *
385 * @param string $bucket Bucket name
386 * @param string $uri Object URI
387 * @param boolean $returnInfo Return response information
388 * @return mixed | false
389 */
390 public static function getObjectInfo($bucket = '', $uri = '', $returnInfo = true) {
391 $rest = new S3Request('HEAD', $bucket, $uri);
392 $rest = $rest->getResponse();
393 if ($rest->error === false && ($rest->code !== 200 && $rest->code !== 404))
394 $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
395 if ($rest->error !== false) {
396 trigger_error(sprintf("S3::getObjectInfo({$bucket}, {$uri}): [%s] %s",
397 $rest->error['code'], $rest->error['message']), E_USER_WARNING);
398 return false;
399 }
400 return $rest->code == 200 ? $returnInfo ? $rest->headers : true : false;
401 }
402
403
404 /**
405 * Set logging for a bucket
406 *
407 * @param string $bucket Bucket name
408 * @param string $targetBucket Target bucket (where logs are stored)
409 * @param string $targetPrefix Log prefix (e,g; domain.com-)
410 * @return boolean
411 */
412 public static function setBucketLogging($bucket, $targetBucket, $targetPrefix) {
413 $dom = new DOMDocument;
414 $bucketLoggingStatus = $dom->createElement('BucketLoggingStatus');
415 $bucketLoggingStatus->setAttribute('xmlns', 'http://s3.amazonaws.com/doc/2006-03-01/');
416
417 $loggingEnabled = $dom->createElement('LoggingEnabled');
418
419 $loggingEnabled->appendChild($dom->createElement('TargetBucket', $targetBucket));
420 $loggingEnabled->appendChild($dom->createElement('TargetPrefix', $targetPrefix));
421
422 // TODO: Add TargetGrants
423
424 $bucketLoggingStatus->appendChild($loggingEnabled);
425 $dom->appendChild($bucketLoggingStatus);
426
427 $rest = new S3Request('PUT', $bucket, '');
428 $rest->setParameter('logging', null);
429 $rest->data = $dom->saveXML();
430 $rest->size = strlen($rest->data);
431 $rest->setHeader('Content-Type', 'application/xml');
432 $rest = $rest->getResponse();
433 if ($rest->error === false && $rest->code !== 200)
434 $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
435 if ($rest->error !== false) {
436 trigger_error(sprintf("S3::setBucketLogging({$bucket}, {$uri}): [%s] %s",
437 $rest->error['code'], $rest->error['message']), E_USER_WARNING);
438 return false;
439 }
440 return true;
441 }
442
443
444 /**
445 * Get logging status for a bucket
446 *
447 * This will return false if logging is not enabled.
448 * Note: To enable logging, you also need to grant write access to the log group
449 *
450 * @param string $bucket Bucket name
451 * @return array | false
452 */
453 public static function getBucketLogging($bucket = '') {
454 $rest = new S3Request('GET', $bucket, '');
455 $rest->setParameter('logging', null);
456 $rest = $rest->getResponse();
457 if ($rest->error === false && $rest->code !== 200)
458 $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
459 if ($rest->error !== false) {
460 trigger_error(sprintf("S3::getBucketLogging({$bucket}): [%s] %s",
461 $rest->error['code'], $rest->error['message']), E_USER_WARNING);
462 return false;
463 }
464 if (!isset($rest->body->LoggingEnabled)) return false; // No logging
465 return array(
466 'targetBucket' => (string)$rest->body->LoggingEnabled->TargetBucket,
467 'targetPrefix' => (string)$rest->body->LoggingEnabled->TargetPrefix,
468 );
469 }
470
471
472 /**
473 * Set object or bucket Access Control Policy
474 *
475 * @param string $bucket Bucket name
476 * @param string $uri Object URI
477 * @param array $acp Access Control Policy Data (same as the data returned from getAccessControlPolicy)
478 * @return boolean
479 */
480 public static function setAccessControlPolicy($bucket, $uri = '', $acp = array()) {
481 $dom = new DOMDocument;
482 $dom->formatOutput = true;
483 $accessControlPolicy = $dom->createElement('AccessControlPolicy');
484 $accessControlList = $dom->createElement('AccessControlList');
485
486 // It seems the owner has to be passed along too
487 $owner = $dom->createElement('Owner');
488 $owner->appendChild($dom->createElement('ID', $acp['owner']['id']));
489 $owner->appendChild($dom->createElement('DisplayName', $acp['owner']['name']));
490 $accessControlPolicy->appendChild($owner);
491
492 foreach ($acp['acl'] as $g) {
493 $grant = $dom->createElement('Grant');
494 $grantee = $dom->createElement('Grantee');
495 $grantee->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
496 if (isset($g['id'])) { // CanonicalUser (DisplayName is omitted)
497 $grantee->setAttribute('xsi:type', 'CanonicalUser');
498 $grantee->appendChild($dom->createElement('ID', $g['id']));
499 } elseif (isset($g['email'])) { // AmazonCustomerByEmail
500 $grantee->setAttribute('xsi:type', 'AmazonCustomerByEmail');
501 $grantee->appendChild($dom->createElement('EmailAddress', $g['email']));
502 } elseif ($g['type'] == 'Group') { // Group
503 $grantee->setAttribute('xsi:type', 'Group');
504 $grantee->appendChild($dom->createElement('URI', $g['uri']));
505 }
506 $grant->appendChild($grantee);
507 $grant->appendChild($dom->createElement('Permission', $g['permission']));
508 $accessControlList->appendChild($grant);
509 }
510
511 $accessControlPolicy->appendChild($accessControlList);
512 $dom->appendChild($accessControlPolicy);
513
514 $rest = new S3Request('PUT', $bucket, '');
515 $rest->setParameter('acl', null);
516 $rest->data = $dom->saveXML();
517 $rest->size = strlen($rest->data);
518 $rest->setHeader('Content-Type', 'application/xml');
519 $rest = $rest->getResponse();
520 if ($rest->error === false && $rest->code !== 200)
521 $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
522 if ($rest->error !== false) {
523 trigger_error(sprintf("S3::setAccessControlPolicy({$bucket}, {$uri}): [%s] %s",
524 $rest->error['code'], $rest->error['message']), E_USER_WARNING);
525 return false;
526 }
527 return true;
528 }
529
530
531 /**
532 * Get object or bucket Access Control Policy
533 *
534 * Currently this will trigger an error if there is no ACL on an object (will fix soon)
535 *
536 * @param string $bucket Bucket name
537 * @param string $uri Object URI
538 * @return mixed | false
539 */
540 public static function getAccessControlPolicy($bucket, $uri = '') {
541 $rest = new S3Request('GET', $bucket, $uri);
542 $rest->setParameter('acl', null);
543 $rest = $rest->getResponse();
544 if ($rest->error === false && $rest->code !== 200)
545 $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
546 if ($rest->error !== false) {
547 trigger_error(sprintf("S3::getAccessControlPolicy({$bucket}, {$uri}): [%s] %s",
548 $rest->error['code'], $rest->error['message']), E_USER_WARNING);
549 return false;
550 }
551
552 $acp = array();
553 if (isset($rest->body->Owner, $rest->body->Owner->ID, $rest->body->Owner->DisplayName)) {
554 $acp['owner'] = array(
555 'id' => (string)$rest->body->Owner->ID, 'name' => (string)$rest->body->Owner->DisplayName
556 );
557 }
558 if (isset($rest->body->AccessControlList)) {
559 $acp['acl'] = array();
560 foreach ($rest->body->AccessControlList->Grant as $grant) {
561 foreach ($grant->Grantee as $grantee) {
562 if (isset($grantee->ID, $grantee->DisplayName)) // CanonicalUser
563 $acp['acl'][] = array(
564 'type' => 'CanonicalUser',
565 'id' => (string)$grantee->ID,
566 'name' => (string)$grantee->DisplayName,
567 'permission' => (string)$grant->Permission
568 );
569 elseif (isset($grantee->EmailAddress)) // AmazonCustomerByEmail
570 $acp['acl'][] = array(
571 'type' => 'AmazonCustomerByEmail',
572 'email' => (string)$grantee->EmailAddress,
573 'permission' => (string)$grant->Permission
574 );
575 elseif (isset($grantee->URI)) // Group
576 $acp['acl'][] = array(
577 'type' => 'Group',
578 'uri' => (string)$grantee->URI,
579 'permission' => (string)$grant->Permission
580 );
581 else continue;
582 }
583 }
584 }
585 return $acp;
586 }
587
588
589 /**
590 * Delete an object
591 *
592 * @param string $bucket Bucket name
593 * @param string $uri Object URI
594 * @return mixed
595 */
596 public static function deleteObject($bucket = '', $uri = '') {
597 $rest = new S3Request('DELETE', $bucket, $uri);
598 $rest = $rest->getResponse();
599 if ($rest->error === false && $rest->code !== 204)
600 $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status');
601 if ($rest->error !== false) {
602 trigger_error(sprintf("S3::deleteObject(): [%s] %s", $rest->error['code'], $rest->error['message']), E_USER_WARNING);
603 return false;
604 }
605 return true;
606 }
607
608
609 /**
610 * Get MIME type for file
611 *
612 * @internal Used to get mime types
613 * @param string &$file File path
614 * @return string
615 */
616 public static function __getMimeType(&$file) {
617 $type = false;
618 // Fileinfo documentation says fileinfo_open() will use the
619 // MAGIC env var for the magic file
620 if (extension_loaded('fileinfo') && isset($_ENV['MAGIC']) &&
621 ($finfo = finfo_open(FILEINFO_MIME, $_ENV['MAGIC'])) !== false) {
622 if (($type = finfo_file($finfo, $file)) !== false) {
623 // Remove the charset and grab the last content-type
624 $type = explode(' ', str_replace('; charset=', ';charset=', $type));
625 $type = array_pop($type);
626 $type = explode(';', $type);
627 $type = array_shift($type);
628 }
629 finfo_close($finfo);
630
631 // If anyone is still using mime_content_type()
632 } elseif (function_exists('mime_content_type'))
633 $type = mime_content_type($file);
634
635 if ($type !== false && strlen($type) > 0) return $type;
636
637 // Otherwise do it the old fashioned way
638 static $exts = array(
639 'jpg' => 'image/jpeg', 'gif' => 'image/gif', 'png' => 'image/png',
640 'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'ico' => 'image/x-icon',
641 'swf' => 'application/x-shockwave-flash', 'pdf' => 'application/pdf',
642 'zip' => 'application/zip', 'gz' => 'application/x-gzip',
643 'tar' => 'application/x-tar', 'bz' => 'application/x-bzip',
644 'bz2' => 'application/x-bzip2', 'txt' => 'text/plain',
645 'asc' => 'text/plain', 'htm' => 'text/html', 'html' => 'text/html',
646 'xml' => 'text/xml', 'xsl' => 'application/xsl+xml',
647 'ogg' => 'application/ogg', 'mp3' => 'audio/mpeg', 'wav' => 'audio/x-wav',
648 'avi' => 'video/x-msvideo', 'mpg' => 'video/mpeg', 'mpeg' => 'video/mpeg',
649 'mov' => 'video/quicktime', 'flv' => 'video/x-flv', 'php' => 'text/x-php'
650 );
651 $ext = strToLower(pathInfo($file, PATHINFO_EXTENSION));
652 return isset($exts[$ext]) ? $exts[$ext] : 'application/octet-stream';
653 }
654
655
656 /**
657 * Generate the auth string: "AWS AccessKey:Signature"
658 *
659 * This uses the hash extension if loaded
660 *
661 * @internal Signs the request
662 * @param string $string String to sign
663 * @return string
664 */
665 public static function __getSignature($string) {
666 return 'AWS '.self::$__accessKey.':'.base64_encode(extension_loaded('hash') ?
667 hash_hmac('sha1', $string, self::$__secretKey, true) : pack('H*', sha1(
668 (str_pad(self::$__secretKey, 64, chr(0x00)) ^ (str_repeat(chr(0x5c), 64))) .
669 pack('H*', sha1((str_pad(self::$__secretKey, 64, chr(0x00)) ^
670 (str_repeat(chr(0x36), 64))) . $string)))));
671 }
672
673
674 }
675
676 final class S3Request {
677 private $verb, $bucket, $uri, $resource = '', $parameters = array(),
678 $amzHeaders = array(), $headers = array(
679 'Host' => '', 'Date' => '', 'Content-MD5' => '', 'Content-Type' => ''
680 );
681 public $fp = false, $size = 0, $data = false, $response;
682
683
684 /**
685 * Constructor
686 *
687 * @param string $verb Verb
688 * @param string $bucket Bucket name
689 * @param string $uri Object URI
690 * @return mixed
691 */
692 function __construct($verb, $bucket = '', $uri = '') {
693 $this->verb = $verb;
694 $this->bucket = strtolower($bucket);
695 $this->uri = $uri !== '' ? '/'.$uri : '/';
696
697 if ($this->bucket !== '') {
698 $this->bucket = explode('/', $this->bucket);
699 $this->resource = '/'.$this->bucket[0].$this->uri;
700 $this->headers['Host'] = $this->bucket[0].'.s3.amazonaws.com';
701 $this->bucket = implode('/', $this->bucket);
702 } else {
703 $this->headers['Host'] = 's3.amazonaws.com';
704 if (strlen($this->uri) > 1)
705 $this->resource = '/'.$this->bucket.$this->uri;
706 else $this->resource = $this->uri;
707 }
708 $this->headers['Date'] = gmdate('D, d M Y H:i:s T');
709
710 $this->response = new STDClass;
711 $this->response->error = false;
712 }
713
714
715 /**
716 * Set request parameter
717 *
718 * @param string $key Key
719 * @param string $value Value
720 * @return void
721 */
722 public function setParameter($key, $value) {
723 $this->parameters[$key] = $value;
724 }
725
726
727 /**
728 * Set request header
729 *
730 * @param string $key Key
731 * @param string $value Value
732 * @return void
733 */
734 public function setHeader($key, $value) {
735 $this->headers[$key] = $value;
736 }
737
738
739 /**
740 * Set x-amz-meta-* header
741 *
742 * @param string $key Key
743 * @param string $value Value
744 * @return void
745 */
746 public function setAmzHeader($key, $value) {
747 $this->amzHeaders[$key] = $value;
748 }
749
750
751 /**
752 * Get the S3 response
753 *
754 * @return object | false
755 */
756 public function getResponse() {
757 $query = '';
758 if (sizeof($this->parameters) > 0) {
759 $query = substr($this->uri, -1) !== '?' ? '?' : '&';
760 foreach ($this->parameters as $var => $value)
761 if ($value == null || $value == '') $query .= $var.'&';
762 else $query .= $var.'='.$value.'&';
763 $query = substr($query, 0, -1);
764 $this->uri .= $query;
765 if (isset($this->parameters['acl']) || !isset($this->parameters['logging']))
766 $this->resource .= $query;
767 }
768 $url = (extension_loaded('openssl')?'https://':'http://').$this->headers['Host'].$this->uri;
769 //var_dump($this->bucket, $this->uri, $this->resource, $url);
770
771 // Basic setup
772 $curl = curl_init();
773 curl_setopt($curl, CURLOPT_USERAGENT, 'S3/php');
774 curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
775 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
776 curl_setopt($curl, CURLOPT_URL, $url);
777
778 // Headers
779 $headers = array(); $amz = array();
780 foreach ($this->amzHeaders as $header => $value)
781 if (strlen($value) > 0) $headers[] = $header.': '.$value;
782 foreach ($this->headers as $header => $value)
783 if (strlen($value) > 0) $headers[] = $header.': '.$value;
784 foreach ($this->amzHeaders as $header => $value)
785 if (strlen($value) > 0) $amz[] = strToLower($header).':'.$value;
786 $amz = (sizeof($amz) > 0) ? "\n".implode("\n", $amz) : '';
787
788 // Authorization string
789 $headers[] = 'Authorization: ' . S3::__getSignature(
790 $this->verb."\n".
791 $this->headers['Content-MD5']."\n".
792 $this->headers['Content-Type']."\n".
793 $this->headers['Date'].$amz."\n".$this->resource
794 );
795
796 curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
797 curl_setopt($curl, CURLOPT_HEADER, false);
798 curl_setopt($curl, CURLOPT_RETURNTRANSFER, false);
799 curl_setopt($curl, CURLOPT_WRITEFUNCTION, array(&$this, '__responseWriteCallback'));
800 curl_setopt($curl, CURLOPT_HEADERFUNCTION, array(&$this, '__responseHeaderCallback'));
801
802 // Request types
803 switch ($this->verb) {
804 case 'GET': break;
805 case 'PUT':
806 if ($this->fp !== false) {
807 curl_setopt($curl, CURLOPT_PUT, true);
808 curl_setopt($curl, CURLOPT_INFILE, $this->fp);
809 if ($this->size > 0)
810 curl_setopt($curl, CURLOPT_INFILESIZE, $this->size);
811 } elseif ($this->data !== false) {
812 curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'PUT');
813 curl_setopt($curl, CURLOPT_POSTFIELDS, $this->data);
814 if ($this->size > 0)
815 curl_setopt($curl, CURLOPT_BUFFERSIZE, $this->size);
816 } else
817 curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'PUT');
818 break;
819 case 'HEAD':
820 curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'HEAD');
821 curl_setopt($curl, CURLOPT_NOBODY, true);
822 break;
823 case 'DELETE':
824 curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'DELETE');
825 break;
826 default: break;
827 }
828
829 // Execute, grab errors
830 if (curl_exec($curl))
831 $this->response->code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
832 else
833 $this->response->error = array(
834 'code' => curl_errno($curl),
835 'message' => curl_error($curl),
836 'resource' => $this->resource
837 );
838
839 @curl_close($curl);
840
841 // Parse body into XML
842 if ($this->response->error === false && isset($this->response->headers['type']) &&
843 $this->response->headers['type'] == 'application/xml' && isset($this->response->body)) {
844 $this->response->body = simplexml_load_string($this->response->body);
845
846 // Grab S3 errors
847 if (!in_array($this->response->code, array(200, 204)) &&
848 isset($this->response->body->Code, $this->response->body->Message)) {
849 $this->response->error = array(
850 'code' => (string)$this->response->body->Code,
851 'message' => (string)$this->response->body->Message
852 );
853 if (isset($this->response->body->Resource))
854 $this->response->error['resource'] = (string)$this->response->body->Resource;
855 unset($this->response->body);
856 }
857 }
858
859 // Clean up file resources
860 if ($this->fp !== false && is_resource($this->fp)) fclose($this->fp);
861
862 return $this->response;
863 }
864
865
866 /**
867 * CURL write callback
868 *
869 * @param resource &$curl CURL resource
870 * @param string &$data Data
871 * @return integer
872 */
873 private function __responseWriteCallback(&$curl, &$data) {
874 if ($this->response->code == 200 && $this->fp !== false)
875 return fwrite($this->fp, $data);
876 else
877 $this->response->body .= $data;
878 return strlen($data);
879 }
880
881
882 /**
883 * CURL header callback
884 *
885 * @param resource &$curl CURL resource
886 * @param string &$data Data
887 * @return integer
888 */
889 private function __responseHeaderCallback(&$curl, &$data) {
890 if (($strlen = strlen($data)) <= 2) return $strlen;
891 if (substr($data, 0, 4) == 'HTTP')
892 $this->response->code = (int)substr($data, 9, 3);
893 else {
894 list($header, $value) = explode(': ', trim($data));
895 if ($header == 'Last-Modified')
896 $this->response->headers['time'] = strtotime($value);
897 elseif ($header == 'Content-Length')
898 $this->response->headers['size'] = (int)$value;
899 elseif ($header == 'Content-Type')
900 $this->response->headers['type'] = $value;
901 elseif ($header == 'ETag')
902 $this->response->headers['hash'] = substr($value, 1, -1);
903 elseif (preg_match('/^x-amz-meta-.*$/', $header))
904 $this->response->headers[$header] = is_numeric($value) ? (int)$value : $value;
905 }
906 return $strlen;
907 }
908
909 }
910 ?>