Просмотр исходного кода

Merge tag '3.1.7' into develop

Tagging hotfix 3.1.7 3.1.7
wapplay 7 лет назад
Родитель
Сommit
04a92e7904
2 измененных файлов с 157 добавлено и 9 удалено
  1. 53 9
      src/PhpZip/Stream/ZipInputStream.php
  2. 104 0
      tests/PhpZip/Issue24Test.php

+ 53 - 9
src/PhpZip/Stream/ZipInputStream.php

@@ -144,8 +144,14 @@ class ZipInputStream implements ZipInputStreamInterface
                 );
             }
             // .ZIP file comment       (variable size)
-            if (0 < $data['commentLength']) {
-                $comment = fread($this->in, $data['commentLength']);
+            if ($data['commentLength'] > 0) {
+                $comment = '';
+                $offset = 0;
+                while ($offset < $data['commentLength']) {
+                    $read = min(8192 /* chunk size */, $data['commentLength'] - $offset);
+                    $comment .= fread($this->in, $read);
+                    $offset += $read;
+                }
             }
             $this->preamble = $endOfCentralDirRecordPos;
             $this->postamble = $size - ftell($this->in);
@@ -314,7 +320,13 @@ class ZipInputStream implements ZipInputStreamInterface
 //        $utf8 = ($data['gpbf'] & ZipEntry::GPBF_UTF8) !== 0;
 
         // See appendix D of PKWARE's ZIP File Format Specification.
-        $name = fread($this->in, $data['fileLength']);
+        $name = '';
+        $offset = 0;
+        while ($offset < $data['fileLength']) {
+            $read = min(8192 /* chunk size */, $data['fileLength'] - $offset);
+            $name .= fread($this->in, $read);
+            $offset += $read;
+        }
 
         $entry = new ZipSourceEntry($this);
         $entry->setName($name);
@@ -329,10 +341,24 @@ class ZipInputStream implements ZipInputStreamInterface
         $entry->setExternalAttributes($data['rawExternalAttributes']);
         $entry->setOffset($data['lfhOff']); // must be unmapped!
         if ($data['extraLength'] > 0) {
-            $entry->setExtra(fread($this->in, $data['extraLength']));
+            $extra = '';
+            $offset = 0;
+            while ($offset < $data['extraLength']) {
+                $read = min(8192 /* chunk size */, $data['extraLength'] - $offset);
+                $extra .= fread($this->in, $read);
+                $offset += $read;
+            }
+            $entry->setExtra($extra);
         }
         if ($data['commentLength'] > 0) {
-            $entry->setComment(fread($this->in, $data['commentLength']));
+            $comment = '';
+            $offset = 0;
+            while ($offset < $data['commentLength']) {
+                $read = min(8192 /* chunk size */, $data['commentLength'] - $offset);
+                $comment .= fread($this->in, $read);
+                $offset += $read;
+            }
+            $entry->setComment($comment);
         }
         return $entry;
     }
@@ -382,10 +408,14 @@ class ZipInputStream implements ZipInputStreamInterface
         // Get raw entry content
         $compressedSize = $entry->getCompressedSize();
         $compressedSize = PHP_INT_SIZE === 4 ? sprintf('%u', $compressedSize) : $compressedSize;
+        $content = '';
         if ($compressedSize > 0) {
-            $content = fread($this->in, $compressedSize);
-        } else {
-            $content = '';
+            $offset = 0;
+            while ($offset < $compressedSize) {
+                $read = min(8192 /* chunk size */, $compressedSize - $offset);
+                $content .= fread($this->in, $read);
+                $offset += $read;
+            }
         }
 
         $skipCheckCrc = false;
@@ -453,6 +483,14 @@ class ZipInputStream implements ZipInputStreamInterface
                 throw new ZipUnsupportMethodException($entry->getName() .
                     " (compression method " . $method . " is not supported)");
         }
