* Collapse multiple directory separators in FileBackend::normalizeStoragePath()....
authorAaron Schulz <aaron@users.mediawiki.org>
Thu, 12 Jan 2012 22:01:02 +0000 (22:01 +0000)
committerAaron Schulz <aaron@users.mediawiki.org>
Thu, 12 Jan 2012 22:01:02 +0000 (22:01 +0000)
* Made SwiftFileBackend respect the 'latest' parameter of various functions via "X-Newest: true" header. Added to TODO in the doGetFileStat() function, as cloudfiles needs upstream changes for that.
* Fixed SwiftFileIterator to return paths relative to the given directory.
* Clean up for FSFileBackend trailing slash handling.

includes/filerepo/backend/FSFileBackend.php
includes/filerepo/backend/FileBackend.php
includes/filerepo/backend/SwiftFileBackend.php

index ccbfe48..4654070 100644 (file)
@@ -514,7 +514,8 @@ class FSFileIterator implements Iterator {
         * @param $dir string
         */
        public function __construct( $dir ) {
-               $this->suffixStart = strlen( realpath( $dir ) ) + 1; // size of "path/to/dir/"
+               $dir = realpath( $dir ); // normalize
+               $this->suffixStart = strlen( $dir ) + 1; // size of "path/to/dir/"
                try {
                        $flags = FilesystemIterator::CURRENT_AS_FILEINFO | FilesystemIterator::SKIP_DOTS;
                        $this->iter = new RecursiveIteratorIterator(
index d971098..76025c8 100644 (file)
@@ -1226,12 +1226,14 @@ abstract class FileBackend extends FileBackendBase {
         * Null is returned if the path involves directory traversal.
         * Traversal is insecure for FS backends and broken for others.
         *
-        * @param $path string
+        * @param $path string Storage path relative to a container
         * @return string|null
         */
-       final protected static function normalizeStoragePath( $path ) {
+       final protected static function normalizeContainerPath( $path ) {
                // Normalize directory separators
                $path = strtr( $path, '\\', '/' );
+               // Collapse consecutive directory separators
+               $path = preg_replace( '![/]{2,}!', '/', $path );
                // Use the same traversal protection as Title::secureAndSplit()
                if ( strpos( $path, '.' ) !== false ) {
                        if (
@@ -1264,7 +1266,7 @@ abstract class FileBackend extends FileBackendBase {
        final protected function resolveStoragePath( $storagePath ) {
                list( $backend, $container, $relPath ) = self::splitStoragePath( $storagePath );
                if ( $backend === $this->name ) { // must be for this backend
-                       $relPath = self::normalizeStoragePath( $relPath );
+                       $relPath = self::normalizeContainerPath( $relPath );
                        if ( $relPath !== null ) {
                                // Get shard for the normalized path if this container is sharded
                                $cShard = $this->getContainerShard( $container, $relPath );
index e9bbb4e..c630011 100644 (file)
@@ -15,8 +15,6 @@
  * which is available at https://github.com/rackspace/php-cloudfiles.
  * All of the library classes must be registed in $wgAutoloadClasses.
  *
- * @TODO: handle 'latest' param as "X-Newest: true".
- *
  * @ingroup FileBackend
  */
 class SwiftFileBackend extends FileBackend {
@@ -378,7 +376,7 @@ class SwiftFileBackend extends FileBackend {
        protected function doSecureInternal( $fullCont, $dir, array $params ) {
                $status = Status::newGood();
                // @TODO: restrict container from $this->swiftProxyUser
-               return $status; // badgers? We don't need no steenking badgers!
+               return $status;
        }
 
        /**
@@ -398,6 +396,7 @@ class SwiftFileBackend extends FileBackend {
                $stat = false;
                try {
                        $container = $conn->get_container( $srcCont );
+                       // @TODO: handle 'latest' param as "X-Newest: true"
                        $obj = $container->get_object( $srcRel );
                        // Convert "Tue, 03 Jan 2012 22:01:04 GMT" to TS_MW
                        $date = DateTime::createFromFormat( 'D, d F Y G:i:s e', $obj->last_modified );
@@ -440,7 +439,7 @@ class SwiftFileBackend extends FileBackend {
                try {
                        $container = $conn->get_container( $srcCont );
                        $obj = $container->get_object( $srcRel );
-                       $data = $obj->read();
+                       $data = $obj->read( $this->headersFromParams( $params ) );
                } catch ( NoSuchContainerException $e ) {
                } catch ( NoSuchObjectException $e ) {
                } catch ( InvalidResponseException $e ) {
@@ -462,7 +461,7 @@ class SwiftFileBackend extends FileBackend {
         * Do not call this function outside of SwiftFileIterator
         * 
         * @param $fullCont string Resolved container name
-        * @param $dir string Resolved storage directory
+        * @param $dir string Resolved storage directory with no trailing slash
         * @param $after string Storage path of file to list items after
         * @param $limit integer Max number of items to list
         * @return Array
@@ -476,7 +475,7 @@ class SwiftFileBackend extends FileBackend {
                $files = array();
                try {
                        $container = $conn->get_container( $fullCont );
-                       $files = $container->list_objects( $limit, $after, $dir );
+                       $files = $container->list_objects( $limit, $after, "{$dir}/" );
                } catch ( NoSuchContainerException $e ) {
                } catch ( NoSuchObjectException $e ) {
                } catch ( InvalidResponseException $e ) {
@@ -534,8 +533,8 @@ class SwiftFileBackend extends FileBackend {
                }
 
                try {
-                       $output = fopen("php://output", "w");
-                       $obj->stream( $output );
+                       $output = fopen( "php://output", "w" );
+                       $obj->stream( $output, $this->headersFromParams( $params ) );
                } catch ( InvalidResponseException $e ) {
                        $status->fatal( 'backend-fail-connect', $this->name );
                } catch ( Exception $e ) { // some other exception?
@@ -571,13 +570,17 @@ class SwiftFileBackend extends FileBackend {
                try {
                        $cont = $conn->get_container( $srcCont );
                        $obj = $cont->get_object( $srcRel );
-                       $obj->save_to_filename( $tmpFile->getPath() );
+                       $handle = fopen( $tmpFile->getPath(), 'w' );
+                       if ( $handle ) {
+                               $obj->stream( $handle, $this->headersFromParams( $params ) );
+                               fclose( $handle );
+                       } else {
+                               $tmpFile = null; // couldn't open temp file
+                       }
                } catch ( NoSuchContainerException $e ) {
                        $tmpFile = null;
                } catch ( NoSuchObjectException $e ) {
                        $tmpFile = null;
-               } catch ( IOException $e ) {
-                       $tmpFile = null;
                } catch ( InvalidResponseException $e ) {
                        $tmpFile = null;
                } catch ( Exception $e ) { // some other exception?
@@ -588,6 +591,22 @@ class SwiftFileBackend extends FileBackend {
                return $tmpFile;
        }
 
+       /**
+        * Get headers to send to Swift when reading a file based
+        * on a FileBackend params array, e.g. that of getLocalCopy(). 
+        * $params is currently only checked for a 'latest' flag.
+        * 
+        * @param $params Array
+        * @return Array 
+        */
+       protected function headersFromParams( array $params ) {
+               $hdrs = array();
+               if ( !empty( $params['latest'] ) ) {
+                       $hdrs[] = 'X-Newest: true';
+               }
+               return $hdrs;
+       }
+
        /**
         * Get a connection to the swift proxy
         *
@@ -644,6 +663,7 @@ class SwiftFileIterator implements Iterator {
        protected $backend; 
        protected $container; //
        protected $dir; // string storage directory
+       protected $suffixStart; // integer
 
        const PAGE_SIZE = 5000; // file listing buffer size
 
@@ -652,16 +672,20 @@ class SwiftFileIterator implements Iterator {
         * 
         * @param $backend SwiftFileBackend
         * @param $fullCont string Resolved container name
-        * @param $dir string Resolved relateive path
+        * @param $dir string Resolved relative directory
         */
        public function __construct( SwiftFileBackend $backend, $fullCont, $dir ) {
+               $this->backend = $backend;
                $this->container = $fullCont;
                $this->dir = $dir;
-               $this->backend = $backend;
+               if ( substr( $this->dir, -1 ) === '/' ) {
+                       $this->dir = substr( $this->dir, 0, -1 ); // remove trailing slash
+               }
+               $this->suffixStart = strlen( $dir ) + 1; // size of "path/to/dir/"
        }
 
        public function current() {
-               return current( $this->bufferIter );
+               return substr( current( $this->bufferIter ), $this->suffixStart );
        }
 
        public function key() {