michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef Zip_h michael@0: #define Zip_h michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include "Utils.h" michael@0: #include "mozilla/RefPtr.h" michael@0: michael@0: /** michael@0: * Forward declaration michael@0: */ michael@0: class ZipCollection; michael@0: michael@0: /** michael@0: * Class to handle access to Zip archive streams. The Zip archive is mapped michael@0: * in memory, and streams are direct references to that mapped memory. michael@0: * Zip files are assumed to be correctly formed. No boundary checks are michael@0: * performed, which means hand-crafted malicious Zip archives can make the michael@0: * code fail in bad ways. However, since the only intended use is to load michael@0: * libraries from Zip archives, there is no interest in making this code michael@0: * safe, since the libraries could contain malicious code anyways. michael@0: */ michael@0: class Zip: public mozilla::AtomicRefCounted michael@0: { michael@0: public: michael@0: MOZ_DECLARE_REFCOUNTED_TYPENAME(Zip) michael@0: /** michael@0: * Create a Zip instance for the given file name. Returns nullptr in case michael@0: * of failure. michael@0: */ michael@0: static mozilla::TemporaryRef Create(const char *filename); michael@0: michael@0: /** michael@0: * Create a Zip instance using the given buffer. michael@0: */ michael@0: static mozilla::TemporaryRef Create(void *buffer, size_t size) { michael@0: return Create(nullptr, buffer, size); michael@0: } michael@0: michael@0: private: michael@0: static mozilla::TemporaryRef Create(const char *filename, michael@0: void *buffer, size_t size); michael@0: michael@0: /** michael@0: * Private constructor michael@0: */ michael@0: Zip(const char *filename, void *buffer, size_t size); michael@0: michael@0: public: michael@0: /** michael@0: * Destructor michael@0: */ michael@0: ~Zip(); michael@0: michael@0: /** michael@0: * Class used to access Zip archive item streams michael@0: */ michael@0: class Stream michael@0: { michael@0: public: michael@0: /** michael@0: * Stream types michael@0: */ michael@0: enum Type { michael@0: STORE = 0, michael@0: DEFLATE = 8 michael@0: }; michael@0: michael@0: /** michael@0: * Constructor michael@0: */ michael@0: Stream(): compressedBuf(nullptr), compressedSize(0), uncompressedSize(0) michael@0: , type(STORE) { } michael@0: michael@0: /** michael@0: * Getters michael@0: */ michael@0: const void *GetBuffer() { return compressedBuf; } michael@0: size_t GetSize() { return compressedSize; } michael@0: size_t GetUncompressedSize() { return uncompressedSize; } michael@0: Type GetType() { return type; } michael@0: michael@0: /** michael@0: * Returns a z_stream for use with inflate functions using the given michael@0: * buffer as inflate output. The caller is expected to allocate enough michael@0: * memory for the Stream uncompressed size. michael@0: */ michael@0: z_stream GetZStream(void *buf) michael@0: { michael@0: z_stream zStream; michael@0: memset(&zStream, 0, sizeof(zStream)); michael@0: zStream.avail_in = compressedSize; michael@0: zStream.next_in = reinterpret_cast( michael@0: const_cast(compressedBuf)); michael@0: zStream.avail_out = uncompressedSize; michael@0: zStream.next_out = static_cast(buf); michael@0: return zStream; michael@0: } michael@0: michael@0: protected: michael@0: friend class Zip; michael@0: const void *compressedBuf; michael@0: size_t compressedSize; michael@0: size_t uncompressedSize; michael@0: Type type; michael@0: }; michael@0: michael@0: /** michael@0: * Returns a stream from the Zip archive. michael@0: */ michael@0: bool GetStream(const char *path, Stream *out) const; michael@0: michael@0: /** michael@0: * Returns the file name of the archive michael@0: */ michael@0: const char *GetName() const michael@0: { michael@0: return name; michael@0: } michael@0: michael@0: private: michael@0: /* File name of the archive */ michael@0: char *name; michael@0: /* Address where the Zip archive is mapped */ michael@0: void *mapped; michael@0: /* Size of the archive */ michael@0: size_t size; michael@0: michael@0: /** michael@0: * Strings (file names, comments, etc.) in the Zip headers are NOT zero michael@0: * terminated. This class is a helper around them. michael@0: */ michael@0: class StringBuf michael@0: { michael@0: public: michael@0: /** michael@0: * Constructor michael@0: */ michael@0: StringBuf(const char *buf, size_t length): buf(buf), length(length) { } michael@0: michael@0: /** michael@0: * Returns whether the string has the same content as the given zero michael@0: * terminated string. michael@0: */ michael@0: bool Equals(const char *str) const michael@0: { michael@0: return strncmp(str, buf, length) == 0; michael@0: } michael@0: michael@0: private: michael@0: const char *buf; michael@0: size_t length; michael@0: }; michael@0: michael@0: /* All the following types need to be packed */ michael@0: #pragma pack(1) michael@0: public: michael@0: /** michael@0: * A Zip archive is an aggregate of entities which all start with a michael@0: * signature giving their type. This template is to be used as a base michael@0: * class for these entities. michael@0: */ michael@0: template michael@0: class SignedEntity michael@0: { michael@0: public: michael@0: /** michael@0: * Equivalent to reinterpret_cast(buf), with an additional michael@0: * check of the signature. michael@0: */ michael@0: static const T *validate(const void *buf) michael@0: { michael@0: const T *ret = static_cast(buf); michael@0: if (ret->signature == T::magic) michael@0: return ret; michael@0: return nullptr; michael@0: } michael@0: michael@0: SignedEntity(uint32_t magic): signature(magic) { } michael@0: private: michael@0: le_uint32 signature; michael@0: }; michael@0: michael@0: private: michael@0: /** michael@0: * Header used to describe a Local File entry. The header is followed by michael@0: * the file name and an extra field, then by the data stream. michael@0: */ michael@0: struct LocalFile: public SignedEntity michael@0: { michael@0: /* Signature for a Local File header */ michael@0: static const uint32_t magic = 0x04034b50; michael@0: michael@0: /** michael@0: * Returns the file name michael@0: */ michael@0: StringBuf GetName() const michael@0: { michael@0: return StringBuf(reinterpret_cast(this) + sizeof(*this), michael@0: filenameSize); michael@0: } michael@0: michael@0: /** michael@0: * Returns a pointer to the data associated with this header michael@0: */ michael@0: const void *GetData() const michael@0: { michael@0: return reinterpret_cast(this) + sizeof(*this) michael@0: + filenameSize + extraFieldSize; michael@0: } michael@0: michael@0: le_uint16 minVersion; michael@0: le_uint16 generalFlag; michael@0: le_uint16 compression; michael@0: le_uint16 lastModifiedTime; michael@0: le_uint16 lastModifiedDate; michael@0: le_uint32 CRC32; michael@0: le_uint32 compressedSize; michael@0: le_uint32 uncompressedSize; michael@0: le_uint16 filenameSize; michael@0: le_uint16 extraFieldSize; michael@0: }; michael@0: michael@0: /** michael@0: * In some cases, when a zip archive is created, compressed size and CRC michael@0: * are not known when writing the Local File header. In these cases, the michael@0: * 3rd bit of the general flag in the Local File header is set, and there michael@0: * is an additional header following the compressed data. michael@0: */ michael@0: struct DataDescriptor: public SignedEntity michael@0: { michael@0: /* Signature for a Data Descriptor header */ michael@0: static const uint32_t magic = 0x08074b50; michael@0: michael@0: le_uint32 CRC32; michael@0: le_uint32 compressedSize; michael@0: le_uint32 uncompressedSize; michael@0: }; michael@0: michael@0: /** michael@0: * Header used to describe a Central Directory Entry. The header is michael@0: * followed by the file name, an extra field, and a comment. michael@0: */ michael@0: struct DirectoryEntry: public SignedEntity michael@0: { michael@0: /* Signature for a Central Directory Entry header */ michael@0: static const uint32_t magic = 0x02014b50; michael@0: michael@0: /** michael@0: * Returns the file name michael@0: */ michael@0: StringBuf GetName() const michael@0: { michael@0: return StringBuf(reinterpret_cast(this) + sizeof(*this), michael@0: filenameSize); michael@0: } michael@0: michael@0: /** michael@0: * Returns the Central Directory Entry following this one. michael@0: */ michael@0: const DirectoryEntry *GetNext() const michael@0: { michael@0: return validate(reinterpret_cast(this) + sizeof(*this) michael@0: + filenameSize + extraFieldSize + fileCommentSize); michael@0: } michael@0: michael@0: le_uint16 creatorVersion; michael@0: le_uint16 minVersion; michael@0: le_uint16 generalFlag; michael@0: le_uint16 compression; michael@0: le_uint16 lastModifiedTime; michael@0: le_uint16 lastModifiedDate; michael@0: le_uint32 CRC32; michael@0: le_uint32 compressedSize; michael@0: le_uint32 uncompressedSize; michael@0: le_uint16 filenameSize; michael@0: le_uint16 extraFieldSize; michael@0: le_uint16 fileCommentSize; michael@0: le_uint16 diskNum; michael@0: le_uint16 internalAttributes; michael@0: le_uint32 externalAttributes; michael@0: le_uint32 offset; michael@0: }; michael@0: michael@0: /** michael@0: * Header used to describe the End of Central Directory Record. michael@0: */ michael@0: struct CentralDirectoryEnd: public SignedEntity michael@0: { michael@0: /* Signature for the End of Central Directory Record */ michael@0: static const uint32_t magic = 0x06054b50; michael@0: michael@0: le_uint16 diskNum; michael@0: le_uint16 startDisk; michael@0: le_uint16 recordsOnDisk; michael@0: le_uint16 records; michael@0: le_uint32 size; michael@0: le_uint32 offset; michael@0: le_uint16 commentSize; michael@0: }; michael@0: #pragma pack() michael@0: michael@0: /** michael@0: * Returns the first Directory entry michael@0: */ michael@0: const DirectoryEntry *GetFirstEntry() const; michael@0: michael@0: /* Pointer to the Local File Entry following the last one GetStream() used. michael@0: * This is used by GetStream to avoid scanning the Directory Entries when the michael@0: * requested entry is that one. */ michael@0: mutable const LocalFile *nextFile; michael@0: michael@0: /* Likewise for the next Directory entry */ michael@0: mutable const DirectoryEntry *nextDir; michael@0: michael@0: /* Pointer to the Directory entries */ michael@0: mutable const DirectoryEntry *entries; michael@0: }; michael@0: michael@0: /** michael@0: * Class for bookkeeping Zip instances michael@0: */ michael@0: class ZipCollection michael@0: { michael@0: public: michael@0: static ZipCollection Singleton; michael@0: michael@0: /** michael@0: * Get a Zip instance for the given path. If there is an existing one michael@0: * already, return that one, otherwise create a new one. michael@0: */ michael@0: static mozilla::TemporaryRef GetZip(const char *path); michael@0: michael@0: protected: michael@0: friend class Zip; michael@0: /** michael@0: * Register the given Zip instance. This method is meant to be called michael@0: * by Zip::Create. michael@0: */ michael@0: static void Register(Zip *zip); michael@0: michael@0: /** michael@0: * Forget about the given Zip instance. This method is meant to be called michael@0: * by the Zip destructor. michael@0: */ michael@0: static void Forget(Zip *zip); michael@0: michael@0: private: michael@0: /* Zip instances bookkept in this collection */ michael@0: std::vector zips; michael@0: }; michael@0: michael@0: #endif /* Zip_h */