+
+        if ($content === false) {
+            throw new ZipException(sprintf(
+                'Failed to get the contents of the zip entry "%s"',
+                $entry->getName()
+            ));
+        }
+
         if (!$skipCheckCrc) {
             $localCrc = crc32($content);
             $localCrc = PHP_INT_SIZE === 4 ? sprintf('%u', $localCrc) : $localCrc;
@@ -498,7 +536,13 @@ class ZipInputStream implements ZipInputStreamInterface
         if ($sourceExtraLength > 0) {
             // read Local File Header extra fields
             fseek($this->in, $pos + ZipEntry::LOCAL_FILE_HEADER_MIN_LEN + $nameLength, SEEK_SET);
-            $extra = fread($this->in, $sourceExtraLength);
+            $extra = '';
+            $offset = 0;
+            while ($offset < $sourceExtraLength) {
+                $read = min(8192 /* chunk size */, $sourceExtraLength - $offset);
+                $extra .= fread($this->in, $read);
+                $offset += $read;
+            }
             $extraFieldsCollection = ExtraFieldsFactory::createExtraFieldCollections($extra, $entry);
             if (isset($extraFieldsCollection[ApkAlignmentExtraField::getHeaderId()]) && $this->zipModel->isZipAlign()) {
                 unset($extraFieldsCollection[ApkAlignmentExtraField::getHeaderId()]);

+ 104 - 0
tests/PhpZip/Issue24Test.php

@@ -0,0 +1,104 @@
+<?php
+
+namespace PhpZip;
+
+use PhpZip\Exception\ZipException;
+use PhpZip\Util\CryptoUtil;
+
+class Issue24Test extends ZipTestCase
+{
+    /**
+     * This method is called before the first test of this test class is run.
+     */
+    public static function setUpBeforeClass()
+    {
+        stream_wrapper_register("dummyfs", DummyFileSystemStream::class);
+    }
+
+    /**
+     * @throws ZipException
+     */
+    public function testDummyFS()
+    {
+        $fileContents = str_repeat(base64_encode(CryptoUtil::randomBytes(12000)), 100);
+
+        // create zip file
+        $zip = new ZipFile();
+        $zip->addFromString(
+            'file.txt',
+            $fileContents,
+            ZipFile::METHOD_DEFLATED
+        );
+        $zip->saveAsFile($this->outputFilename);
+        $zip->close();
+
+        $this->assertCorrectZipArchive($this->outputFilename);
+
+        $stream = fopen('dummyfs://localhost/' . $this->outputFilename, 'rb');
+        $this->assertNotFalse($stream);
+        $zip->openFromStream($stream);
+        $this->assertEquals($zip->getListFiles(), ['file.txt']);
+        $this->assertEquals($zip['file.txt'], $fileContents);
+        $zip->close();
+    }
+}
+
+/**
+ * Try to load using dummy stream
+ */
+class DummyFileSystemStream
+{
+    /**
+     * @var resource
+     */
+    private $fp;
+
+    function stream_open($path, $mode, $options, &$opened_path)
+    {
+//        echo "DummyFileSystemStream->stream_open($path, $mode, $options)" . PHP_EOL;
+
+        $parsedUrl = parse_url($path);
+        $path = $parsedUrl['path'];
+        $this->fp = fopen($path, $mode);
+
+        return true;
+    }
+
+    function stream_read($count)
+    {
+//        echo "DummyFileSystemStream->stream_read($count)" . PHP_EOL;
+        $position = ftell($this->fp);
+
+//        echo "Loading chunk " . $position . " to " . ($position + $count - 1) . PHP_EOL;
+        $ret = fread($this->fp, $count);
+
+//        echo "String length: " . strlen($ret) . PHP_EOL;
+
+        return $ret;
+    }
+
+    function stream_tell()
+    {
+//        echo "DummyFileSystemStream->stream_tell()" . PHP_EOL;
+        return ftell($this->fp);
+    }
+
+    function stream_eof()
+    {
+//        echo "DummyFileSystemStream->stream_eof()" . PHP_EOL;
+        $isfeof = feof($this->fp);
+        return $isfeof;
+    }
+
+    function stream_seek($offset, $whence)
+    {
+//        echo "DummyFileSystemStream->stream_seek($offset, $whence)" . PHP_EOL;
+        fseek($this->fp, $offset, $whence);
+    }
+
+    function stream_stat()
+    {
+//        echo "DummyFileSystemStream->stream_stat()" . PHP_EOL;
+        return fstat($this->fp);
+    }
+}