fixed GH #2661: Poco::Zip::ZipArchive cannot load new tomcat.zip file

This commit is contained in:
Günter Obiltschnig 2019-06-22 18:24:49 +02:00
parent bb916b84d0
commit bad473f568
5 changed files with 87 additions and 20 deletions

View File

@ -11,7 +11,7 @@ POCO Zip adds support for parsing and creating Zip files. It offers the followin
!!Restrictions
* POCO Zip does not support the DEFLATE64 algorithm.
* encrypted files are not supported
!!!Main Classes
Most users will work with two common classes: <*Compress*> and <*Decompress*>
@ -23,31 +23,31 @@ Creating a Zip file is a basically a three-step process:
Compress(std::ostream& out, bool seekableOut);
----
* Add entries: either add single files or directory entries
void addFile(std::istream& input,
const Poco::DateTime& lastModifiedAt,
const Poco::Path& fileName,
ZipCommon::CompressionMethod cm = ZipCommon::CM_DEFLATE,
void addFile(std::istream& input,
const Poco::DateTime& lastModifiedAt,
const Poco::Path& fileName,
ZipCommon::CompressionMethod cm = ZipCommon::CM_DEFLATE,
ZipCommon::CompressionLevel cl = ZipCommon::CL_MAXIMUM);
/// Adds a single file to the Zip File. fileName must not be a directory name.
void addFile(const Poco::Path& file,
const Poco::Path& fileName,
ZipCommon::CompressionMethod cm = ZipCommon::CM_DEFLATE,
void addFile(const Poco::Path& file,
const Poco::Path& fileName,
ZipCommon::CompressionMethod cm = ZipCommon::CM_DEFLATE,
ZipCommon::CompressionLevel cl = ZipCommon::CL_MAXIMUM);
/// Adds a single file to the Zip File. fileName must not be a directory name. The file must exist physically!
void addDirectory(const Poco::Path& entryName, const Poco::DateTime& lastModifiedAt);
/// Adds a directory entry excluding all children to the Zip file, entryName must not be empty.
void addRecursive(const Poco::Path& entry,
ZipCommon::CompressionLevel cl = ZipCommon::CL_MAXIMUM,
bool excludeRoot = true,
void addRecursive(const Poco::Path& entry,
ZipCommon::CompressionLevel cl = ZipCommon::CL_MAXIMUM,
bool excludeRoot = true,
const Poco::Path& name = Poco::Path());
/// Adds a directory entry recursively to the zip file, set excludeRoot to false to exclude the parent directory.
/// The name specifies under which path the entries are added in the Zip file.
----
Note that one must always define a name when adding a file entry, otherwise the compresser can not decide if the file should be added with an absolute or a relative path.
Note that one must always define a name when adding a file entry, otherwise the compresser can not decide if the file should be added with an absolute or a relative path.
Assume you are adding the file <*c:\\data\\hello.txt*> twice to a Zip:
// MUST use binary!
@ -96,7 +96,7 @@ To get notified about the entries that are added during <*addRecursive*> registe
Poco::FIFOEvent<const ZipLocalFileHeader> EDone;
----
* Closing the Zip file: It is mandatory to manually close Compress objects. This guarantees that the Zip directory is written, thus creating a valid Zip file. It is safe to call <*close*> multiple times, only the first call takes effect.
ZipArchive close();
----
<*close*> returns a ZipArchive which describes all entries inside a Zip file.
@ -110,7 +110,7 @@ The following sample code shows how all entries can be extracted from a Zip file
std::ifstream inp("test.zip", std::ios::binary);
poco_assert (inp);
// decompress to current working dir
Decompress dec(inp, Poco::Path());
Decompress dec(inp, Poco::Path());
// if an error happens invoke the ZipTest::onDecompressError method
dec.EError += Poco::Delegate<ZipTest, std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string> >(this, &ZipTest::onDecompressError);
dec.decompressAllFiles();
@ -152,7 +152,8 @@ To decompress single files you must first parse a Zip file, and then decompress
ZipArchive arch(inp);
ZipArchive::FileHeaders::const_iterator it = arch.findHeader("data/hello.txt");
poco_assert (it != arch.headerEnd());
ZipInputStream zipin (inp, it->second);
inp.clear();
ZipInputStream zipin(inp, it->second);
std::ostringstream out(std::ios::binary);
Poco::StreamCopier::copyStream(zipin, out);
----

View File

@ -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

View File

@ -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;
}

View File

@ -52,7 +52,7 @@ ZipFileInfo::ZipFileInfo(const ZipLocalFileHeader& header):
if (getHostSystem() == ZipCommon::HS_UNIX)
setUnixAttributes();
_rawInfo[GENERAL_PURPOSE_POS+1] |= 0x08; // Set "language encoding flag" to indicate that filenames and paths are in UTF-8.
_rawInfo[GENERAL_PURPOSE_POS+1] |= 0x08; // Set "language encoding flag" to indicate that filenames and paths are in UTF-8.
if (header.searchCRCAndSizesAfterData())
_rawInfo[GENERAL_PURPOSE_POS] |= 0x08;
@ -126,8 +126,7 @@ void ZipFileInfo::parse(std::istream& inp, bool assumeHeaderRead)
ptr += 2;
if (id == ZipCommon::ZIP64_EXTRA_ID)
{
poco_assert(size >= 8);
if (getUncompressedSizeFromHeader() == ZipCommon::ZIP64_MAGIC)
if (size >= 8 && getUncompressedSizeFromHeader() == ZipCommon::ZIP64_MAGIC)
{
setUncompressedSize(ZipUtil::get64BitValue(ptr, 0));
size -= 8;

View File

@ -161,6 +161,70 @@ 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)
{
if (fn.find("\\") != std::string::npos)