Commit message: merge zip entry absolute path vulnerability fix (#1968) from develop
CVE: CVE-2017-1000472
CWE: CWE-22
Vulnerability description: The ZipCommon::isValidPath() function in Zip/src/ZipCommon.cpp in POCO C++ Libraries before 1.8 does not properly restrict the filename value in the ZIP header, which allows attackers to conduct absolute path traversal attacks during the ZIP decompression, and possibly create or overwrite arbitrary files, via a crafted ZIP file, related to a "file path injection vulnerability".

Project: poco
File: ZipTest.cpp

std::string ZipTest::getTestFile(const std::string& directory, const std::string& file)
{
	std::ostringstream ostr;
	ostr << directory << '/' << file;
	std::string validDir(ostr.str());
	Poco::Path pathPattern(validDir);
	if (Poco::File(pathPattern).exists())
	{
		return validDir;
	}

	ostr.str("");
	ostr << "/Zip/testsuite/" << directory << '/' << file;
	validDir = Poco::Environment::get("POCO_BASE") + ostr.str();
	pathPattern = validDir;

	if (!Poco::File(pathPattern).exists())
	{
		std::cout << "Can't find " << validDir << std::endl;
		throw Poco::NotFoundException("cannot locate directory containing valid Zip test files");
	}
	return validDir;
}

Project: poco
File: ZipTest.cpp

void ZipTest::testDecompressVuln()
{
	std::string testFile = getTestFile("data", "vuln.zip");
	std::ifstream inp(testFile.c_str(), std::ios::binary);
	assert(inp.good());
	Decompress dec(inp, Poco::Path::temp());
	dec.EError += Poco::Delegate<ZipTest, std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string> >(this, &ZipTest::onDecompressError);
	dec.decompressAllFiles();
	dec.EError -= Poco::Delegate<ZipTest, std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string> >(this, &ZipTest::onDecompressError);
	assert (_errCnt == 1);
	assert (dec.mapping().empty());
}

Project: poco
File: ZipTest.cpp

void ZipTest::testSkipSingleFile()
{
	std::string testFile = getTestFile("data", "test.zip");
	std::ifstream inp(testFile.c_str(), std::ios::binary);
	assert (inp.good());
	SkipCallback skip;
	ZipLocalFileHeader hdr(inp, false, skip);
	assert (ZipCommon::HS_FAT == hdr.getHostSystem());
	int major = hdr.getMajorVersionNumber();
	int POCO_UNUSED minor = hdr.getMinorVersionNumber();
	assert (major <= 2);
	std::size_t hdrSize = hdr.getHeaderSize();
	assert (hdrSize > 30);
	ZipCommon::CompressionMethod POCO_UNUSED cm = hdr.getCompressionMethod();
	assert (!hdr.isEncrypted());
	Poco::DateTime aDate = hdr.lastModifiedAt();
	Poco::UInt64 POCO_UNUSED cS = hdr.getCompressedSize();
	Poco::UInt64 POCO_UNUSED uS = hdr.getUncompressedSize();
	const std::string& POCO_UNUSED fileName = hdr.getFileName();
}

Project: poco
File: ZipTest.cpp

void ZipTest::verifyDataFile(const std::string& path, Poco::UInt64 size)
{
	std::ifstream in(path.c_str(), std::ios::binary);
	assert( ! in.fail() );
	Poco::Buffer<char> buffer1(MB);
	Poco::Buffer<char> buffer2(MB);
	for (int i = 0; size != 0; i++)
	{
		std::memset(buffer1.begin(), i, buffer1.size());
		std::memset(buffer2.begin(), 0, buffer2.size());
		Poco::UInt64 bytesToRead = std::min(size, static_cast<Poco::UInt64>(buffer2.size()));
		in.read(buffer2.begin(), bytesToRead);
		assert (!in.fail() );
		assert (std::memcmp(buffer1.begin(), buffer2.begin(), static_cast<std::size_t>(bytesToRead)) == 0);
		size -= bytesToRead;
	}
	char c;
	in.read(&c, 1);
	assert ( in.eof() );
}

Project: poco
File: Decompress.cpp

ZipArchive Decompress::decompressAllFiles()
{
	poco_assert (_mapping.empty());
	ZipArchive arch(_in, *this);
	return arch;
}

Project: poco
File: ZipStream.cpp

bool ZipInputStream::crcValid() const
{
	return _buf.crcValid();
}

Project: poco
File: ZipStream.cpp

ZipOutputStream::~ZipOutputStream()
{
}

Project: poco
File: CompressTest.cpp

void CompressTest::setUp()
{
}

Project: poco
File: ZipCommon.cpp

bool ZipCommon::isValidPath(const std::string& path)
{
	try
	{
		if (Path(path, Path::PATH_UNIX).isAbsolute() || Path(path, Path::PATH_WINDOWS).isAbsolute())
			return false;
	}
	catch (...)
	{
		return false;
	}

	if (path == "..")
		return false;
	if ((path.size() >= 3) && path.compare(0, 3, "../") == 0)
		return false;
	if ((path.size() >= 3) && path.compare(0, 3, "..\\") == 0)
		return false;
	if (path.find("/../") != std::string::npos)
		return false;
	if (path.find("\\..\\") != std::string::npos)
		return false;
	if (path.find("/..\\") != std::string::npos)
		return false;
	if (path.find("\\../") != std::string::npos)
		return false;
	if ((path.size() >= 2) && path.compare(0, 2, "~/") == 0)
		return false;

	return true;
}

Project: poco
File: ZipStream.cpp

int ZipStreamBuf::writeToDevice(const char* buffer, std::streamsize length)
{
	if (!_ptrOBuf) return 0; // directory entry
	if (length == 0)
		return 0;
	_bytesWritten += length;
	_ptrOBuf->write(buffer, length);
	_crc32.update(buffer, static_cast<unsigned int>(length));
	return static_cast<int>(length);
}

Project: poco
File: CompressTest.cpp

void CompressTest::tearDown()
{
}

Project: poco
File: ZipTest.cpp

void ZipTest::testDecompressFlat()
{
	std::string testFile = getTestFile("data", "test.zip");
	std::ifstream inp(testFile.c_str(), std::ios::binary);
	assert (inp.good());
	Decompress dec(inp, Poco::Path::temp(), true);
	dec.EError += Poco::Delegate<ZipTest, std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string> >(this, &ZipTest::onDecompressError);
	dec.decompressAllFiles();
	dec.EError -= Poco::Delegate<ZipTest, std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string> >(this, &ZipTest::onDecompressError);
	assert (_errCnt == 0);
	assert (!dec.mapping().empty());
}

Project: poco
File: ZipTest.cpp

void ZipTest::testDecompressSingleFileInDir()
{
	std::string testFile = getTestFile("data","test.zip");
	std::ifstream inp(testFile.c_str(), std::ios::binary);
	assert (inp.good());
	ZipArchive arch(inp);
	ZipArchive::FileHeaders::const_iterator it = arch.findHeader("testdir/testfile.txt");
	assert (it != arch.headerEnd());
	ZipInputStream zipin (inp, it->second);
	std::ostringstream out(std::ios::binary);
	Poco::StreamCopier::copyStream(zipin, out);
	assert(!out.str().empty());
}

Project: poco
File: ZipStream.cpp

ZipIOS::ZipIOS(std::ostream& ostr, ZipLocalFileHeader& fileEntry, bool reposition): _buf(ostr, fileEntry, reposition)
{
	poco_ios_init(&_buf);
}

Project: poco
File: CompressTest.cpp

CompressTest::CompressTest(const std::string& name): CppUnit::TestCase(name)
{
}

Project: poco
File: CompressTest.cpp

void CompressTest::testZip64()
{
	typedef std::map<std::string, Poco::UInt64> FileMap;
	std::cout << std::endl;
	FileMap files;
	files["data1.bin"] = static_cast<Poco::UInt64>(KB)*4096+1;
	files["data2.bin"] = static_cast<Poco::UInt64>(KB)*16;
	files["data3.bin"] = static_cast<Poco::UInt64>(KB)*4096-1;
	
	for(FileMap::const_iterator it = files.begin(); it != files.end(); it++)
	{
		std::cout << '\t' << "createDataFile(" << it->first << ", " << it->second << ");" << std::endl;
		createDataFile(it->first, it->second);
	}
	std::ofstream out(Poco::Path::temp() + "zip64.zip", std::ios::binary | std::ios::trunc);
	Compress c(out, true, true);
	for(FileMap::const_iterator it = files.begin(); it != files.end(); it++)
	{
		const std::string& path = it->first;
		std::cout << '\t' << "addFile(" << path <<  ");" << std::endl;
		c.addFile(path, path, ZipCommon::CM_STORE);
	}
	ZipArchive a(c.close());
	for(FileMap::const_iterator it = files.begin(); it != files.end(); it++)
	{
		const std::string& path = it->first;
		Poco::UInt64 size = it->second;
		ZipArchive::FileHeaders::const_iterator it2 = a.findHeader(path);
		assert (it2 != a.headerEnd());
		const Poco::Zip::ZipLocalFileHeader& file = it2->second;
		assert(file.getUncompressedSize() == size);
		assert(file.getCompressedSize() == size);
	}
	for (FileMap::const_iterator it = files.begin(); it != files.end(); it++)
	{
		Poco::File(it->first).remove();
	}
}

Project: poco
File: ZipTest.cpp

ZipTest::~ZipTest()
{
}

Project: poco
File: ZipStream.cpp

ZipIOS::~ZipIOS()
{
}

Project: poco
File: ZipTest.cpp

void ZipTest::testCrcAndSizeAfterDataWithArchive()
{
	std::string testFile = getTestFile("data", "data.zip");
	std::ifstream inp(testFile.c_str(), std::ios::binary);
	assert (inp.good());
	Poco::Zip::ZipArchive zip(inp);
	inp.clear();
	inp.seekg(0);
	Poco::Zip::ZipArchive::FileHeaders::const_iterator it = zip.headerBegin();
	for ( ; it!=zip.headerEnd(); ++it)
	{
		Poco::Zip::ZipInputStream zipis(inp,it->second);
		Poco::Path path(it->second.getFileName());
		if (path.isFile())
		{
			std::ofstream os(Poco::Path::temp() + "test.dat");
			Poco::StreamCopier::copyStream(zipis,os);
		}
	}
}

Project: poco
File: ZipStream.cpp

int ZipStreamBuf::readFromDevice(char* buffer, std::streamsize length)
{
	if (!_ptrBuf) return 0; // directory entry
	_ptrBuf->read(buffer, length);
	int cnt = static_cast<int>(_ptrBuf->gcount());
	if (cnt > 0)
	{
		_crc32.update(buffer, cnt);
	}
	else
	{
		if (_crc32.checksum() != _expectedCrc32)
		{
			if (_checkCRC)
				throw ZipException("CRC failure");
			else
			{
				// the CRC value is written directly after the data block
				// parse it directly from the input stream
				ZipDataInfo nfo(*_pIstr, false);
				// now push back the header to the stream, so that the ZipLocalFileHeader can read it
				Poco::Int32 size = static_cast<Poco::Int32>(nfo.getFullHeaderSize());
				_expectedCrc32 = nfo.getCRC32();
				_pIstr->seekg(-size, std::ios::cur);
				if (!_pIstr->good()) throw Poco::IOException("Failed to seek on input stream");
				if (!crcValid())
					throw ZipException("CRC failure");
			}
		}
	}
	return cnt;
}

Project: poco
File: ZipStream.cpp

ZipStreamBuf* ZipIOS::rdbuf()
{
	return &_buf;
}

Project: poco
File: CompressTest.cpp

void CompressTest::testSetZipComment()
{
	std::string comment("Testing...123...");
	std::ofstream out(Poco::Path::temp() + "comment.zip", std::ios::binary);
	Poco::Path theFile(ZipTest::getTestFile("data", "test.zip"));
	Compress c(out, true);
	c.addFile(theFile, theFile.getFileName());
	c.setZipComment(comment);
	ZipArchive a(c.close());
	assert(a.getZipComment() == comment);
}

Project: poco
File: ZipStream.cpp

ZipStreamBuf::ZipStreamBuf(std::istream& istr, const ZipLocalFileHeader& fileEntry, bool reposition):
	Poco::BufferedStreamBuf(STREAM_BUFFER_SIZE, std::ios::in),
	_pIstr(&istr),
	_pOstr(0),
	_ptrBuf(),
	_ptrOBuf(),
	_ptrHelper(),
	_ptrOHelper(),
	_crc32(Poco::Checksum::TYPE_CRC32),
	_expectedCrc32(0),
	_checkCRC(true),
	_bytesWritten(0),
	_pHeader(0)
{
	if (fileEntry.isDirectory())
		return;
	_expectedCrc32 = fileEntry.getCRC();
	std::streamoff start = fileEntry.getDataStartPos();
	std::streamoff end = fileEntry.getDataEndPos();
	_checkCRC = !fileEntry.searchCRCAndSizesAfterData();
	if (fileEntry.getCompressionMethod() == ZipCommon::CM_DEFLATE)
	{
		// Fake init bytes at beginning of stream
		std::string init = ZipUtil::fakeZLibInitString(fileEntry.getCompressionLevel());

		// Fake adler at end of stream: just some dummy value, not checked anway
		std::string crc(4, ' ');
		if (fileEntry.searchCRCAndSizesAfterData())
		{
			_ptrHelper = new AutoDetectInputStream(istr, init, crc, reposition, static_cast<Poco::UInt32>(start), fileEntry.needsZip64());
		}
		else
		{
			_ptrHelper = new PartialInputStream(istr, start, end, reposition, init, crc);
		}
		_ptrBuf = new Poco::InflatingInputStream(*_ptrHelper, Poco::InflatingStreamBuf::STREAM_ZIP);
	}
	else if (fileEntry.getCompressionMethod() == ZipCommon::CM_STORE)
	{
		if (fileEntry.searchCRCAndSizesAfterData())
		{
			_ptrBuf = new AutoDetectInputStream(istr, "", "", reposition, static_cast<Poco::UInt32>(start), fileEntry.needsZip64());
		}
		else
		{
			_ptrBuf = new PartialInputStream(istr, start, end, reposition);
		}
	}
	else throw Poco::NotImplementedException("Unsupported compression method");
}

Project: poco
File: Decompress.cpp

Decompress::~Decompress()
{
	try
	{
		EOk -= Poco::Delegate<Decompress, std::pair<const ZipLocalFileHeader, const Poco::Path> >(this, &Decompress::onOk);
	}
	catch (...)
	{
		poco_unexpected();
	}
}

Project: poco
File: ZipTest.cpp

void ZipTest::setUp()
{
	_errCnt = 0;
}

Project: poco
File: CompressTest.cpp

void CompressTest::testManipulatorDel()
{
	{
		std::ofstream out(Poco::Path::temp() + "appinf.zip", std::ios::binary);
		Poco::Path theFile(ZipTest::getTestFile("data", "test.zip"));
		Compress c(out, true);
		c.addFile(theFile, theFile.getFileName());
		ZipArchive a(c.close());
	}
	ZipManipulator zm(Poco::Path::temp() + "appinf.zip", true);
	zm.deleteFile("test.zip");
	zm.addFile("doc/data.zip", ZipTest::getTestFile("data", "data.zip"));
	ZipArchive archive=zm.commit();
	assert (archive.findHeader("test.zip") == archive.headerEnd());
	assert (archive.findHeader("doc/data.zip") != archive.headerEnd());
}

Project: poco
File: ZipTest.cpp

void ZipTest::tearDown()
{
}

Project: poco
File: CompressTest.cpp

void CompressTest::testManipulator()
{
	{
		std::ofstream out(Poco::Path::temp() + "appinf.zip", std::ios::binary);
		Poco::Path theFile(ZipTest::getTestFile("data", "test.zip"));
		Compress c(out, true);
		c.addFile(theFile, theFile.getFileName());
		ZipArchive a(c.close());
	}
	ZipManipulator zm(Poco::Path::temp() + "appinf.zip", true);
	zm.renameFile("test.zip", "renamedtest.zip");
	zm.addFile("doc/othertest.zip", ZipTest::getTestFile("data", "test.zip"));
	ZipArchive archive=zm.commit();
	assert (archive.findHeader("doc/othertest.zip") != archive.headerEnd());
}

Project: poco
File: ZipStream.cpp

ZipStreamBuf::ZipStreamBuf(std::ostream& ostr, ZipLocalFileHeader& fileEntry, bool reposition):
	Poco::BufferedStreamBuf(STREAM_BUFFER_SIZE, std::ios::out),
	_pIstr(0),
	_pOstr(&ostr),
	_ptrBuf(),
	_ptrOBuf(),
	_ptrHelper(),
	_ptrOHelper(),
	_crc32(Poco::Checksum::TYPE_CRC32),
	_expectedCrc32(0),
	_checkCRC(false),
	_bytesWritten(0),
	_pHeader(&fileEntry)
{
	if (fileEntry.isEncrypted())
		throw Poco::NotImplementedException("Encryption not supported");

	if (fileEntry.isDirectory())
	{
		// only header, no payload, zero crc
		fileEntry.setSearchCRCAndSizesAfterData(false);
		fileEntry.setCompressedSize(0);
		fileEntry.setUncompressedSize(0);
		fileEntry.setCRC(0);
		std::string header = fileEntry.createHeader();
		ostr.write(header.c_str(), static_cast<std::streamsize>(header.size()));
	}
	else
	{
		fileEntry.setSearchCRCAndSizesAfterData(!reposition);
		if (fileEntry.getCompressionMethod() == ZipCommon::CM_DEFLATE)
		{
			int level = Z_DEFAULT_COMPRESSION;
			if (fileEntry.getCompressionLevel() == ZipCommon::CL_FAST || fileEntry.getCompressionLevel() == ZipCommon::CL_SUPERFAST)
				level = Z_BEST_SPEED;
			else if (fileEntry.getCompressionLevel() == ZipCommon::CL_MAXIMUM)
				level = Z_BEST_COMPRESSION;
			// ignore the zlib init string which is of size 2 and also ignore the 4 byte adler32 value at the end of the stream!
			_ptrOHelper = new PartialOutputStream(*_pOstr, 2, 4, false);
			_ptrOBuf = new Poco::DeflatingOutputStream(*_ptrOHelper, DeflatingStreamBuf::STREAM_ZLIB, level);
		}
		else if (fileEntry.getCompressionMethod() == ZipCommon::CM_STORE)
		{
			_ptrOHelper = new PartialOutputStream(*_pOstr, 0, 0, false);
			_ptrOBuf = new PartialOutputStream(*_ptrOHelper, 0, 0, false);
		}
		else throw Poco::NotImplementedException("Unsupported compression method");

		// now write the header to the ostr!
        if (fileEntry.needsZip64())
            fileEntry.setZip64Data();
		std::string header = fileEntry.createHeader();
		ostr.write(header.c_str(), static_cast<std::streamsize>(header.size()));
	}
}

Project: poco
File: CompressTest.cpp

CppUnit::Test* CompressTest::suite()
{
	CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("CompressTest");

	CppUnit_addTest(pSuite, CompressTest, testSingleFile);
	CppUnit_addTest(pSuite, CompressTest, testDirectory);
	CppUnit_addTest(pSuite, CompressTest, testManipulator);
	CppUnit_addTest(pSuite, CompressTest, testManipulatorDel);
	CppUnit_addTest(pSuite, CompressTest, testManipulatorReplace);
	CppUnit_addTest(pSuite, CompressTest, testSetZipComment);
	CppUnit_addTest(pSuite, CompressTest, testZip64);

	return pSuite;
}

Project: poco
File: CompressTest.cpp

CompressTest::~CompressTest()
{
}

Project: poco
File: ZipStream.cpp

bool ZipStreamBuf::crcValid() const
{
	if (!_ptrBuf) return true; // directory entry
	return _crc32.checksum() == _expectedCrc32;
}

Project: poco
File: CompressTest.cpp

void CompressTest::testSingleFile()
{
	std::ofstream out(Poco::Path::temp() + "appinf.zip", std::ios::binary);
	Poco::Path theFile(ZipTest::getTestFile("data", "test.zip"));
	Compress c(out, true);
	c.addFile(theFile, theFile.getFileName());
	ZipArchive a(c.close());
}

Project: poco
File: ZipTest.cpp

void ZipTest::testValidPath()
{
	assert (ZipCommon::isValidPath("."));
	assert (ZipCommon::isValidPath("file.txt"));
	assert (ZipCommon::isValidPath(".file.txt"));
	assert (ZipCommon::isValidPath("..file.txt"));
	assert (ZipCommon::isValidPath("file.txt.."));
	assert (ZipCommon::isValidPath(".file..txt"));
	assert (ZipCommon::isValidPath("~file..txt"));
	assert (ZipCommon::isValidPath("~file/~"));
	assert (ZipCommon::isValidPath("dir/~"));
	assert (ZipCommon::isValidPath("some"));
	assert (ZipCommon::isValidPath("some/dir"));
	assert (ZipCommon::isValidPath("some/dir/or/another"));
	assert (ZipCommon::isValidPath("some/dir/./another"));
	assert (ZipCommon::isValidPath("some/dir/or/another/file.txt"));
	assert (ZipCommon::isValidPath("s~me\\d.r\\.or..\\an..her\\file.txt"));
	assert (ZipCommon::isValidPath("some\\dir\\or\\another"));
	assert (ZipCommon::isValidPath("some\\dir\\or\\another\\file.txt"));
	assert (ZipCommon::isValidPath("s~me\\d.r/.or..\\an..her\\file.txt"));

	assert (!ZipCommon::isValidPath("/../"));
	assert (!ZipCommon::isValidPath("/"));
	assert (!ZipCommon::isValidPath("\\..\\"));
	assert (!ZipCommon::isValidPath("/..\\"));
	assert (!ZipCommon::isValidPath("\\../"));
	assert (!ZipCommon::isValidPath(".."));
	assert (!ZipCommon::isValidPath("~/"));
	assert (!ZipCommon::isValidPath("~/~"));
	assert (!ZipCommon::isValidPath("/~"));
	assert (!ZipCommon::isValidPath("/file.txt"));
	assert (!ZipCommon::isValidPath("~/file.txt"));
	assert (!ZipCommon::isValidPath("some/dir/or/../another/file.txt"));
	assert (!ZipCommon::isValidPath("C:\\Windows\\system32"));
}

Project: poco
File: Decompress.cpp

void Decompress::onOk(const void*, std::pair<const ZipLocalFileHeader, const Poco::Path>& val)
{
	_mapping.insert(std::make_pair(val.first.getFileName(), val.second));
}

Project: poco
File: CompressTest.cpp

void CompressTest::testDirectory()
{
	std::ofstream out(Poco::Path::temp() + "pocobin.zip", std::ios::binary);
	Poco::File aFile("some/");
	if (aFile.exists()) aFile.remove(true);
	Poco::File aDir("some/recursive/dir/");
	aDir.createDirectories();
	Poco::File aDir2("some/other/recursive/dir/");
	aDir2.createDirectories();
	Poco::File aF("some/recursive/dir/test.file");
	aF.createFile();
	Poco::FileOutputStream fos(aF.path());
	fos << "just some test data";
	fos.close();

	Poco::Path theFile(aFile.path());
	theFile.makeDirectory();
	Compress c(out, true);
	c.addRecursive(theFile, ZipCommon::CL_MAXIMUM, false, theFile);
	ZipArchive a(c.close());
	Poco::File(aFile).remove(true);
}

Project: poco
File: ZipTest.cpp

void ZipTest::testCrcAndSizeAfterData()
{
	std::string testFile = getTestFile("data", "data.zip");
	std::ifstream inp(testFile.c_str(), std::ios::binary);
	assert (inp.good());
	Decompress dec(inp, Poco::Path::temp());
	dec.EError += Poco::Delegate<ZipTest, std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string> >(this, &ZipTest::onDecompressError);
	dec.decompressAllFiles();
	dec.EError -= Poco::Delegate<ZipTest, std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string> >(this, &ZipTest::onDecompressError);
	assert (_errCnt == 0);
	assert (!dec.mapping().empty());
}

Project: poco
File: ZipTest.cpp

void ZipTest::onDecompressError(const void* pSender, std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string>& info)
{
	++_errCnt;
}

Project: poco
File: ZipTest.cpp

void ZipTest::testDecompress()
{
	std::string testFile = getTestFile("data", "test.zip");
	std::ifstream inp(testFile.c_str(), std::ios::binary);
	assert (inp.good());
	Decompress dec(inp, Poco::Path::temp());
	dec.EError += Poco::Delegate<ZipTest, std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string> >(this, &ZipTest::onDecompressError);
	dec.decompressAllFiles();
	dec.EError -= Poco::Delegate<ZipTest, std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string> >(this, &ZipTest::onDecompressError);
	assert (_errCnt == 0);
	assert (!dec.mapping().empty());
}

Project: poco
File: CompressTest.cpp

void CompressTest::createDataFile(const std::string& path, Poco::UInt64 size)
{
	std::ofstream out(path.c_str(), std::ios::binary | std::ios::trunc);
	assert( ! out.fail() );
	Poco::Buffer<char> buffer(MB);
	for(int i = 0; size != 0; i++) {
		std::memset(buffer.begin(), i, buffer.size());
		Poco::UInt64 bytesToWrite = std::min(size, static_cast<Poco::UInt64>(buffer.size()));
		out.write(buffer.begin(), bytesToWrite);
		assert( ! out.fail() );
		size -= bytesToWrite;
	}
	out.flush();
	assert( ! out.fail() );
	out.close();
	assert( ! out.fail() );
}

Project: poco
File: ZipTest.cpp

ZipTest::ZipTest(const std::string& name): CppUnit::TestCase(name)
{
}

Project: poco
File: ZipStream.cpp

ZipInputStream::ZipInputStream(std::istream& istr, const ZipLocalFileHeader& fileEntry, bool reposition): ZipIOS(istr, fileEntry, reposition), std::istream(&_buf)
{
}

Project: poco
File: Decompress.cpp

Decompress::Decompress(std::istream& in, const Poco::Path& outputDir, bool flattenDirs, bool keepIncompleteFiles):
	_in(in),
	_outDir(outputDir),
	_flattenDirs(flattenDirs),
	_keepIncompleteFiles(keepIncompleteFiles),
	_mapping()
{
	_outDir.makeAbsolute();
	_outDir.makeDirectory();
	if (!_in.good()) throw Poco::IOException("Bad input stream");
	Poco::File tmp(_outDir);
	if (!tmp.exists())
	{
		tmp.createDirectories();
	}
	if (!tmp.isDirectory())
		throw Poco::IOException("Failed to create/open directory: " + _outDir.toString());
	EOk += Poco::Delegate<Decompress, std::pair<const ZipLocalFileHeader, const Poco::Path> >(this, &Decompress::onOk);

}

Project: poco
File: ZipStream.cpp

ZipStreamBuf::~ZipStreamBuf()
{
	// make sure destruction of streams happens in correct order
	_ptrOBuf = 0;
	_ptrOHelper = 0;
	_ptrBuf = 0;
	_ptrHelper = 0;
}

Project: poco
File: ZipStream.cpp

ZipIOS::ZipIOS(std::istream& istr, const ZipLocalFileHeader& fileEntry, bool reposition): _buf(istr, fileEntry, reposition)
{
	poco_ios_init(&_buf);
}

Project: poco
File: ZipTest.cpp

void ZipTest::testDecompressSingleFile()
{
	std::string testFile = getTestFile("data", "test.zip");
	std::ifstream inp(testFile.c_str(), std::ios::binary);
	assert (inp.good());
	ZipArchive arch(inp);
	ZipArchive::FileHeaders::const_iterator it = arch.findHeader("testfile.txt");
	assert (it != arch.headerEnd());
	ZipInputStream zipin (inp, it->second);
	std::ostringstream out(std::ios::binary);
	Poco::StreamCopier::copyStream(zipin, out);
	assert(!out.str().empty());
}

Project: poco
File: Decompress.cpp

bool Decompress::handleZipEntry(std::istream& zipStream, const ZipLocalFileHeader& hdr)
{
	if (hdr.isDirectory())
	{
		// directory have 0 size, nth to read
		if (!_flattenDirs)
		{
			std::string dirName = hdr.getFileName();
			if (!ZipCommon::isValidPath(dirName))
				throw ZipException("Illegal entry name", dirName);
			Poco::Path dir(_outDir, dirName);
			dir.makeDirectory();
			Poco::File aFile(dir);
			aFile.createDirectories();
		}
		return true;
	}
	try
	{
		std::string fileName = hdr.getFileName();
		if (_flattenDirs)
		{
			// remove path info
			Poco::Path p(fileName);
			p.makeFile();
			fileName = p.getFileName();
		}

		if (!ZipCommon::isValidPath(fileName))
			throw ZipException("Illegal entry name", fileName);

		Poco::Path file(fileName);
		file.makeFile();
		Poco::Path dest(_outDir, file);
		dest.makeFile();
		if (dest.depth() > 0)
		{
			Poco::File aFile(dest.parent());
			aFile.createDirectories();
		}
		Poco::FileOutputStream out(dest.toString());
		ZipInputStream inp(zipStream, hdr, false);
		Poco::StreamCopier::copyStream(inp, out);
		out.close();
		Poco::File aFile(dest.toString());
		if (!aFile.exists() || !aFile.isFile())
		{
			std::pair<const ZipLocalFileHeader, const std::string> tmp = std::make_pair(hdr, "Failed to create output stream " + dest.toString());
			EError.notify(this, tmp);
			return false;
		}

		if (!inp.crcValid())
		{
			if (!_keepIncompleteFiles)
				aFile.remove();
			std::pair<const ZipLocalFileHeader, const std::string> tmp = std::make_pair(hdr, "CRC mismatch. Corrupt file: " + dest.toString());
			EError.notify(this, tmp);
			return false;
		}

		// cannot check against hdr.getUnCompressedSize if CRC and size are not set in hdr but in a ZipDataInfo
		// crc is typically enough to detect errors
		if (aFile.getSize() != hdr.getUncompressedSize() && !hdr.searchCRCAndSizesAfterData())
		{
			if (!_keepIncompleteFiles)
				aFile.remove();
			std::pair<const ZipLocalFileHeader, const std::string> tmp = std::make_pair(hdr, "Filesizes do not match. Corrupt file: " + dest.toString());
			EError.notify(this, tmp);
			return false;
		}

		std::pair<const ZipLocalFileHeader, const Poco::Path> tmp = std::make_pair(hdr, file);
		EOk.notify(this, tmp);
	}
	catch (Poco::Exception& e)
	{
		std::pair<const ZipLocalFileHeader, const std::string> tmp = std::make_pair(hdr, std::string("Exception: " + e.displayText()));
		EError.notify(this, tmp);
		return false;
	}
	catch (...)
	{
		std::pair<const ZipLocalFileHeader, const std::string> tmp = std::make_pair(hdr, std::string("Unknown Exception"));
		EError.notify(this, tmp);
		return false;
	}

	return true;
}

Project: poco
File: ZipStream.cpp

ZipOutputStream::ZipOutputStream(std::ostream& ostr, ZipLocalFileHeader& fileEntry, bool seekableOutput): ZipIOS(ostr, fileEntry, seekableOutput), std::ostream(&_buf)
{
}

Project: poco
File: CompressTest.cpp

void CompressTest::testManipulatorReplace()
{
	{
		std::ofstream out(Poco::Path::temp() + "appinf.zip", std::ios::binary);
		Poco::Path theFile(ZipTest::getTestFile("data", "test.zip"));
		Compress c(out, true);
		c.addFile(theFile, theFile.getFileName());
		ZipArchive a(c.close());
	}
	ZipManipulator zm(Poco::Path::temp() + "appinf.zip", true);
	zm.replaceFile("test.zip", ZipTest::getTestFile("data", "doc.zip"));
	
	ZipArchive archive=zm.commit();
	assert (archive.findHeader("test.zip") != archive.headerEnd());
	assert (archive.findHeader("doc.zip") == archive.headerEnd());
}

Project: poco
File: ZipTest.cpp

CppUnit::Test* ZipTest::suite()
{
	CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("ZipTest");

	CppUnit_addTest(pSuite, ZipTest, testSkipSingleFile);
	CppUnit_addTest(pSuite, ZipTest, testDecompressSingleFile);
	CppUnit_addTest(pSuite, ZipTest, testDecompressSingleFileInDir);
	CppUnit_addTest(pSuite, ZipTest, testDecompress);
	CppUnit_addTest(pSuite, ZipTest, testDecompressFlat);
	CppUnit_addTest(pSuite, ZipTest, testDecompressVuln);
	CppUnit_addTest(pSuite, ZipTest, testDecompressFlatVuln);
	CppUnit_addTest(pSuite, ZipTest, testCrcAndSizeAfterData);
	CppUnit_addTest(pSuite, ZipTest, testCrcAndSizeAfterDataWithArchive);
	CppUnit_addTest(pSuite, ZipTest, testDecompressZip64);
	CppUnit_addTest(pSuite, ZipTest, testValidPath);

	return pSuite;
}

Project: poco
File: ZipStream.cpp

void ZipOutputStream::close(Poco::UInt64& extraDataSize)
{
	flush();
	_buf.close(extraDataSize);
}

Project: poco
File: ZipStream.cpp

ZipInputStream::~ZipInputStream()
{
}

Project: poco
File: ZipStream.cpp

void ZipStreamBuf::close(Poco::UInt64& extraDataSize)
{
	extraDataSize = 0;
	if (_ptrOBuf && _pHeader)
	{
		_ptrOBuf->flush();
		DeflatingOutputStream* pDO = dynamic_cast<DeflatingOutputStream*>(_ptrOBuf.get());
		if (pDO)
			pDO->close();
		if (_ptrOHelper)
		{
			_ptrOHelper->flush();
			_ptrOHelper->close();
		}
		_ptrOBuf = 0;
		if (!*_pOstr) throw Poco::IOException("Bad output stream");

		// write an extra datablock if required
		// or fix the crc entries
		poco_check_ptr(_pHeader);
		_pHeader->setCRC(_crc32.checksum());
		_pHeader->setUncompressedSize(_bytesWritten);
		_pHeader->setCompressedSize(_ptrOHelper->bytesWritten());
		if (_bytesWritten == 0)
		{
			poco_assert (_ptrOHelper->bytesWritten() == 0);
			// Empty files must use CM_STORE, otherwise unzipping will fail
			_pHeader->setCompressionMethod(ZipCommon::CM_STORE);
			_pHeader->setCompressionLevel(ZipCommon::CL_NORMAL);
		}
		_pHeader->setStartPos(_pHeader->getStartPos()); // This resets EndPos now that compressed Size is known

		if (_pHeader->searchCRCAndSizesAfterData())
		{
            if (_pHeader->needsZip64())
            {
			    ZipDataInfo64 info;
			    info.setCRC32(_crc32.checksum());
			    info.setUncompressedSize(_bytesWritten);
			    info.setCompressedSize(_ptrOHelper->bytesWritten());
                extraDataSize = info.getFullHeaderSize();
			    _pOstr->write(info.getRawHeader(), static_cast<std::streamsize>(extraDataSize));
            }
            else
            {
 			    ZipDataInfo info;
			    info.setCRC32(_crc32.checksum());
			    info.setUncompressedSize(static_cast<Poco::UInt32>(_bytesWritten));
			    info.setCompressedSize(static_cast<Poco::UInt32>(_ptrOHelper->bytesWritten()));
                extraDataSize = info.getFullHeaderSize();
			    _pOstr->write(info.getRawHeader(), static_cast<std::streamsize>(extraDataSize));
           }
		}
		else
		{
			_pOstr->seekp(_pHeader->getStartPos(), std::ios_base::beg);
			if (!*_pOstr) throw Poco::IOException("Bad output stream");

            if (_pHeader->hasExtraField())   // Update sizes in header extension.
                _pHeader->setZip64Data();
			std::string header = _pHeader->createHeader();
			_pOstr->write(header.c_str(), static_cast<std::streamsize>(header.size()));
			_pOstr->seekp(0, std::ios_base::end);
			if (!*_pOstr) throw Poco::IOException("Bad output stream");
		}
		_pHeader = 0;
	}
}

Project: poco
File: ZipTest.cpp

void ZipTest::testDecompressZip64()
{
	std::map<std::string, Poco::UInt64> files;
	files[Poco::Path::temp() + "data1.bin"] = static_cast<Poco::UInt64>(KB)*4096+1;
	files[Poco::Path::temp() + "data2.bin"] = static_cast<Poco::UInt64>(KB)*16;
	files[Poco::Path::temp() + "data3.bin"] = static_cast<Poco::UInt64>(KB)*4096-1;

	for(std::map<std::string, Poco::UInt64>::const_iterator it = files.begin(); it != files.end(); it++)
	{
		Poco::File file(it->first);
		if(file.exists())
			file.remove();
	}
	std::ifstream in(Poco::Path::temp() + "zip64.zip", std::ios::binary);
	Decompress c(in, Poco::Path::temp());
	c.decompressAllFiles();
	for(std::map<std::string, Poco::UInt64>::const_iterator it = files.begin(); it != files.end(); it++)
	{
		verifyDataFile(it->first, it->second);
	}
}

Project: poco
File: ZipTest.cpp

void ZipTest::testDecompressFlatVuln()
{
	std::string testFile = getTestFile("data", "vuln.zip");
	std::ifstream inp(testFile.c_str(), std::ios::binary);
	assert(inp.good());
	Decompress dec(inp, Poco::Path::temp(), true);
	dec.EError += Poco::Delegate<ZipTest, std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string> >(this, &ZipTest::onDecompressError);
	dec.decompressAllFiles();
	dec.EError -= Poco::Delegate<ZipTest, std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string> >(this, &ZipTest::onDecompressError);
	assert (_errCnt == 0);
	assert (!dec.mapping().empty());
}