* 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 (
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 );
* 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 {
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;
}
/**
$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 );
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 ) {
* 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
$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 ) {
}
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?
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?
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
*
protected $backend;
protected $container; //
protected $dir; // string storage directory
+ protected $suffixStart; // integer
const PAGE_SIZE = 5000; // file listing buffer size
*
* @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() {