1/*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#ifndef OHOS_ABILITY_BASE_ZIP_FILE_H
17#define OHOS_ABILITY_BASE_ZIP_FILE_H
18
19#include <memory>
20#include <set>
21#include <string>
22#include <unordered_map>
23#include <vector>
24
25#include "file_mapper.h"
26#include <contrib/minizip/unzip.h>
27
28namespace panda {
29namespace ecmascript {
30class ZipFileReader;
31struct CentralDirEntry;
32struct ZipEntry;
33using ZipPos = ZPOS64_T;
34using ZipEntryMap = std::unordered_map<std::string, ZipEntry>;
35using BytePtr = Byte *;
36
37// Local file header: descript in APPNOTE-6.3.4
38//    local file header signature     4 bytes  (0x04034b50)
39//    version needed to extract       2 bytes
40//    general purpose bit flag        2 bytes
41//    compression method              2 bytes  10
42//    last mod file time              2 bytes
43//    last mod file date              2 bytes
44//    crc-32                          4 bytes
45//    compressed size                 4 bytes  22
46//    uncompressed size               4 bytes
47//    file name length                2 bytes
48//    extra field length              2 bytes  30
49struct __attribute__((packed)) LocalHeader {
50    uint32_t signature = 0;
51    uint16_t versionNeeded = 0;
52    uint16_t flags = 0;
53    uint16_t compressionMethod = 0;
54    uint16_t modifiedTime = 0;
55    uint16_t modifiedDate = 0;
56    uint32_t crc = 0;
57    uint32_t compressedSize = 0;
58    uint32_t uncompressedSize = 0;
59    uint16_t nameSize = 0;
60    uint16_t extraSize = 0;
61};
62
63// central file header
64//    Central File header:
65//    central file header signature   4 bytes  (0x02014b50)
66//    version made by                 2 bytes
67//    version needed to extract       2 bytes
68//    general purpose bit flag        2 bytes  10
69//    compression method              2 bytes
70//    last mod file time              2 bytes
71//    last mod file date              2 bytes
72//    crc-32                          4 bytes  20
73//    compressed size                 4 bytes
74//    uncompressed size               4 bytes
75//    file name length                2 bytes  30
76//    extra field length              2 bytes
77//    file comment length             2 bytes
78//    disk number start               2 bytes
79//    internal file attributes        2 bytes
80//    external file attributes        4 bytes
81//    relative offset of local header 4 bytes 46byte
82struct __attribute__((packed)) CentralDirEntry {
83    uint32_t signature = 0;
84    uint16_t versionMade = 0;
85    uint16_t versionNeeded = 0;
86    uint16_t flags = 0;  // general purpose bit flag
87    uint16_t compressionMethod = 0;
88    uint16_t modifiedTime = 0;
89    uint16_t modifiedDate = 0;
90    uint32_t crc = 0;
91    uint32_t compressedSize = 0;
92    uint32_t uncompressedSize = 0;
93    uint16_t nameSize = 0;
94    uint16_t extraSize = 0;
95    uint16_t commentSize = 0;
96    uint16_t diskNumStart = 0;
97    uint16_t internalAttr = 0;
98    uint32_t externalAttr = 0;
99    uint32_t localHeaderOffset = 0;
100};
101
102// end of central directory packed structure
103//    end of central dir signature    4 bytes  (0x06054b50)
104//    number of this disk             2 bytes
105//    number of the disk with the
106//    start of the central directory  2 bytes
107//    total number of entries in the
108//    central directory on this disk  2 bytes
109//    total number of entries in
110//    the central directory           2 bytes
111//    size of the central directory   4 bytes
112//    offset of start of central
113//    directory with respect to
114//    the starting disk number        4 bytes
115//    .ZIP file comment length        2 bytes
116struct __attribute__((packed)) EndDir {
117    uint32_t signature = 0;
118    uint16_t numDisk = 0;
119    uint16_t startDiskOfCentralDir = 0;
120    uint16_t totalEntriesInThisDisk = 0;
121    uint16_t totalEntries = 0;
122    uint32_t sizeOfCentralDir = 0;
123    uint32_t offset = 0;
124    uint16_t commentLen = 0;
125};
126
127// Data descriptor:
128//    data descriptor signature       4 bytes  (0x06054b50)
129//    crc-32                          4 bytes
130//    compressed size                 4 bytes
131//    uncompressed size               4 bytes
132// This descriptor MUST exist if bit 3 of the general purpose bit flag is set (see below).
133// It is byte aligned and immediately follows the last byte of compressed data.
134struct __attribute__((packed)) DataDesc {
135    uint32_t signature = 0;
136    uint32_t crc = 0;
137    uint32_t compressedSize = 0;
138    uint32_t uncompressedSize = 0;
139};
140
141struct ZipEntry {
142    ZipEntry() = default;
143    explicit ZipEntry(const CentralDirEntry &centralEntry);
144    ~ZipEntry() = default;  // for CodeDEX warning
145
146    uint16_t compressionMethod = 0;
147    uint32_t uncompressedSize = 0;
148    uint32_t compressedSize = 0;
149    uint32_t localHeaderOffset = 0;
150    uint32_t crc = 0;
151    uint16_t flags = 0;
152    uint16_t modifiedTime = 0;
153    uint16_t modifiedDate = 0;
154    std::string fileName;
155};
156
157struct DirTreeNode {
158    std::unordered_map<std::string, std::shared_ptr<DirTreeNode>> children;
159};
160
161// zip file extract class for bundle format.
162class ZipFile {
163public:
164    explicit ZipFile(const std::string &pathName);
165    ~ZipFile();
166    /**
167     * @brief Open zip file.
168     * @return Returns true if the zip file is successfully opened; returns false otherwise.
169     */
170    bool Open();
171    /**
172     * @brief Close zip file.
173     */
174    void Close();
175    /**
176     * @brief Set this zip content start offset and length in the zip file form pathName.
177     * @param start Indicates the zip content location start position.
178     * @param length Indicates the zip content length.
179     */
180    void SetContentLocation(const ZipPos start, const size_t length);
181    /**
182     * @brief Get all entries in the zip file.
183     * @param start Indicates the zip content location start position.
184     * @param length Indicates the zip content length.
185     * @return Returns the ZipEntryMap object cotain all entries.
186     */
187    const ZipEntryMap &GetAllEntries() const;
188    /**
189     * @brief Has entry by name.
190     * @param entryName Indicates the entry name.
191     * @return Returns true if the ZipEntry is successfully finded; returns false otherwise.
192     */
193    bool HasEntry(const std::string &entryName) const;
194
195    bool IsDirExist(const std::string &dir) const;
196    void GetAllFileList(const std::string &srcPath, std::vector<std::string> &assetList);
197    void GetChildNames(const std::string &srcPath, std::set<std::string> &fileSet);
198
199    /**
200     * @brief Get entry by name.
201     * @param entryName Indicates the entry name.
202     * @param resultEntry Indicates the obtained ZipEntry object.
203     * @return Returns true if the ZipEntry is successfully finded; returns false otherwise.
204     */
205    bool GetEntry(const std::string &entryName, ZipEntry &resultEntry) const;
206    /**
207     * @brief Get data relative offset for file.
208     * @param file Indicates the entry name.
209     * @param offset Indicates the obtained offset.
210     * @param length Indicates the length.
211     * @return Returns true if this function is successfully called; returns false otherwise.
212     */
213    bool GetDataOffsetRelative(const std::string &file, ZipPos &offset, uint32_t &length) const;
214    /**
215     * @brief Get data relative offset for file.
216     * @param file Indicates the entry name.
217     * @param dest Indicates the obtained ostream object.
218     * @return Returns true if file is successfully extracted; returns false otherwise.
219     */
220    bool ExtractFile(const std::string &file, std::ostream &dest) const;
221
222    std::unique_ptr<FileMapper> CreateFileMapper(const std::string &fileName, FileMapperType type) const;
223    bool ExtractToBufByName(const std::string &fileName, std::unique_ptr<uint8_t[]> &dataPtr,
224        size_t &len) const;
225    friend class ZipFileFriend;
226private:
227    bool GetDataOffsetRelative(const ZipEntry &zipEntry, ZipPos &offset, uint32_t &length) const;
228    /**
229     * @brief Check the EndDir object.
230     * @param endDir Indicates the EndDir object to check.
231     * @return Returns true if  successfully checked; returns false otherwise.
232     */
233    bool CheckEndDir(const EndDir &endDir) const;
234    /**
235     * @brief Parse the EndDir.
236     * @return Returns true if  successfully Parsed; returns false otherwise.
237     */
238    bool ParseEndDirectory();
239    /**
240     * @brief Parse one entry.
241     * @return Returns true if successfully parsed; returns false otherwise.
242     */
243    bool ParseOneEntry(uint8_t* &entryPtr);
244    void AddEntryToTree(const std::string &fileName);
245    /**
246     * @brief Parse all Entries.
247     * @return Returns true if successfully parsed; returns false otherwise.
248     */
249    bool ParseAllEntries();
250    /**
251     * @brief Get LocalHeader object size.
252     * @param nameSize Indicates the nameSize.
253     * @param extraSize Indicates the extraSize.
254     * @return Returns size of LocalHeader.
255     */
256    size_t GetLocalHeaderSize(const uint16_t nameSize = 0, const uint16_t extraSize = 0) const;
257    /**
258     * @brief Get entry data offset.
259     * @param zipEntry Indicates the ZipEntry object.
260     * @param extraSize Indicates the extraSize.
261     * @return Returns position.
262     */
263    ZipPos GetEntryDataOffset(const ZipEntry &zipEntry, const uint16_t extraSize) const;
264    /**
265     * @brief Check data description.
266     * @param zipEntry Indicates the ZipEntry object.
267     * @param localHeader Indicates the localHeader object.
268     * @return Returns true if successfully checked; returns false otherwise.
269     */
270    bool CheckDataDesc(const ZipEntry &zipEntry, const LocalHeader &localHeader) const;
271    /**
272     * @brief Check coherency LocalHeader object.
273     * @param zipEntry Indicates the ZipEntry object.
274     * @param extraSize Indicates the obtained size.
275     * @return Returns true if successfully checked; returns false otherwise.
276     */
277    bool CheckCoherencyLocalHeader(const ZipEntry &zipEntry, uint16_t &extraSize) const;
278    /**
279     * @brief Unzip ZipEntry object to ostream.
280     * @param zipEntry Indicates the ZipEntry object.
281     * @param extraSize Indicates the size.
282     * @param dest Indicates the obtained ostream object.
283     * @return Returns true if successfully Unzip; returns false otherwise.
284     */
285    bool UnzipWithStore(const ZipEntry &zipEntry, const uint16_t extraSize, std::ostream &dest) const;
286    /**
287     * @brief Unzip ZipEntry object to ostream.
288     * @param zipEntry Indicates the ZipEntry object.
289     * @param extraSize Indicates the size.
290     * @param dest Indicates the obtained ostream object.
291     * @return Returns true if successfully Unzip; returns false otherwise.
292     */
293    bool UnzipWithInflated(const ZipEntry &zipEntry, const uint16_t extraSize, std::ostream &dest) const;
294    /**
295     * @brief Get Entry start.
296     * @param zipEntry Indicates the ZipEntry object.
297     * @param extraSize Indicates the extra size.
298     * @return Returns true if successfully Seeked; returns false otherwise.
299     */
300    size_t GetEntryStart(const ZipEntry &zipEntry, const uint16_t extraSize) const;
301    /**
302     * @brief Init zlib stream.
303     * @param zstream Indicates the obtained z_stream object.
304     * @return Returns true if successfully init; returns false otherwise.
305     */
306    bool InitZStream(z_stream &zstream) const;
307    /**
308     * @brief Read zlib stream.
309     * @param buffer Indicates the buffer to read.
310     * @param zstream Indicates the obtained z_stream object.
311     * @param remainCompressedSize Indicates the obtained size.
312     * @return Returns true if successfully read; returns false otherwise.
313     */
314    bool ReadZStream(const BytePtr &buffer, z_stream &zstream, uint32_t &remainCompressedSize, size_t &startPos) const;
315
316    bool UnzipWithInflatedFromMMap(const ZipEntry &zipEntry, const uint16_t extraSize,
317        void *mmapDataPtr, std::unique_ptr<uint8_t[]> &dataPtr, size_t &len) const;
318
319    bool ReadZStreamFromMMap(const BytePtr &buffer, void* &dataPtr,
320        z_stream &zstream, uint32_t &remainCompressedSize) const;
321
322private:
323    std::string pathName_;
324    std::shared_ptr<ZipFileReader> zipFileReader_;
325    EndDir endDir_;
326    ZipEntryMap entriesMap_;
327    std::shared_ptr<DirTreeNode> dirRoot_;
328    // offset of central directory relative to zip file.
329    ZipPos centralDirPos_ = 0;
330    // this zip content start offset relative to zip file.
331    ZipPos fileStartPos_ = 0;
332    // this zip content length in the zip file.
333    ZipPos fileLength_ = 0;
334    bool isOpen_ = false;
335};
336}  // namespace AbilityBase
337}  // namespace OHOS
338#endif  // OHOS_ABILITY_BASE_ZIP_FILE_H
339