From e69661c881f3bbab07fa44547bb9b9796e386135 Mon Sep 17 00:00:00 2001 From: Xavier De Cock Date: Tue, 22 Mar 2011 15:42:50 +0100 Subject: [PATCH] [FileByteStream] Ensure Stream is seekable before trying to seek through this should address bug #187 and allow embedding of url altough it's clearly not recommended --- .../Swift/ByteStream/FileByteStream.php | 63 ++++++++++++++++++- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/lib/classes/Swift/ByteStream/FileByteStream.php b/lib/classes/Swift/ByteStream/FileByteStream.php index 9494c35ab..367c6f977 100644 --- a/lib/classes/Swift/ByteStream/FileByteStream.php +++ b/lib/classes/Swift/ByteStream/FileByteStream.php @@ -37,6 +37,9 @@ class Swift_ByteStream_FileByteStream /** If magic_quotes_runtime is on, this will be true */ private $_quotes = false; + + /** If stream is seekable true/false, or null if not known */ + private $_seekable = null; /** * Create a new FileByteStream for $path. @@ -100,7 +103,7 @@ public function setReadPointer($byteOffset) { if (isset($this->_reader)) { - fseek($this->_reader, $byteOffset, SEEK_SET); + $this->_seekReadStreamToPosition($byteOffset); } $this->_offset = $byteOffset; } @@ -130,7 +133,10 @@ private function _getReadHandle() 'Unable to open file for reading [' . $this->_path . ']' ); } - fseek($this->_reader, $this->_offset, SEEK_SET); + if ($this->_offset <> 0) { + $this->_getReadStreamSeekableStatus(); + $this->_seekReadStreamToPosition($this->_offset); + } } return $this->_reader; } @@ -160,4 +166,57 @@ private function _resetReadHandle() } } + /** Check if ReadOnly Stream is seekable */ + private function _getReadStreamSeekableStatus() { + $metas = stream_get_meta_data($this->_reader); + $this->_seekable = $metas['seekable']; + } + + /** Streams in a readOnly stream ensuring copy if needed */ + private function _seekReadStreamToPosition($offset) { + if ($this->_seekable===null) { + $this->_getReadStreamSeekableStatus(); + } + if ($this->_seekable === false) { + $currentPos = ftell($this->_reader); + if ($currentPos<$offset) { + $toDiscard = $offset-$currentPos; + fread($this->_reader, $toDiscard); + return; + } + $this->_copyReadStream(); + } + fseek($this->_reader, $offset, SEEK_SET); + } + + /** Copy a readOnly Stream to ensure seekability */ + private function _copyReadStream() { + if ($tmpFile = fopen('php://temp/maxmemory:4096', 'w+b')) + { + /* We have opened a php:// Stream Should work without problem */ + } + elseif (function_exists('sys_get_temp_dir') && is_writable(sys_get_temp_dir()) && ($tmpFile = tmpfile())) + { + /* We have opened a tmpfile */ + } + else + { + throw new Swift_IoException('Unable to copy the file to make it seekable, sys_temp_dir is not writable, php://memory not available'); + } + $currentPos = ftell($this->_reader); + fclose($this->_reader); + $source = fopen($this->_path, 'rb'); + if (!$source) + { + throw new Swift_IoException('Unable to open file for copying [' . $this->_path . ']'); + } + fseek($tmpFile, 0, SEEK_SET); + while (!feof($source)) + { + fwrite($tmpFile, fread($source, 4096)); + } + fseek($tmpFile, $currentPos, SEEK_SET); + fclose($source); + $this->_reader = $tmpFile; + } }