junzip: import from DomTerm 912add15f3d0aec
[platform/upstream/libwebsockets.git] / lib / junzip.c
1 // Unzip library by Per Bothner and Joonas Pihlajamaa.
2 // See junzip.h for license and details.
3
4 #include <stdlib.h>
5 #include <string.h>
6
7 #include <zlib.h>
8
9 #include "junzip.h"
10
11 #define ZIP_CENTRAL_SIGNATURE 0
12 #define ZIP_CENTRAL_VERSION_MADE_BY 4
13 #define ZIP_CENTRAL_VERSION_NEEDED_TO_EXTRACT 6
14 #define ZIP_CENTRAL_GENERAL_PURPOSE_BIT_FLAG 8
15 #define ZIP_CENTRAL_COMPRESSION_METHOD 10
16 #define ZIP_CENTRAL_LAST_MOD_FILE_TIME 12
17 #define ZIP_CENTRAL_LAST_MOD_FILE_DATE 14
18 #define ZIP_CENTRAL_CRC32 16
19 #define ZIP_CENTRAL_COMPRESSED_SIZE 20
20 #define ZIP_CENTRAL_UNCOMPRESSED_SIZE 24
21 #define ZIP_CENTRAL_FILE_NAME_LENGTH 28
22 #define ZIP_CENTRAL_EXTRA_FIELD_LENGTH 30
23 #define ZIP_CENTRAL_FILE_COMMENT_LENGTH 32
24 #define ZIP_CENTRAL_DISK_NUMBER_START 34
25 #define ZIP_CENTRAL_INTERNAL_FILE_ATTRIBUTES 36
26 #define ZIP_CENTRAL_EXTERNAL_FILE_ATTRIBUTES 38
27 #define ZIP_CENTRAL_RELATIVE_OFFSET_OF_LOCAL_HEADER 42
28 #define ZIP_CENTRAL_DIRECTORY_LENGTH 46
29
30 #define ZIP_END_SIGNATURE_OFFSET 0
31 #define ZIP_END_DESK_NUMBER 4
32 #define ZIP_END_CENTRAL_DIRECTORY_DISK_NUMBER 6
33 #define ZIP_END_NUM_ENTRIES_THIS_DISK 8
34 #define ZIP_END_NUM_ENTRIES 10
35 #define ZIP_END_CENTRAL_DIRECTORY_SIZE 12
36 #define ZIP_END_CENTRAL_DIRECTORY_OFFSET 16
37 #define ZIP_END_ZIP_COMMENT_LENGTH 20
38 #define ZIP_END_DIRECTORY_LENGTH 22
39
40 #define get_u16(PTR) ((PTR)[0] | ((PTR)[1] << 8))
41 #define get_u32(PTR) ((PTR)[0] | ((PTR)[1]<<8) | ((PTR)[2]<<16) | ((PTR)[3]<<24))
42
43 static int
44 zf_seek_set(JZFile *zfile, size_t offset)
45 {
46     int new_position = offset;
47     if (new_position < 0 || new_position > zfile->length)
48         return -1;
49     zfile->position = new_position;
50     return 0;
51 }
52
53 static int
54 zf_seek_cur(JZFile *zfile, size_t offset)
55 {
56     int new_position = zfile->position + offset;
57     if (new_position < 0 || new_position > zfile->length)
58         return -1;
59     zfile->position = new_position;
60     return 0;
61 }
62
63 static int
64 zf_seek_end(JZFile *zfile, size_t offset)
65 {
66     int new_position = zfile->length + offset;
67     if (new_position < 0 || new_position > zfile->length)
68         return -1;
69     zfile->position = new_position;
70     return 0;
71 }
72
73 size_t zf_read(JZFile *zfile, void *buf, size_t size)
74 {
75     size_t avail = zfile->length - zfile->position;
76     if (size > avail)
77         size = avail;
78     memcpy(buf, zfile->start + zfile->position, size);
79     zfile->position += size;
80     return size;
81 }
82
83 // Read ZIP file end record. Will move within file.
84 int jzReadEndRecord(JZFile *zip) {
85     long fileSize, readBytes, i;
86
87     if(zf_seek_end(zip, -ZIP_END_DIRECTORY_LENGTH)) {
88         return Z_ERRNO;
89     }
90
91     unsigned char *ptr = zf_current(zip);
92     while (ptr[0] != 0x50 || ptr[1] != 0x4B || ptr[2] != 0x05 || ptr[3] != 0x06) {
93         if (ptr == zip->start) {
94             return Z_ERRNO;
95         }
96         ptr--;
97     }
98     zip->numEntries = get_u16(ptr + ZIP_END_NUM_ENTRIES);
99     zip->centralDirectoryOffset= get_u32(ptr + ZIP_END_CENTRAL_DIRECTORY_OFFSET);
100     if (get_u16(ptr + ZIP_END_DESK_NUMBER)
101         || get_u16(ptr + ZIP_END_CENTRAL_DIRECTORY_DISK_NUMBER)
102         || zip->numEntries != get_u16(ptr + ZIP_END_NUM_ENTRIES_THIS_DISK)) {
103         return Z_ERRNO;
104     }
105
106     return Z_OK;
107 }
108
109 // Read ZIP file global directory. Will move within file.
110 int jzReadCentralDirectory(JZFile *zip, JZRecordCallback callback) {
111     JZFileHeader header;
112     int i;
113
114     if(zf_seek_set(zip, zip->centralDirectoryOffset)) {
115         return Z_ERRNO;
116     }
117
118     for(i=0; i < zip->numEntries; i++) {
119         unsigned char *ptr = zf_current(zip);
120         if (zf_available(zip) < ZIP_CENTRAL_DIRECTORY_LENGTH) {
121             return Z_ERRNO;
122         }
123         zf_seek_cur(zip, ZIP_CENTRAL_DIRECTORY_LENGTH);
124         if (get_u32(ptr + ZIP_CENTRAL_SIGNATURE) != 0x02014B50) {
125             return Z_ERRNO;
126         }
127         // Construct JZFileHeader from global file header
128         header.compressionMethod = get_u16(ptr + ZIP_CENTRAL_COMPRESSION_METHOD);
129         header.crc32 = get_u32(ptr + ZIP_CENTRAL_CRC32);
130         header.compressedSize = get_u32(ptr + ZIP_CENTRAL_COMPRESSED_SIZE);
131         header.uncompressedSize = get_u32(ptr + ZIP_CENTRAL_UNCOMPRESSED_SIZE);
132         header.fileNameLength = get_u16(ptr + ZIP_CENTRAL_FILE_NAME_LENGTH);
133         header.extraFieldLength = get_u16(ptr + ZIP_CENTRAL_EXTRA_FIELD_LENGTH);
134         header.offset = get_u32(ptr + ZIP_CENTRAL_RELATIVE_OFFSET_OF_LOCAL_HEADER);
135
136         header.fileNameStart = zf_tell(zip);
137         if (zf_seek_cur(zip, header.fileNameLength + header.extraFieldLength + get_u16(ptr + ZIP_CENTRAL_FILE_COMMENT_LENGTH))) {
138             return Z_ERRNO;
139         }
140
141         if(!callback(zip, i, &header))
142             break; // end if callback returns zero
143     }
144
145     return Z_OK;
146 }
147
148 int jzSeekData(JZFile *zip, JZFileHeader *entry) {
149     size_t offset = entry->offset;
150     offset += ZIP_LOCAL_FILE_HEADER_LENGTH;
151     offset += entry->fileNameLength + entry->extraFieldLength;
152     if (offset < 0 || offset > zip->length)
153         return Z_STREAM_END;
154     zip->position = offset;
155     return Z_OK;
156 }
157
158 // Read data from file stream, described by header, to preallocated buffer
159 int jzReadData(JZFile *zip, JZFileHeader *header, void *buffer) {
160     unsigned char *bytes = (unsigned char *)buffer; // cast
161     long compressedLeft, uncompressedLeft;
162     z_stream strm;
163     int ret;
164
165     if(header->compressionMethod == 0) { // Store - just read it
166         if (zf_read(zip, buffer, header->uncompressedSize) <
167                 header->uncompressedSize)
168             return Z_ERRNO;
169     } else if (header->compressionMethod == 8) { // Deflate - using zlib
170         strm.zalloc = Z_NULL;
171         strm.zfree = Z_NULL;
172         strm.opaque = Z_NULL;
173
174         strm.avail_in = 0;
175         strm.next_in = Z_NULL;
176
177         // Use inflateInit2 with negative window bits to indicate raw data
178         if ((ret = inflateInit2(&strm, -MAX_WBITS)) != Z_OK)
179             return ret; // Zlib errors are negative
180
181         // Inflate compressed data
182         for (compressedLeft = header->compressedSize,
183                 uncompressedLeft = header->uncompressedSize;
184                 compressedLeft && uncompressedLeft && ret != Z_STREAM_END;
185                 compressedLeft -= strm.avail_in) {
186             // Read next chunk
187             unsigned char *ptr = zf_current(zip);
188             strm.avail_in = compressedLeft;
189             zf_seek_cur(zip, compressedLeft);
190             if(strm.avail_in == 0) {
191                 inflateEnd(&strm);
192                 return Z_ERRNO;
193             }
194
195             strm.next_in = ptr;
196             strm.avail_out = uncompressedLeft;
197             strm.next_out = bytes;
198
199             compressedLeft -= strm.avail_in; // inflate will change avail_in
200
201             ret = inflate(&strm, Z_NO_FLUSH);
202
203             if(ret == Z_STREAM_ERROR) return ret; // shouldn't happen
204
205             switch (ret) {
206                 case Z_NEED_DICT:
207                     ret = Z_DATA_ERROR;     /* and fall through */
208                 case Z_DATA_ERROR: case Z_MEM_ERROR:
209                     (void)inflateEnd(&strm);
210                     return ret;
211             }
212
213             bytes += uncompressedLeft - strm.avail_out; // bytes uncompressed
214             uncompressedLeft = strm.avail_out;
215         }
216
217         inflateEnd(&strm);
218     } else {
219         return Z_ERRNO;
220     }
221
222     return Z_OK;
223 }