Final refactoring of ZipHandler class.

This commit is contained in:
Stephen Anthony 2018-09-05 12:21:55 -02:30
parent cacb1e3341
commit ee643e818b
2 changed files with 138 additions and 61 deletions

View File

@ -393,6 +393,9 @@ void ZipHandler::ZipFile::decompress(BytePtr& out, uInt64 length)
case 14: case 14:
throw ZipError::LZMA_UNSUPPORTED; // FIXME - LZMA format not yet supported throw ZipError::LZMA_UNSUPPORTED; // FIXME - LZMA format not yet supported
default:
throw ZipError::UNSUPPORTED;
} }
} }
catch(const ZipError&) catch(const ZipError&)
@ -404,18 +407,27 @@ void ZipHandler::ZipFile::decompress(BytePtr& out, uInt64 length)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt64 ZipHandler::ZipFile::getCompressedDataOffset() uInt64 ZipHandler::ZipFile::getCompressedDataOffset()
{ {
// Don't support a number of features
GeneralFlagReader const flags(myHeader.bitFlag);
if(myHeader.startDiskNumber != myEcd.diskNumber ||
myHeader.versionNeeded > 63 || flags.patchData() ||
flags.encrypted() || flags.strongEncryption())
throw ZipError::UNSUPPORTED;
// Read the fixed-sized part of the local file header // Read the fixed-sized part of the local file header
uInt64 read_length = 0; uInt64 read_length = 0;
bool success = readStream(myBuffer, myHeader.localHeaderOffset, 0x1e, read_length); bool success = readStream(myBuffer, myHeader.localHeaderOffset, 0x1e, read_length);
if(!success) if(!success)
throw ZipError::FILE_ERROR; throw ZipError::FILE_ERROR;
else if(read_length != 0x1e) else if(read_length != LocalFileHeaderReader::minimumLength())
throw ZipError::FILE_TRUNCATED; throw ZipError::FILE_TRUNCATED;
// Compute the final offset // Compute the final offset
return myHeader.localHeaderOffset + 0x1e + LocalFileHeaderReader reader(&myBuffer[0]);
read_word(myBuffer.get() + 0x1a) + if(!reader.signatureCorrect())
read_word(myBuffer.get() + 0x1c); throw ZipError::BAD_SIGNATURE;
return myHeader.localHeaderOffset + reader.totalLength();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -151,64 +151,145 @@ class ZipHandler
}; };
using ZipFilePtr = unique_ptr<ZipFile>; using ZipFilePtr = unique_ptr<ZipFile>;
class EcdReader /** Classes to parse the ZIP metadata in an abstracted way */
class ReaderBase
{ {
public: protected:
explicit EcdReader(const uInt8* const b) : myBuf(b) { } explicit ReaderBase(const uInt8* const b) : myBuf(b) { }
uInt32 signature() const { return read_dword(myBuf + 0x00); } uInt8 read_byte(size_t offs) const
uInt16 thisDiskNo() const { return read_word(myBuf + 0x04); } {
uInt16 dirStartDisk() const { return read_word(myBuf + 0x06); } return myBuf[offs];
uInt16 dirDiskEntries() const { return read_word(myBuf + 0x08); } }
uInt16 dirTotalEntries() const { return read_word(myBuf + 0x0a); } uInt16 read_word(size_t offs) const
uInt32 dirSize() const { return read_dword(myBuf + 0x0c); } {
uInt32 dirOffset() const { return read_dword(myBuf + 0x10); } return (uInt16(myBuf[offs + 1]) << 8) |
uInt16 commentLength() const { return read_word(myBuf + 0x14); } (uInt16(myBuf[offs + 0]) << 0);
string comment() const { return read_string(myBuf + 0x16, commentLength()); } }
uInt32 read_dword(std::size_t offs) const
bool signatureCorrect() const { return signature() == 0x06054b50; } {
return (uInt32(myBuf[offs + 3]) << 24) |
size_t totalLength() const { return minimumLength() + commentLength(); } (uInt32(myBuf[offs + 2]) << 16) |
static size_t minimumLength() { return 0x16; } (uInt32(myBuf[offs + 1]) << 8) |
(uInt32(myBuf[offs + 0]) << 0);
}
uInt64 read_qword(size_t offs) const
{
return (uInt64(myBuf[offs + 7]) << 56) |
(uInt64(myBuf[offs + 6]) << 48) |
(uInt64(myBuf[offs + 5]) << 40) |
(uInt64(myBuf[offs + 4]) << 32) |
(uInt64(myBuf[offs + 3]) << 24) |
(uInt64(myBuf[offs + 2]) << 16) |
(uInt64(myBuf[offs + 1]) << 8) |
(uInt64(myBuf[offs + 0]) << 0);
}
string read_string(size_t offs, size_t len = string::npos) const
{
return string(reinterpret_cast<char const *>(myBuf + offs), len);
}
private: private:
const uInt8* const myBuf; const uInt8* const myBuf;
}; };
class CentralDirEntryReader class LocalFileHeaderReader : public ReaderBase
{ {
public: public:
explicit CentralDirEntryReader(const uInt8* const b) : myBuf(b) { } explicit LocalFileHeaderReader(const uInt8* const b) : ReaderBase(b) { }
uInt32 signature() const { return read_dword(myBuf + 0x00); } uInt32 signature() const { return read_dword(0x00); }
uInt8 versionCreated() const { return myBuf[0x04]; } uInt8 versionNeeded() const { return read_byte(0x04); }
uInt8 osCreated() const { return myBuf[0x05]; } uInt8 osNeeded() const { return read_byte(0x05); }
uInt8 versionNeeded() const { return myBuf[0x06]; } uInt16 generalFlag() const { return read_word(0x06); }
uInt8 osNeeded() const { return myBuf[0x07]; } uInt16 compressionMethod() const { return read_word(0x08); }
uInt16 generalFlag() const { return read_word(myBuf + 0x08); } uInt16 modifiedTime() const { return read_word(0x0a); }
uInt16 compressionMethod() const { return read_word(myBuf + 0x0a); } uInt16 modifiedDate() const { return read_word(0x0c); }
uInt16 modifiedTime() const { return read_word(myBuf + 0x0c); } uInt32 crc32() const { return read_dword(0x0e); }
uInt16 modifiedDate() const { return read_word(myBuf + 0x0e); } uInt32 compressedSize() const { return read_dword(0x12); }
uInt32 crc32() const { return read_dword(myBuf + 0x10); } uInt32 uncompressedSize() const { return read_dword(0x16); }
uInt32 compressedSize() const { return read_dword(myBuf + 0x14); } uInt16 filenameLength() const { return read_word(0x1a); }
uInt32 uncompressedSize() const { return read_dword(myBuf + 0x18); } uInt16 extraFieldLength() const { return read_word(0x1c); }
uInt16 filenameLength() const { return read_word(myBuf + 0x1c); } string filename() const { return read_string(0x1e, filenameLength()); }
uInt16 extraFieldLength() const { return read_word(myBuf + 0x1e); }
uInt16 fileCommentLength() const { return read_word(myBuf + 0x20); } bool signatureCorrect() const { return signature() == 0x04034b50; }
uInt16 startDisk() const { return read_word(myBuf + 0x22); }
uInt16 intFileAttr() const { return read_word(myBuf + 0x24); } size_t totalLength() const { return minimumLength() + filenameLength() + extraFieldLength(); }
uInt32 extFileAttr() const { return read_dword(myBuf + 0x26); } static size_t minimumLength() { return 0x1e; }
uInt32 headerOffset() const { return read_dword(myBuf + 0x2a); } };
string filename() const { return read_string(myBuf + 0x2e, filenameLength()); }
string fileComment() const { return read_string(myBuf + 0x2e + filenameLength() + extraFieldLength(), fileCommentLength()); } class CentralDirEntryReader : public ReaderBase
{
public:
explicit CentralDirEntryReader(const uInt8* const b) : ReaderBase(b) { }
uInt32 signature() const { return read_dword(0x00); }
uInt8 versionCreated() const { return read_byte(0x04); }
uInt8 osCreated() const { return read_byte(0x05); }
uInt8 versionNeeded() const { return read_byte(0x06); }
uInt8 osNeeded() const { return read_byte(0x07); }
uInt16 generalFlag() const { return read_word(0x08); }
uInt16 compressionMethod() const { return read_word(0x0a); }
uInt16 modifiedTime() const { return read_word(0x0c); }
uInt16 modifiedDate() const { return read_word(0x0e); }
uInt32 crc32() const { return read_dword(0x10); }
uInt32 compressedSize() const { return read_dword(0x14); }
uInt32 uncompressedSize() const { return read_dword(0x18); }
uInt16 filenameLength() const { return read_word(0x1c); }
uInt16 extraFieldLength() const { return read_word(0x1e); }
uInt16 fileCommentLength() const { return read_word(0x20); }
uInt16 startDisk() const { return read_word(0x22); }
uInt16 intFileAttr() const { return read_word(0x24); }
uInt32 extFileAttr() const { return read_dword(0x26); }
uInt32 headerOffset() const { return read_dword(0x2a); }
string filename() const { return read_string(0x2e, filenameLength()); }
string fileComment() const { return read_string(0x2e + filenameLength() + extraFieldLength(), fileCommentLength()); }
bool signatureCorrect() const { return signature() == 0x02014b50; } bool signatureCorrect() const { return signature() == 0x02014b50; }
size_t totalLength() const { return minimumLength() + filenameLength() + extraFieldLength() + fileCommentLength(); } size_t totalLength() const { return minimumLength() + filenameLength() + extraFieldLength() + fileCommentLength(); }
static size_t minimumLength() { return 0x2e; } static size_t minimumLength() { return 0x2e; }
};
class EcdReader : public ReaderBase
{
public:
explicit EcdReader(const uInt8* const b) : ReaderBase(b) { }
uInt32 signature() const { return read_dword(0x00); }
uInt16 thisDiskNo() const { return read_word(0x04); }
uInt16 dirStartDisk() const { return read_word(0x06); }
uInt16 dirDiskEntries() const { return read_word(0x08); }
uInt16 dirTotalEntries() const { return read_word(0x0a); }
uInt32 dirSize() const { return read_dword(0x0c); }
uInt32 dirOffset() const { return read_dword(0x10); }
uInt16 commentLength() const { return read_word(0x14); }
string comment() const { return read_string(0x16, commentLength()); }
bool signatureCorrect() const { return signature() == 0x06054b50; }
size_t totalLength() const { return minimumLength() + commentLength(); }
static size_t minimumLength() { return 0x16; }
};
class GeneralFlagReader
{
public:
explicit GeneralFlagReader(uInt16 val) : myValue(val) { }
bool encrypted() const { return bool(myValue & 0x0001); }
bool implode8kDict() const { return bool(myValue & 0x0002); }
bool implode3Trees() const { return bool(myValue & 0x0004); }
uInt32 deflateOption() const { return uInt32((myValue >> 1) & 0x0003); }
bool lzmaEosMark() const { return bool(myValue & 0x0002); }
bool useDescriptor() const { return bool(myValue & 0x0008); }
bool patchData() const { return bool(myValue & 0x0020); }
bool strongEncryption() const { return bool(myValue & 0x0040); }
bool utf8Encoding() const { return bool(myValue & 0x0800); }
bool directoryEncryption() const { return bool(myValue & 0x2000); }
private: private:
const uInt8* const myBuf; uInt16 myValue;
}; };
private: private:
@ -221,22 +302,6 @@ class ZipHandler
/** Close a ZIP file and add it to the cache */ /** Close a ZIP file and add it to the cache */
void addToCache(); void addToCache();
/** Convenience functions to read specific datatypes */
static inline uInt16 read_word(const uInt8* const buf)
{
uInt16 p0 = uInt16(buf[0]), p1 = uInt16(buf[1]);
return (p1 << 8) | p0;
}
static inline uInt32 read_dword(const uInt8* const buf)
{
return (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
}
static inline string read_string(const uInt8* const buf, size_t len = string::npos)
{
return (buf == nullptr || *buf == '\0') ? "" :
string(reinterpret_cast<const char*>(buf), len);
}
private: private:
static constexpr uInt32 DECOMPRESS_BUFSIZE = 16384; static constexpr uInt32 DECOMPRESS_BUFSIZE = 16384;
static constexpr uInt32 CACHE_SIZE = 8; // number of open files to cache static constexpr uInt32 CACHE_SIZE = 8; // number of open files to cache