Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* libcdr
3 : * Version: MPL 1.1 / GPLv2+ / LGPLv2+
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License or as specified alternatively below. You may obtain a copy of
8 : * the License at http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * Major Contributor(s):
16 : * Copyright (C) 2012 Fridrich Strba <fridrich.strba@bluewin.ch>
17 : * Copyright (C) 2011 Eilidh McAdam <tibbylickle@gmail.com>
18 : *
19 : *
20 : * All Rights Reserved.
21 : *
22 : * For minor contributions see the git repository.
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either the GNU General Public License Version 2 or later (the "GPLv2+"), or
26 : * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"),
27 : * in which case the provisions of the GPLv2+ or the LGPLv2+ are applicable
28 : * instead of those above.
29 : */
30 :
31 :
32 : #include <string.h>
33 : #include <zlib.h>
34 : #include <map>
35 : #include <libwpd-stream/libwpd-stream.h>
36 : #include "CDRZipStream.h"
37 : #include "CDRInternalStream.h"
38 : #include "libcdr_utils.h"
39 :
40 : namespace
41 : {
42 :
43 : struct LocalFileHeader
44 : {
45 : unsigned short general_flag;
46 : unsigned short compression;
47 : unsigned crc32;
48 : unsigned compressed_size;
49 : unsigned uncompressed_size;
50 : std::string filename;
51 0 : LocalFileHeader()
52 0 : : general_flag(0), compression(0), crc32(0), compressed_size(0), uncompressed_size(0), filename() {}
53 0 : ~LocalFileHeader() {}
54 : };
55 :
56 0 : struct CentralDirectoryEntry
57 : {
58 : unsigned short general_flag;
59 : unsigned short compression;
60 : unsigned crc32;
61 : unsigned compressed_size;
62 : unsigned uncompressed_size;
63 : unsigned offset;
64 : std::string filename;
65 0 : CentralDirectoryEntry()
66 0 : : general_flag(0), compression(0), crc32(0), compressed_size(0), uncompressed_size(0), offset(0), filename() {}
67 0 : ~CentralDirectoryEntry() {}
68 : };
69 :
70 : struct CentralDirectoryEnd
71 : {
72 : unsigned cdir_size;
73 : unsigned cdir_offset;
74 0 : CentralDirectoryEnd()
75 0 : : cdir_size(0), cdir_offset(0) {}
76 0 : ~CentralDirectoryEnd() {}
77 : };
78 :
79 : } // anonymous namespace
80 :
81 : namespace libcdr
82 : {
83 :
84 : struct CDRZipStreamImpl
85 : {
86 : WPXInputStream *m_input;
87 : unsigned m_cdir_offset;
88 : std::map<std::string, CentralDirectoryEntry> m_cdir;
89 : bool m_initialized;
90 0 : CDRZipStreamImpl(WPXInputStream *input)
91 0 : : m_input(input), m_cdir_offset(0), m_cdir(), m_initialized(false) {}
92 0 : ~CDRZipStreamImpl() {}
93 :
94 : bool isZipStream();
95 : WPXInputStream *getSubstream(const char *name);
96 : private:
97 : CDRZipStreamImpl(const CDRZipStreamImpl &);
98 : CDRZipStreamImpl &operator=(const CDRZipStreamImpl &);
99 :
100 : bool findCentralDirectoryEnd();
101 : bool readCentralDirectoryEnd(CentralDirectoryEnd &end);
102 : bool readCentralDirectory(const CentralDirectoryEnd &end);
103 : bool readLocalFileHeader(LocalFileHeader &header);
104 : bool areHeadersConsistent(const LocalFileHeader &header, const CentralDirectoryEntry &entry);
105 : };
106 :
107 : } // namespace libcdr
108 :
109 :
110 : using namespace libcdr;
111 :
112 :
113 0 : libcdr::CDRZipStream::CDRZipStream(WPXInputStream *input) :
114 : WPXInputStream(),
115 0 : m_pImpl(new CDRZipStreamImpl(input))
116 : {
117 0 : }
118 :
119 0 : libcdr::CDRZipStream::~CDRZipStream()
120 : {
121 0 : if (m_pImpl)
122 0 : delete m_pImpl;
123 0 : }
124 :
125 0 : const unsigned char *libcdr::CDRZipStream::read(unsigned long numBytes, unsigned long &numBytesRead)
126 : {
127 0 : return m_pImpl->m_input->read(numBytes, numBytesRead);
128 : }
129 :
130 0 : int libcdr::CDRZipStream::seek(long offset, WPX_SEEK_TYPE seekType)
131 : {
132 0 : return m_pImpl->m_input->seek(offset, seekType);
133 : }
134 :
135 0 : long libcdr::CDRZipStream::tell()
136 : {
137 0 : return m_pImpl->m_input->tell();
138 : }
139 :
140 0 : bool libcdr::CDRZipStream::atEOS()
141 : {
142 0 : return m_pImpl->m_input->atEOS();
143 : }
144 :
145 0 : bool libcdr::CDRZipStream::isOLEStream()
146 : {
147 0 : return m_pImpl->isZipStream();
148 : }
149 :
150 0 : WPXInputStream *libcdr::CDRZipStream::getDocumentOLEStream(const char *name)
151 : {
152 0 : if (!m_pImpl->isZipStream())
153 0 : return 0;
154 0 : return m_pImpl->getSubstream(name);
155 : }
156 :
157 : #define CDIR_ENTRY_SIG 0x02014b50
158 : #define LOC_FILE_HEADER_SIG 0x04034b50
159 : #define CDIR_END_SIG 0x06054b50
160 :
161 0 : bool libcdr::CDRZipStreamImpl::findCentralDirectoryEnd()
162 : {
163 : #if defined(LIBWPD_STREAM_VERSION_MAJOR) && defined(LIBWPD_STREAM_VERSION_MINOR) && defined(LIBWPD_STREAM_VERSION_REVISION) \
164 : && (LIBWPD_STREAM_VERSION_MAJOR > 0 || (LIBWPD_STREAM_VERSION_MAJOR == 0 && (LIBWPD_STREAM_VERSION_MINOR > 9 \
165 : || (LIBWPD_STREAM_VERSION_MINOR == 9 && LIBWPD_STREAM_VERSION_REVISION >= 5))))
166 : if (m_cdir_offset || m_input->seek(-1024, WPX_SEEK_END))
167 : #endif
168 0 : m_input->seek(m_cdir_offset, WPX_SEEK_SET);
169 : try
170 : {
171 0 : while (!m_input->atEOS())
172 : {
173 0 : unsigned signature = readU32(m_input);
174 0 : if (signature == CDIR_END_SIG)
175 : {
176 0 : m_input->seek(-4, WPX_SEEK_CUR);
177 0 : m_cdir_offset = m_input->tell();
178 0 : return true;
179 : }
180 : else
181 0 : m_input->seek(-3, WPX_SEEK_CUR);
182 : }
183 : }
184 0 : catch (...)
185 : {
186 0 : return false;
187 : }
188 0 : return false;
189 : }
190 :
191 0 : bool libcdr::CDRZipStreamImpl::isZipStream()
192 : {
193 0 : if (m_cdir_offset)
194 : {
195 0 : if(m_cdir.empty())
196 0 : return false;
197 0 : return true;
198 : }
199 0 : if (m_initialized)
200 0 : return false;
201 0 : m_initialized = true;
202 0 : if (!findCentralDirectoryEnd())
203 0 : return false;
204 0 : CentralDirectoryEnd end;
205 0 : if (!readCentralDirectoryEnd(end))
206 0 : return false;
207 0 : if (!readCentralDirectory(end))
208 0 : return false;
209 0 : CentralDirectoryEntry entry = m_cdir.begin()->second;
210 0 : m_input->seek(entry.offset, WPX_SEEK_SET);
211 0 : LocalFileHeader header;
212 0 : if (!readLocalFileHeader(header))
213 0 : return false;
214 0 : if (!areHeadersConsistent(header, entry))
215 0 : return false;
216 0 : return true;
217 : }
218 :
219 0 : bool libcdr::CDRZipStreamImpl::readCentralDirectory(const CentralDirectoryEnd &end)
220 : {
221 : try
222 : {
223 0 : m_input->seek(end.cdir_offset, WPX_SEEK_SET);
224 0 : while (!m_input->atEOS())
225 : {
226 0 : unsigned signature = readU32(m_input);
227 0 : if (signature != CDIR_ENTRY_SIG)
228 : {
229 0 : if (m_cdir.empty())
230 0 : return false;
231 : else
232 0 : return true;
233 : }
234 :
235 0 : CentralDirectoryEntry entry;
236 0 : m_input->seek(4, WPX_SEEK_CUR);
237 0 : entry.general_flag = readU16(m_input);
238 0 : entry.compression = readU16(m_input);
239 0 : m_input->seek(4, WPX_SEEK_CUR);
240 0 : entry.crc32 = readU32(m_input);
241 0 : entry.compressed_size = readU32(m_input);
242 0 : entry.uncompressed_size = readU32(m_input);
243 0 : unsigned short filename_size = readU16(m_input);
244 0 : unsigned short extra_field_size = readU16(m_input);
245 0 : unsigned short file_comment_size = readU16(m_input);
246 0 : m_input->seek(8, WPX_SEEK_CUR);
247 0 : entry.offset = readU32(m_input);
248 0 : entry.filename.clear();
249 0 : entry.filename.reserve(filename_size);
250 0 : unsigned long bytesRead = 0;
251 0 : const unsigned char *buffer = m_input->read(filename_size, bytesRead);
252 0 : entry.filename.assign((const char *)buffer, bytesRead);
253 0 : m_input->seek(extra_field_size+file_comment_size, WPX_SEEK_CUR);
254 :
255 0 : m_cdir[entry.filename] = entry;
256 0 : }
257 : }
258 0 : catch (...)
259 : {
260 0 : return false;
261 : }
262 0 : return true;
263 : }
264 :
265 0 : WPXInputStream *libcdr::CDRZipStreamImpl::getSubstream(const char *name)
266 : {
267 0 : if (m_cdir.empty())
268 0 : return 0;
269 0 : std::map<std::string, CentralDirectoryEntry>::const_iterator iter = m_cdir.lower_bound(name);
270 0 : if (iter == m_cdir.end())
271 0 : return 0;
272 0 : if (m_cdir.key_comp()(name, iter->first))
273 : {
274 0 : size_t name_length = strlen(name);
275 0 : if (iter->first.compare(0, name_length, name))
276 0 : return 0;
277 : }
278 0 : CentralDirectoryEntry entry = iter->second;
279 0 : m_input->seek(entry.offset, WPX_SEEK_SET);
280 0 : LocalFileHeader header;
281 0 : if (!readLocalFileHeader(header))
282 0 : return 0;
283 0 : if (!areHeadersConsistent(header, entry))
284 0 : return 0;
285 0 : if (!entry.compression)
286 0 : return new CDRInternalStream(m_input, entry.compressed_size);
287 : else
288 : {
289 : int ret;
290 : z_stream strm;
291 :
292 : /* allocate inflate state */
293 0 : strm.zalloc = Z_NULL;
294 0 : strm.zfree = Z_NULL;
295 0 : strm.opaque = Z_NULL;
296 0 : strm.avail_in = 0;
297 0 : strm.next_in = Z_NULL;
298 0 : ret = inflateInit2(&strm,-MAX_WBITS);
299 0 : if (ret != Z_OK)
300 0 : return 0;
301 :
302 0 : unsigned long numBytesRead = 0;
303 0 : const unsigned char *compressedData = m_input->read(entry.compressed_size, numBytesRead);
304 0 : if (numBytesRead != entry.compressed_size)
305 0 : return 0;
306 :
307 0 : strm.avail_in = numBytesRead;
308 0 : strm.next_in = (Bytef *)compressedData;
309 :
310 0 : std::vector<unsigned char>data(entry.uncompressed_size);
311 :
312 0 : strm.avail_out = entry.uncompressed_size;
313 0 : strm.next_out = reinterpret_cast<Bytef *>(&data[0]);
314 0 : ret = inflate(&strm, Z_FINISH);
315 0 : switch (ret)
316 : {
317 : case Z_NEED_DICT:
318 : case Z_DATA_ERROR:
319 : case Z_MEM_ERROR:
320 0 : (void)inflateEnd(&strm);
321 0 : data.clear();
322 0 : return 0;
323 : }
324 0 : (void)inflateEnd(&strm);
325 0 : return new CDRInternalStream(data);
326 0 : }
327 : }
328 :
329 0 : bool libcdr::CDRZipStreamImpl::readCentralDirectoryEnd(CentralDirectoryEnd &end)
330 : {
331 : try
332 : {
333 0 : unsigned signature = readU32(m_input);
334 0 : if (signature != CDIR_END_SIG)
335 0 : return false;
336 :
337 0 : m_input->seek(8, WPX_SEEK_CUR);
338 0 : end.cdir_size = readU32(m_input);
339 0 : end.cdir_offset = readU32(m_input);
340 0 : unsigned short comment_size = readU16(m_input);
341 0 : m_input->seek(comment_size, WPX_SEEK_CUR);
342 : }
343 0 : catch (...)
344 : {
345 0 : return false;
346 : }
347 0 : return true;
348 : }
349 :
350 0 : bool libcdr::CDRZipStreamImpl::readLocalFileHeader(LocalFileHeader &header)
351 : {
352 : try
353 : {
354 0 : unsigned signature = readU32(m_input);
355 0 : if (signature != LOC_FILE_HEADER_SIG)
356 0 : return false;
357 :
358 0 : m_input->seek(2, WPX_SEEK_CUR);
359 0 : header.general_flag = readU16(m_input);
360 0 : header.compression = readU16(m_input);
361 0 : m_input->seek(4, WPX_SEEK_CUR);
362 0 : header.crc32 = readU32(m_input);
363 0 : header.compressed_size = readU32(m_input);
364 0 : header.uncompressed_size = readU32(m_input);
365 0 : unsigned short filename_size = readU16(m_input);
366 0 : unsigned short extra_field_size = readU16(m_input);
367 0 : header.filename.clear();
368 0 : header.filename.reserve(filename_size);
369 0 : unsigned long bytesRead = 0;
370 0 : const unsigned char *buffer = m_input->read(filename_size, bytesRead);
371 0 : header.filename.assign((const char *)buffer, bytesRead);
372 0 : m_input->seek(extra_field_size, WPX_SEEK_CUR);
373 : }
374 0 : catch (...)
375 : {
376 0 : return false;
377 : }
378 0 : return true;
379 : }
380 :
381 0 : bool libcdr::CDRZipStreamImpl::areHeadersConsistent(const LocalFileHeader &header, const CentralDirectoryEntry &entry)
382 : {
383 0 : if (header.general_flag != entry.general_flag)
384 0 : return false;
385 0 : if (header.compression != entry.compression)
386 0 : return false;
387 0 : if (!(header.general_flag & 0x08))
388 : {
389 0 : if (header.crc32 != entry.crc32)
390 0 : return false;
391 0 : if (header.compressed_size != entry.compressed_size)
392 0 : return false;
393 0 : if (header.uncompressed_size != entry.uncompressed_size)
394 0 : return false;
395 : }
396 0 : return true;
397 : }
398 :
399 :
400 : /* vim:set shiftwidth=2 softtabstop=2 expandtab: */
|