Browse Source

fix: Cannot open certain zip files, #2467

Feng Hao 7 years ago
parent
commit
9faebd9c52

+ 3 - 0
Zip/include/Poco/Zip/ZipUtil.h

@@ -54,6 +54,9 @@ public:
 	static void sync(std::istream& in);
 		/// Searches the next valid header in the input stream, stops right before it
 
+	static void syncDataDescriptor(std::istream& in, bool force64);
+		/// Searches the next data descriptor
+
 	static void verifyZipEntryFileName(const std::string& zipPath);
 		/// Verifies that the name of the ZipEntry is a valid path
 

+ 1 - 1
Zip/src/SkipCallback.cpp

@@ -37,7 +37,7 @@ bool SkipCallback::handleZipEntry(std::istream& zipStream, const ZipLocalFileHea
 	if (!hdr.searchCRCAndSizesAfterData())
 		zipStream.seekg(hdr.getCompressedSize(), std::ios_base::cur);
 	else
-		ZipUtil::sync(zipStream);
+		ZipUtil::syncDataDescriptor(zipStream, hdr.needsZip64());
 	if (!zipStream.good()) throw Poco::IOException("Failed to seek on input stream");
 	return true;
 }

+ 63 - 0
Zip/src/ZipUtil.cpp

@@ -160,6 +160,69 @@ void ZipUtil::sync(std::istream& in)
 	}
 }
 
+void ZipUtil::syncDataDescriptor(std::istream & in, bool force64)
+{
+	std::streampos start = in.tellg();
+	const int eof = std::char_traits<char>::eof();
+
+	int c = in.get();
+	do
+	{
+		while (c != eof && c != ZipDataInfo::HEADER[0]) { c = in.get(); }
+
+		if (c == eof) return;
+
+		bool match = true;
+		for (int i = 1; i < 4 && match; i++)
+		{
+			c = in.get();
+			if (c != ZipDataInfo::HEADER[i]) match = false;
+		}
+
+		if (match)
+		{
+			std::streampos end = in.tellg();
+
+			if (force64)
+			{
+				ZipDataInfo64 nfo(in, true);
+				if (nfo.isValid())
+				{
+					if (end - start == nfo.getCompressedSize() + 4)
+					{
+						in.seekg(-static_cast<int>(ZipDataInfo64::getFullHeaderSize()), std::ios::cur);
+						if (!in.good()) throw Poco::IOException("Failed to seek on input stream");
+						break;
+					}
+					else
+					{
+						in.seekg(-static_cast<int>(ZipDataInfo64::getFullHeaderSize()) + 4, std::ios::cur);
+						if (!in.good()) throw Poco::IOException("Failed to seek on input stream");
+					}
+				}
+			}
+			else
+			{
+				ZipDataInfo nfo(in, true);
+				if (nfo.isValid())
+				{
+					if (end - start == nfo.getCompressedSize() + 4)
+					{
+						in.seekg(-static_cast<int>(ZipDataInfo::getFullHeaderSize()), std::ios::cur);
+						if (!in.good()) throw Poco::IOException("Failed to seek on input stream");
+						break;
+					}
+					else
+					{
+						in.seekg(-static_cast<int>(ZipDataInfo::getFullHeaderSize()) + 4, std::ios::cur);
+						if (!in.good()) throw Poco::IOException("Failed to seek on input stream");
+					}
+				}
+			}
+		}
+	} while (c != eof);
+}
+
 
 void ZipUtil::verifyZipEntryFileName(const std::string& fn)
 {

BIN
Zip/testsuite/data/encapsulated.zip


+ 29 - 0
Zip/testsuite/src/ZipTest.cpp

@@ -65,6 +65,34 @@ void ZipTest::testSkipSingleFile()
 	const std::string& POCO_UNUSED fileName = hdr.getFileName();
 }
 
+void ZipTest::testCrcAndSizeAfterDataEncapsulated()
+{
+	// touch empty.txt
+	// zip -fd foo.zip empty.txt
+	// zip -fd encapsulated.zip foo.zip
+	std::string testFile = getTestFile("data", "encapsulated.zip");
+	Poco::FileInputStream inp(testFile);
+	assertTrue(inp.good());
+
+	ZipArchive arch(inp);
+	ZipArchive::FileHeaders::const_iterator it = arch.findHeader("foo.zip");
+	assertTrue(it != arch.headerEnd());
+	inp.clear(); // inp eof(), should clear
+
+	ZipInputStream zipin(inp, it->second);
+	std::ostringstream out(std::ios::binary);
+	Poco::StreamCopier::copyStream(zipin, out);
+
+	std::string result = out.str();
+	// sub zip
+	std::istringstream istr(result);
+	ZipArchive subArch(istr);
+	it = subArch.findHeader("empty.txt");
+	assertTrue(it != subArch.headerEnd());
+	assertTrue(it->second.getCompressedSize() == 0);
+	assertTrue(it->second.getUncompressedSize() == 0);
+}
+
 
 void ZipTest::testDecompressSingleFile()
 {
@@ -325,6 +353,7 @@ CppUnit::Test* ZipTest::suite()
 	CppUnit_addTest(pSuite, ZipTest, testDecompressFlatVuln);
 	CppUnit_addTest(pSuite, ZipTest, testCrcAndSizeAfterData);
 	CppUnit_addTest(pSuite, ZipTest, testCrcAndSizeAfterDataWithArchive);
+	CppUnit_addTest(pSuite, ZipTest, testCrcAndSizeAfterDataEncapsulated);
 	CppUnit_addTest(pSuite, ZipTest, testDecompressZip64);
 	CppUnit_addTest(pSuite, ZipTest, testValidPath);
 

+ 1 - 0
Zip/testsuite/src/ZipTest.h

@@ -34,6 +34,7 @@ public:
 	void testDecompressFlatVuln();
 	void testCrcAndSizeAfterData();
 	void testCrcAndSizeAfterDataWithArchive();
+	void testCrcAndSizeAfterDataEncapsulated();
 
 	static const Poco::UInt64 KB = 1024;
 	static const Poco::UInt64 MB = 1024*KB;