1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
3
* Version: MPL 2.0 / LGPLv2.1+
5
* This Source Code Form is subject to the terms of the Mozilla Public
6
* License, v. 2.0. If a copy of the MPL was not distributed with this
7
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
9
* Major Contributor(s):
10
* Copyright (C) 2012 Fridrich Strba (fridrich.strba@bluewin.ch)
12
* For minor contributions see the git repository.
14
* Alternatively, the contents of this file may be used under the terms
15
* of the GNU Lesser General Public License Version 2.1 or later
16
* (LGPLv2.1+), in which case the provisions of the LGPLv2.1+ are
17
* applicable instead of those above.
19
* For further information visit http://libwpd.sourceforge.net
26
#ifdef BUILD_ZIP_STREAM
32
#include "WPXZipStream.h"
33
#include "WPXStreamImplementation.h"
41
struct LocalFileHeader
43
unsigned short min_version;
44
unsigned short general_flag;
45
unsigned short compression;
46
unsigned short lastmod_time;
47
unsigned short lastmod_date;
49
unsigned compressed_size;
50
unsigned uncompressed_size;
51
unsigned short filename_size;
52
unsigned short extra_field_size;
54
std::string extra_field;
56
: min_version(0), general_flag(0), compression(0), lastmod_time(0), lastmod_date(0),
57
crc32(0), compressed_size(0), uncompressed_size(0), filename_size(0), extra_field_size(0),
58
filename(), extra_field() {}
62
struct CentralDirectoryEntry
64
unsigned short creator_version;
65
unsigned short min_version;
66
unsigned short general_flag;
67
unsigned short compression;
68
unsigned short lastmod_time;
69
unsigned short lastmod_date;
71
unsigned compressed_size;
72
unsigned uncompressed_size;
73
unsigned short filename_size;
74
unsigned short extra_field_size;
75
unsigned short file_comment_size;
76
unsigned short disk_num;
77
unsigned short internal_attr;
78
unsigned external_attr;
81
std::string extra_field;
82
std::string file_comment;
83
CentralDirectoryEntry()
84
: creator_version(0), min_version(0), general_flag(0), compression(0), lastmod_time(0),
85
lastmod_date(0), crc32(0), compressed_size(0), uncompressed_size(0), filename_size(0),
86
extra_field_size(0), file_comment_size(0), disk_num(0), internal_attr(0),
87
external_attr(0), offset(0), filename(), extra_field(), file_comment() {}
88
~CentralDirectoryEntry() {}
91
struct CentralDirectoryEnd
93
unsigned short disk_num;
94
unsigned short cdir_disk;
95
unsigned short disk_entries;
96
unsigned short cdir_entries;
99
unsigned short comment_size;
101
CentralDirectoryEnd()
102
: disk_num(0), cdir_disk(0), disk_entries(0), cdir_entries(0),
103
cdir_size(0), cdir_offset(0), comment_size(0), comment() {}
104
~CentralDirectoryEnd() {}
107
#define CDIR_ENTRY_SIG 0x02014b50
108
#define LOC_FILE_HEADER_SIG 0x04034b50
109
#define CDIR_END_SIG 0x06054b50
111
static unsigned char getByte(WPXInputStream *input)
113
unsigned long numBytesRead = 0;
114
const unsigned char *ret = input->read(1, numBytesRead);
115
if (numBytesRead != 1)
116
throw StreamException();
120
static unsigned short getShort(WPXInputStream *input)
122
unsigned long numBytesRead = 0;
123
const unsigned char *ret = input->read(2, numBytesRead);
124
if (numBytesRead != 2)
125
throw StreamException();
126
return (unsigned short)(ret[0]|((unsigned short)ret[1]<<8));
129
static unsigned getInt(WPXInputStream *input)
131
unsigned long numBytesRead = 0;
132
const unsigned char *ret = input->read(4, numBytesRead);
133
if (numBytesRead != 4)
134
throw StreamException();
135
return (unsigned)(ret[0]|((unsigned)ret[1]<<8)|((unsigned)ret[2]<<16)|((unsigned)ret[3]<<24));
138
static bool readCentralDirectoryEnd(WPXInputStream *input, CentralDirectoryEnd &end)
142
unsigned signature = getInt(input);
143
if (signature != CDIR_END_SIG)
146
end.disk_num = getShort(input);
147
end.cdir_disk = getShort(input);
148
end.disk_entries = getShort(input);
149
end.cdir_entries = getShort(input);
150
end.cdir_size = getInt(input);
151
end.cdir_offset = getInt(input);
152
end.comment_size = getShort(input);
154
for (unsigned short i = 0; i < end.comment_size; i++)
155
end.comment.append(1, (char)getByte(input));
164
static bool readCentralDirectoryEntry(WPXInputStream *input, CentralDirectoryEntry &entry)
168
unsigned signature = getInt(input);
169
if (signature != CDIR_ENTRY_SIG)
172
entry.creator_version = getShort(input);
173
entry.min_version = getShort(input);
174
entry.general_flag = getShort(input);
175
entry.compression = getShort(input);
176
entry.lastmod_time = getShort(input);
177
entry.lastmod_date = getShort(input);
178
entry.crc32 = getInt(input);
179
entry.compressed_size = getInt(input);
180
entry.uncompressed_size = getInt(input);
181
entry.filename_size = getShort(input);
182
entry.extra_field_size = getShort(input);
183
entry.file_comment_size = getShort(input);
184
entry.disk_num = getShort(input);
185
entry.internal_attr = getShort(input);
186
entry.external_attr = getInt(input);
187
entry.offset = getInt(input);
188
unsigned short i = 0;
189
entry.filename.clear();
190
for (i=0; i < entry.filename_size; i++)
191
entry.filename.append(1, (char)getByte(input));
192
entry.extra_field.clear();
193
for (i=0; i < entry.extra_field_size; i++)
194
entry.extra_field.append(1, (char)getByte(input));
195
entry.file_comment.clear();
196
for (i=0; i < entry.file_comment_size; i++)
197
entry.file_comment.append(1, (char)getByte(input));
206
static bool readLocalFileHeader(WPXInputStream *input, LocalFileHeader &header)
210
unsigned signature = getInt(input);
211
if (signature != LOC_FILE_HEADER_SIG)
214
header.min_version = getShort(input);
215
header.general_flag = getShort(input);
216
header.compression = getShort(input);
217
header.lastmod_time = getShort(input);
218
header.lastmod_date = getShort(input);
219
header.crc32 = getInt(input);
220
header.compressed_size = getInt(input);
221
header.uncompressed_size = getInt(input);
222
header.filename_size = getShort(input);
223
header.extra_field_size = getShort(input);
224
unsigned short i = 0;
225
header.filename.clear();
226
for (i=0; i < header.filename_size; i++)
227
header.filename.append(1, (char)getByte(input));
228
header.extra_field.clear();
229
for (i=0; i < header.extra_field_size; i++)
230
header.extra_field.append(1, (char)getByte(input));
239
static bool areHeadersConsistent(const LocalFileHeader &header, const CentralDirectoryEntry &entry)
241
if (header.min_version != entry.min_version)
243
if (header.general_flag != entry.general_flag)
245
if (header.compression != entry.compression)
247
if (!(header.general_flag & 0x08))
249
if (header.crc32 != entry.crc32)
251
if (header.compressed_size != entry.compressed_size)
253
if (header.uncompressed_size != entry.uncompressed_size)
259
static bool findCentralDirectoryEnd(WPXInputStream *input)
261
if (input->seek(-1024, WPX_SEEK_END))
262
input->seek(0, WPX_SEEK_SET);
266
while (!input->atEOS())
268
unsigned signature = getInt(input);
269
if (signature == CDIR_END_SIG)
271
input->seek(-4, WPX_SEEK_CUR);
275
input->seek(-3, WPX_SEEK_CUR);
285
static bool findDataStream(WPXInputStream *input, CentralDirectoryEntry &entry, const char *name)
287
size_t name_size = strlen(name);
288
if (!findCentralDirectoryEnd(input))
290
CentralDirectoryEnd end;
291
if (!readCentralDirectoryEnd(input, end))
293
input->seek(end.cdir_offset, WPX_SEEK_SET);
294
while (!input->atEOS() && (unsigned)input->tell() < end.cdir_offset + end.cdir_size)
296
if (!readCentralDirectoryEntry(input, entry))
298
if (name_size == entry.filename_size && entry.filename == name)
301
if (name_size != entry.filename_size)
303
if (entry.filename != name)
305
input->seek(entry.offset, WPX_SEEK_SET);
306
LocalFileHeader header;
307
if (!readLocalFileHeader(input, header))
309
if (!areHeadersConsistent(header, entry))
314
} // anonymous namespace
316
bool WPXZipStream::isZipFile(WPXInputStream *input)
318
// look for central directory end
319
if (!findCentralDirectoryEnd(input))
321
CentralDirectoryEnd end;
322
if (!readCentralDirectoryEnd(input, end))
324
input->seek(end.cdir_offset, WPX_SEEK_SET);
325
// read first entry in the central directory
326
CentralDirectoryEntry entry;
327
if (!readCentralDirectoryEntry(input, entry))
329
input->seek(entry.offset, WPX_SEEK_SET);
330
// read the local file header and compare with the central directory information
331
LocalFileHeader header;
332
if (!readLocalFileHeader(input, header))
334
if (!areHeadersConsistent(header, entry))
339
WPXInputStream *WPXZipStream::getSubstream(WPXInputStream *input, const char *name)
341
CentralDirectoryEntry entry;
342
if (!findDataStream(input, entry, name))
344
if (!entry.compressed_size)
346
unsigned long numBytesRead = 0;
347
unsigned char *compressedData = const_cast<unsigned char *>(input->read(entry.compressed_size, numBytesRead));
348
if (numBytesRead != entry.compressed_size)
350
if (!entry.compression)
351
return new WPXStringStream(compressedData, (unsigned)numBytesRead);
357
/* allocate inflate state */
358
strm.zalloc = Z_NULL;
360
strm.opaque = Z_NULL;
362
strm.next_in = Z_NULL;
363
ret = inflateInit2(&strm,-MAX_WBITS);
367
strm.avail_in = (unsigned)numBytesRead;
368
strm.next_in = (Bytef *)compressedData;
370
std::vector<unsigned char>data(entry.uncompressed_size);
372
strm.avail_out = entry.uncompressed_size;
373
strm.next_out = reinterpret_cast<Bytef *>(&data[0]);
374
ret = inflate(&strm, Z_FINISH);
380
(void)inflateEnd(&strm);
386
(void)inflateEnd(&strm);
387
return new WPXStringStream(&data[0], (unsigned)data.size());
391
#endif /* BUILD_ZIP_STREAM */
393
/* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */