Index: trunk/phase3/includes/filerepo/backend/FSFileBackend.php |
— | — | @@ -514,7 +514,8 @@ |
515 | 515 | * @param $dir string |
516 | 516 | */ |
517 | 517 | public function __construct( $dir ) { |
518 | | - $this->suffixStart = strlen( realpath( $dir ) ) + 1; // size of "path/to/dir/" |
| 518 | + $dir = realpath( $dir ); // normalize |
| 519 | + $this->suffixStart = strlen( $dir ) + 1; // size of "path/to/dir/" |
519 | 520 | try { |
520 | 521 | $flags = FilesystemIterator::CURRENT_AS_FILEINFO | FilesystemIterator::SKIP_DOTS; |
521 | 522 | $this->iter = new RecursiveIteratorIterator( |
Index: trunk/phase3/includes/filerepo/backend/SwiftFileBackend.php |
— | — | @@ -15,8 +15,6 @@ |
16 | 16 | * which is available at https://github.com/rackspace/php-cloudfiles. |
17 | 17 | * All of the library classes must be registed in $wgAutoloadClasses. |
18 | 18 | * |
19 | | - * @TODO: handle 'latest' param as "X-Newest: true". |
20 | | - * |
21 | 19 | * @ingroup FileBackend |
22 | 20 | */ |
23 | 21 | class SwiftFileBackend extends FileBackend { |
— | — | @@ -378,7 +376,7 @@ |
379 | 377 | protected function doSecureInternal( $fullCont, $dir, array $params ) { |
380 | 378 | $status = Status::newGood(); |
381 | 379 | // @TODO: restrict container from $this->swiftProxyUser |
382 | | - return $status; // badgers? We don't need no steenking badgers! |
| 380 | + return $status; |
383 | 381 | } |
384 | 382 | |
385 | 383 | /** |
— | — | @@ -398,6 +396,7 @@ |
399 | 397 | $stat = false; |
400 | 398 | try { |
401 | 399 | $container = $conn->get_container( $srcCont ); |
| 400 | + // @TODO: handle 'latest' param as "X-Newest: true" |
402 | 401 | $obj = $container->get_object( $srcRel ); |
403 | 402 | // Convert "Tue, 03 Jan 2012 22:01:04 GMT" to TS_MW |
404 | 403 | $date = DateTime::createFromFormat( 'D, d F Y G:i:s e', $obj->last_modified ); |
— | — | @@ -440,7 +439,7 @@ |
441 | 440 | try { |
442 | 441 | $container = $conn->get_container( $srcCont ); |
443 | 442 | $obj = $container->get_object( $srcRel ); |
444 | | - $data = $obj->read(); |
| 443 | + $data = $obj->read( $this->headersFromParams( $params ) ); |
445 | 444 | } catch ( NoSuchContainerException $e ) { |
446 | 445 | } catch ( NoSuchObjectException $e ) { |
447 | 446 | } catch ( InvalidResponseException $e ) { |
— | — | @@ -462,7 +461,7 @@ |
463 | 462 | * Do not call this function outside of SwiftFileIterator |
464 | 463 | * |
465 | 464 | * @param $fullCont string Resolved container name |
466 | | - * @param $dir string Resolved storage directory |
| 465 | + * @param $dir string Resolved storage directory with no trailing slash |
467 | 466 | * @param $after string Storage path of file to list items after |
468 | 467 | * @param $limit integer Max number of items to list |
469 | 468 | * @return Array |
— | — | @@ -476,7 +475,7 @@ |
477 | 476 | $files = array(); |
478 | 477 | try { |
479 | 478 | $container = $conn->get_container( $fullCont ); |
480 | | - $files = $container->list_objects( $limit, $after, $dir ); |
| 479 | + $files = $container->list_objects( $limit, $after, "{$dir}/" ); |
481 | 480 | } catch ( NoSuchContainerException $e ) { |
482 | 481 | } catch ( NoSuchObjectException $e ) { |
483 | 482 | } catch ( InvalidResponseException $e ) { |
— | — | @@ -534,8 +533,8 @@ |
535 | 534 | } |
536 | 535 | |
537 | 536 | try { |
538 | | - $output = fopen("php://output", "w"); |
539 | | - $obj->stream( $output ); |
| 537 | + $output = fopen( "php://output", "w" ); |
| 538 | + $obj->stream( $output, $this->headersFromParams( $params ) ); |
540 | 539 | } catch ( InvalidResponseException $e ) { |
541 | 540 | $status->fatal( 'backend-fail-connect', $this->name ); |
542 | 541 | } catch ( Exception $e ) { // some other exception? |
— | — | @@ -571,13 +570,17 @@ |
572 | 571 | try { |
573 | 572 | $cont = $conn->get_container( $srcCont ); |
574 | 573 | $obj = $cont->get_object( $srcRel ); |
575 | | - $obj->save_to_filename( $tmpFile->getPath() ); |
| 574 | + $handle = fopen( $tmpFile->getPath(), 'w' ); |
| 575 | + if ( $handle ) { |
| 576 | + $obj->stream( $handle, $this->headersFromParams( $params ) ); |
| 577 | + fclose( $handle ); |
| 578 | + } else { |
| 579 | + $tmpFile = null; // couldn't open temp file |
| 580 | + } |
576 | 581 | } catch ( NoSuchContainerException $e ) { |
577 | 582 | $tmpFile = null; |
578 | 583 | } catch ( NoSuchObjectException $e ) { |
579 | 584 | $tmpFile = null; |
580 | | - } catch ( IOException $e ) { |
581 | | - $tmpFile = null; |
582 | 585 | } catch ( InvalidResponseException $e ) { |
583 | 586 | $tmpFile = null; |
584 | 587 | } catch ( Exception $e ) { // some other exception? |
— | — | @@ -589,6 +592,22 @@ |
590 | 593 | } |
591 | 594 | |
592 | 595 | /** |
| 596 | + * Get headers to send to Swift when reading a file based |
| 597 | + * on a FileBackend params array, e.g. that of getLocalCopy(). |
| 598 | + * $params is currently only checked for a 'latest' flag. |
| 599 | + * |
| 600 | + * @param $params Array |
| 601 | + * @return Array |
| 602 | + */ |
| 603 | + protected function headersFromParams( array $params ) { |
| 604 | + $hdrs = array(); |
| 605 | + if ( !empty( $params['latest'] ) ) { |
| 606 | + $hdrs[] = 'X-Newest: true'; |
| 607 | + } |
| 608 | + return $hdrs; |
| 609 | + } |
| 610 | + |
| 611 | + /** |
593 | 612 | * Get a connection to the swift proxy |
594 | 613 | * |
595 | 614 | * @return CF_Connection|false |
— | — | @@ -644,6 +663,7 @@ |
645 | 664 | protected $backend; |
646 | 665 | protected $container; // |
647 | 666 | protected $dir; // string storage directory |
| 667 | + protected $suffixStart; // integer |
648 | 668 | |
649 | 669 | const PAGE_SIZE = 5000; // file listing buffer size |
650 | 670 | |
— | — | @@ -652,16 +672,20 @@ |
653 | 673 | * |
654 | 674 | * @param $backend SwiftFileBackend |
655 | 675 | * @param $fullCont string Resolved container name |
656 | | - * @param $dir string Resolved relateive path |
| 676 | + * @param $dir string Resolved relative directory |
657 | 677 | */ |
658 | 678 | public function __construct( SwiftFileBackend $backend, $fullCont, $dir ) { |
| 679 | + $this->backend = $backend; |
659 | 680 | $this->container = $fullCont; |
660 | 681 | $this->dir = $dir; |
661 | | - $this->backend = $backend; |
| 682 | + if ( substr( $this->dir, -1 ) === '/' ) { |
| 683 | + $this->dir = substr( $this->dir, 0, -1 ); // remove trailing slash |
| 684 | + } |
| 685 | + $this->suffixStart = strlen( $dir ) + 1; // size of "path/to/dir/" |
662 | 686 | } |
663 | 687 | |
664 | 688 | public function current() { |
665 | | - return current( $this->bufferIter ); |
| 689 | + return substr( current( $this->bufferIter ), $this->suffixStart ); |
666 | 690 | } |
667 | 691 | |
668 | 692 | public function key() { |
Index: trunk/phase3/includes/filerepo/backend/FileBackend.php |
— | — | @@ -1226,12 +1226,14 @@ |
1227 | 1227 | * Null is returned if the path involves directory traversal. |
1228 | 1228 | * Traversal is insecure for FS backends and broken for others. |
1229 | 1229 | * |
1230 | | - * @param $path string |
| 1230 | + * @param $path string Storage path relative to a container |
1231 | 1231 | * @return string|null |
1232 | 1232 | */ |
1233 | | - final protected static function normalizeStoragePath( $path ) { |
| 1233 | + final protected static function normalizeContainerPath( $path ) { |
1234 | 1234 | // Normalize directory separators |
1235 | 1235 | $path = strtr( $path, '\\', '/' ); |
| 1236 | + // Collapse consecutive directory separators |
| 1237 | + $path = preg_replace( '![/]{2,}!', '/', $path ); |
1236 | 1238 | // Use the same traversal protection as Title::secureAndSplit() |
1237 | 1239 | if ( strpos( $path, '.' ) !== false ) { |
1238 | 1240 | if ( |
— | — | @@ -1264,7 +1266,7 @@ |
1265 | 1267 | final protected function resolveStoragePath( $storagePath ) { |
1266 | 1268 | list( $backend, $container, $relPath ) = self::splitStoragePath( $storagePath ); |
1267 | 1269 | if ( $backend === $this->name ) { // must be for this backend |
1268 | | - $relPath = self::normalizeStoragePath( $relPath ); |
| 1270 | + $relPath = self::normalizeContainerPath( $relPath ); |
1269 | 1271 | if ( $relPath !== null ) { |
1270 | 1272 | // Get shard for the normalized path if this container is sharded |
1271 | 1273 | $cShard = $this->getContainerShard( $container, $relPath ); |