Browse Source

Added a workaround in ZipFile for reading files which contain the wrong offset for their central directory

tags/2021-05-28
jules 7 years ago
parent
commit
53392faeb8
2 changed files with 105 additions and 106 deletions
  1. +99
    -98
      modules/juce_core/zip/juce_ZipFile.cpp
  2. +6
    -8
      modules/juce_core/zip/juce_ZipFile.h

+ 99
- 98
modules/juce_core/zip/juce_ZipFile.cpp View File

@@ -23,17 +23,16 @@
namespace juce namespace juce
{ {
class ZipFile::ZipEntryHolder
struct ZipFile::ZipEntryHolder
{ {
public:
ZipEntryHolder (const char* const buffer, const int fileNameLen)
ZipEntryHolder (const char* buffer, int fileNameLen)
{ {
isCompressed = ByteOrder::littleEndianShort (buffer + 10) != 0; isCompressed = ByteOrder::littleEndianShort (buffer + 10) != 0;
entry.fileTime = parseFileTime ((uint32) ByteOrder::littleEndianShort (buffer + 12),
(uint32) ByteOrder::littleEndianShort (buffer + 14));
compressedSize = (int64) (uint32) ByteOrder::littleEndianInt (buffer + 20);
entry.uncompressedSize = (int64) (uint32) ByteOrder::littleEndianInt (buffer + 24);
streamOffset = (int64) (uint32) ByteOrder::littleEndianInt (buffer + 42);
entry.fileTime = parseFileTime (ByteOrder::littleEndianShort (buffer + 12),
ByteOrder::littleEndianShort (buffer + 14));
compressedSize = (int64) ByteOrder::littleEndianInt (buffer + 20);
entry.uncompressedSize = (int64) ByteOrder::littleEndianInt (buffer + 24);
streamOffset = (int64) ByteOrder::littleEndianInt (buffer + 42);
entry.filename = String::fromUTF8 (buffer + 46, fileNameLen); entry.filename = String::fromUTF8 (buffer + 46, fileNameLen);
} }
@@ -45,72 +44,81 @@ public:
} }
}; };
ZipEntry entry;
int64 streamOffset, compressedSize;
bool isCompressed;
private:
static Time parseFileTime (uint32 time, uint32 date) noexcept static Time parseFileTime (uint32 time, uint32 date) noexcept
{ {
const int year = 1980 + (date >> 9);
const int month = ((date >> 5) & 15) - 1;
const int day = date & 31;
const int hours = time >> 11;
const int minutes = (time >> 5) & 63;
const int seconds = (int) ((time & 31) << 1);
return Time (year, month, day, hours, minutes, seconds);
int year = 1980 + (date >> 9);
int month = ((date >> 5) & 15) - 1;
int day = date & 31;
int hours = time >> 11;
int minutes = (time >> 5) & 63;
int seconds = (int) ((time & 31) << 1);
return { year, month, day, hours, minutes, seconds };
} }
ZipEntry entry;
int64 streamOffset, compressedSize;
bool isCompressed;
}; };
//============================================================================== //==============================================================================
namespace
static int64 findCentralDirectoryFileHeader (InputStream& input, int& numEntries)
{ {
int findEndOfZipEntryTable (InputStream& input, int& numEntries)
{
BufferedInputStream in (input, 8192);
in.setPosition (in.getTotalLength());
int64 pos = in.getPosition();
const int64 lowestPos = jmax ((int64) 0, pos - 1024);
BufferedInputStream in (input, 8192);
char buffer [32] = { 0 };
in.setPosition (in.getTotalLength());
auto pos = in.getPosition();
auto lowestPos = jmax ((int64) 0, pos - 1024);
char buffer[32] = {};
while (pos > lowestPos)
{
in.setPosition (pos - 22);
pos = in.getPosition();
memcpy (buffer + 22, buffer, 4);
while (pos > lowestPos)
{
in.setPosition (pos - 22);
pos = in.getPosition();
memcpy (buffer + 22, buffer, 4);
if (in.read (buffer, 22) != 22)
return 0;
if (in.read (buffer, 22) != 22)
return 0;
for (int i = 0; i < 22; ++i)
for (int i = 0; i < 22; ++i)
{
if (ByteOrder::littleEndianInt (buffer + i) == 0x06054b50)
{ {
if (ByteOrder::littleEndianInt (buffer + i) == 0x06054b50)
{
in.setPosition (pos + i);
in.read (buffer, 22);
numEntries = ByteOrder::littleEndianShort (buffer + 10);
in.setPosition (pos + i);
in.read (buffer, 22);
numEntries = ByteOrder::littleEndianShort (buffer + 10);
auto offset = (int64) ByteOrder::littleEndianInt (buffer + 16);
return (int) ByteOrder::littleEndianInt (buffer + 16);
if (offset >= 4)
{
in.setPosition (offset);
// This is a workaround for some zip files which seem to contain the
// wrong offset for the central directory - instead of including the
// header, they point to the byte immediately after it.
if (in.readInt() != 0x02014b50)
{
in.setPosition (offset - 4);
if (in.readInt() == 0x02014b50)
offset -= 4;
}
} }
return offset;
} }
} }
return 0;
} }
return 0;
} }
//============================================================================== //==============================================================================
class ZipFile::ZipInputStream : public InputStream
struct ZipFile::ZipInputStream : public InputStream
{ {
public:
ZipInputStream (ZipFile& zf, ZipFile::ZipEntryHolder& zei)
ZipInputStream (ZipFile& zf, const ZipFile::ZipEntryHolder& zei)
: file (zf), : file (zf),
zipEntryHolder (zei), zipEntryHolder (zei),
pos (0),
headerSize (0),
inputStream (zf.inputStream) inputStream (zf.inputStream)
{ {
if (zf.inputSource != nullptr) if (zf.inputSource != nullptr)
@@ -124,7 +132,7 @@ public:
#endif #endif
} }
char buffer [30];
char buffer[30];
if (inputStream != nullptr if (inputStream != nullptr
&& inputStream->setPosition (zei.streamOffset) && inputStream->setPosition (zei.streamOffset)
@@ -196,8 +204,8 @@ public:
private: private:
ZipFile& file; ZipFile& file;
ZipEntryHolder zipEntryHolder; ZipEntryHolder zipEntryHolder;
int64 pos;
int headerSize;
int64 pos = 0;
int headerSize = 0;
InputStream* inputStream; InputStream* inputStream;
ScopedPointer<InputStream> streamToDelete; ScopedPointer<InputStream> streamToDelete;
@@ -206,7 +214,7 @@ private:
//============================================================================== //==============================================================================
ZipFile::ZipFile (InputStream* const stream, const bool deleteStreamWhenDestroyed)
ZipFile::ZipFile (InputStream* stream, bool deleteStreamWhenDestroyed)
: inputStream (stream) : inputStream (stream)
{ {
if (deleteStreamWhenDestroyed) if (deleteStreamWhenDestroyed)
@@ -215,22 +223,17 @@ ZipFile::ZipFile (InputStream* const stream, const bool deleteStreamWhenDestroye
init(); init();
} }
ZipFile::ZipFile (InputStream& stream)
: inputStream (&stream)
ZipFile::ZipFile (InputStream& stream) : inputStream (&stream)
{ {
init(); init();
} }
ZipFile::ZipFile (const File& file)
: inputStream (nullptr),
inputSource (new FileInputSource (file))
ZipFile::ZipFile (const File& file) : inputSource (new FileInputSource (file))
{ {
init(); init();
} }
ZipFile::ZipFile (InputSource* const source)
: inputStream (nullptr),
inputSource (source)
ZipFile::ZipFile (InputSource* source) : inputSource (source)
{ {
init(); init();
} }
@@ -260,7 +263,7 @@ int ZipFile::getNumEntries() const noexcept
const ZipFile::ZipEntry* ZipFile::getEntry (const int index) const noexcept const ZipFile::ZipEntry* ZipFile::getEntry (const int index) const noexcept
{ {
if (ZipEntryHolder* const zei = entries [index])
if (auto* zei = entries[index])
return &(zei->entry); return &(zei->entry);
return nullptr; return nullptr;
@@ -289,7 +292,7 @@ InputStream* ZipFile::createStreamForEntry (const int index)
{ {
InputStream* stream = nullptr; InputStream* stream = nullptr;
if (ZipEntryHolder* const zei = entries[index])
if (auto* zei = entries[index])
{ {
stream = new ZipInputStream (*this, *zei); stream = new ZipInputStream (*this, *zei);
@@ -337,27 +340,26 @@ void ZipFile::init()
if (in != nullptr) if (in != nullptr)
{ {
int numEntries = 0; int numEntries = 0;
int pos = findEndOfZipEntryTable (*in, numEntries);
auto centralDirectoryPos = findCentralDirectoryFileHeader (*in, numEntries);
if (pos >= 0 && pos < in->getTotalLength())
if (centralDirectoryPos >= 0 && centralDirectoryPos < in->getTotalLength())
{ {
const int size = (int) (in->getTotalLength() - pos);
auto size = (size_t) (in->getTotalLength() - centralDirectoryPos);
in->setPosition (pos);
in->setPosition (centralDirectoryPos);
MemoryBlock headerData; MemoryBlock headerData;
if (in->readIntoMemoryBlock (headerData, size) == (size_t) size)
if (in->readIntoMemoryBlock (headerData, (ssize_t) size) == size)
{ {
pos = 0;
size_t pos = 0;
for (int i = 0; i < numEntries; ++i) for (int i = 0; i < numEntries; ++i)
{ {
if (pos + 46 > size) if (pos + 46 > size)
break; break;
const char* const buffer = static_cast<const char*> (headerData.getData()) + pos;
const int fileNameLen = ByteOrder::littleEndianShort (buffer + 28);
auto* buffer = static_cast<const char*> (headerData.getData()) + pos;
auto fileNameLen = ByteOrder::littleEndianShort (buffer + 28);
if (pos + 46 + fileNameLen > size) if (pos + 46 + fileNameLen > size)
break; break;
@@ -378,7 +380,8 @@ Result ZipFile::uncompressTo (const File& targetDirectory,
{ {
for (int i = 0; i < entries.size(); ++i) for (int i = 0; i < entries.size(); ++i)
{ {
Result result (uncompressEntry (i, targetDirectory, shouldOverwriteFiles));
auto result = uncompressEntry (i, targetDirectory, shouldOverwriteFiles);
if (result.failed()) if (result.failed())
return result; return result;
} }
@@ -386,19 +389,20 @@ Result ZipFile::uncompressTo (const File& targetDirectory,
return Result::ok(); return Result::ok();
} }
Result ZipFile::uncompressEntry (const int index,
const File& targetDirectory,
bool shouldOverwriteFiles)
Result ZipFile::uncompressEntry (int index, const File& targetDirectory, bool shouldOverwriteFiles)
{ {
const ZipEntryHolder* zei = entries.getUnchecked (index);
auto* zei = entries.getUnchecked (index);
#if JUCE_WINDOWS #if JUCE_WINDOWS
const String entryPath (zei->entry.filename);
auto entryPath = zei->entry.filename;
#else #else
const String entryPath (zei->entry.filename.replaceCharacter ('\\', '/'));
auto entryPath = zei->entry.filename.replaceCharacter ('\\', '/');
#endif #endif
const File targetFile (targetDirectory.getChildFile (entryPath));
if (entryPath.isEmpty())
return Result::ok();
auto targetFile = targetDirectory.getChildFile (entryPath);
if (entryPath.endsWithChar ('/') || entryPath.endsWithChar ('\\')) if (entryPath.endsWithChar ('/') || entryPath.endsWithChar ('\\'))
return targetFile.createDirectory(); // (entry is a directory, not a file) return targetFile.createDirectory(); // (entry is a directory, not a file)
@@ -438,13 +442,10 @@ Result ZipFile::uncompressEntry (const int index,
//============================================================================== //==============================================================================
class ZipFile::Builder::Item
struct ZipFile::Builder::Item
{ {
public:
Item (const File& f, InputStream* s, int compression, const String& storedPath, Time time) Item (const File& f, InputStream* s, int compression, const String& storedPath, Time time)
: file (f), stream (s), storedPathname (storedPath), fileTime (time),
compressedSize (0), uncompressedSize (0), headerStart (0),
compressionLevel (compression), checksum (0)
: file (f), stream (s), storedPathname (storedPath), fileTime (time), compressionLevel (compression)
{ {
} }
@@ -496,9 +497,9 @@ private:
ScopedPointer<InputStream> stream; ScopedPointer<InputStream> stream;
String storedPathname; String storedPathname;
Time fileTime; Time fileTime;
int64 compressedSize, uncompressedSize, headerStart;
int compressionLevel;
unsigned long checksum;
int64 compressedSize = 0, uncompressedSize = 0, headerStart = 0;
int compressionLevel = 0;
unsigned long checksum = 0;
static void writeTimeAndDate (OutputStream& target, Time t) static void writeTimeAndDate (OutputStream& target, Time t)
{ {
@@ -523,7 +524,7 @@ private:
while (! stream->isExhausted()) while (! stream->isExhausted())
{ {
const int bytesRead = stream->read (buffer, bufferSize);
auto bytesRead = stream->read (buffer, bufferSize);
if (bytesRead < 0) if (bytesRead < 0)
return false; return false;
@@ -557,7 +558,7 @@ private:
ZipFile::Builder::Builder() {} ZipFile::Builder::Builder() {}
ZipFile::Builder::~Builder() {} ZipFile::Builder::~Builder() {}
void ZipFile::Builder::addFile (const File& file, const int compression, const String& path)
void ZipFile::Builder::addFile (const File& file, int compression, const String& path)
{ {
items.add (new Item (file, nullptr, compression, items.add (new Item (file, nullptr, compression,
path.isEmpty() ? file.getFileName() : path, path.isEmpty() ? file.getFileName() : path,
@@ -568,12 +569,12 @@ void ZipFile::Builder::addEntry (InputStream* stream, int compression, const Str
{ {
jassert (stream != nullptr); // must not be null! jassert (stream != nullptr); // must not be null!
jassert (path.isNotEmpty()); jassert (path.isNotEmpty());
items.add (new Item (File(), stream, compression, path, time));
items.add (new Item ({}, stream, compression, path, time));
} }
bool ZipFile::Builder::writeToStream (OutputStream& target, double* const progress) const bool ZipFile::Builder::writeToStream (OutputStream& target, double* const progress) const
{ {
const int64 fileStart = target.getPosition();
auto fileStart = target.getPosition();
for (int i = 0; i < items.size(); ++i) for (int i = 0; i < items.size(); ++i)
{ {
@@ -584,13 +585,13 @@ bool ZipFile::Builder::writeToStream (OutputStream& target, double* const progre
return false; return false;
} }
const int64 directoryStart = target.getPosition();
auto directoryStart = target.getPosition();
for (int i = 0; i < items.size(); ++i)
if (! items.getUnchecked (i)->writeDirectoryEntry (target))
for (auto* item : items)
if (! item->writeDirectoryEntry (target))
return false; return false;
const int64 directoryEnd = target.getPosition();
auto directoryEnd = target.getPosition();
target.writeInt (0x06054b50); target.writeInt (0x06054b50);
target.writeShort (0); target.writeShort (0);


+ 6
- 8
modules/juce_core/zip/juce_ZipFile.h View File

@@ -218,7 +218,7 @@ public:
//============================================================================== //==============================================================================
private: private:
class Item;
struct Item;
friend struct ContainerDeletePolicy<Item>; friend struct ContainerDeletePolicy<Item>;
OwnedArray<Item> items; OwnedArray<Item> items;
@@ -227,24 +227,22 @@ public:
private: private:
//============================================================================== //==============================================================================
class ZipInputStream;
class ZipEntryHolder;
friend class ZipInputStream;
friend class ZipEntryHolder;
struct ZipInputStream;
struct ZipEntryHolder;
OwnedArray<ZipEntryHolder> entries; OwnedArray<ZipEntryHolder> entries;
CriticalSection lock; CriticalSection lock;
InputStream* inputStream;
InputStream* inputStream = nullptr;
ScopedPointer<InputStream> streamToDelete; ScopedPointer<InputStream> streamToDelete;
ScopedPointer<InputSource> inputSource; ScopedPointer<InputSource> inputSource;
#if JUCE_DEBUG #if JUCE_DEBUG
struct OpenStreamCounter struct OpenStreamCounter
{ {
OpenStreamCounter() : numOpenStreams (0) {}
OpenStreamCounter() {}
~OpenStreamCounter(); ~OpenStreamCounter();
int numOpenStreams;
int numOpenStreams = 0;
}; };
OpenStreamCounter streamCounter; OpenStreamCounter streamCounter;


Loading…
Cancel
Save