LibreOffice Module shell (master) 1
zipfile.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#include "zipexcptn.hxx"
21#include <zipfile.hxx>
22#include <global.hxx>
23#include <types.hxx>
24#include <stream_helper.hxx>
25
26#include <malloc.h>
27#include <algorithm>
28#include <memory>
29
30#include <string.h>
31
32#include <o3tl/safeint.hxx>
33
34#include <zlib.h>
35
36namespace
37{
38
39struct LocalFileHeader
40{
41 unsigned short min_version;
42 unsigned short general_flag;
43 unsigned short compression;
44 unsigned short lastmod_time;
45 unsigned short lastmod_date;
46 unsigned crc32;
47 unsigned compressed_size;
48 unsigned uncompressed_size;
49 unsigned short filename_size;
50 unsigned short extra_field_size;
51 std::string filename;
52 std::string extra_field;
56 filename(), extra_field() {}
57};
58
60{
61 unsigned short creator_version;
62 unsigned short min_version;
63 unsigned short general_flag;
64 unsigned short compression;
65 unsigned short lastmod_time;
66 unsigned short lastmod_date;
67 unsigned crc32;
68 unsigned compressed_size;
69 unsigned uncompressed_size;
70 unsigned short filename_size;
71 unsigned short extra_field_size;
72 unsigned short file_comment_size;
73 unsigned short disk_num;
74 unsigned short internal_attr;
75 unsigned external_attr;
76 unsigned offset;
77 std::string filename;
78 std::string extra_field;
79 std::string file_comment;
85};
86
88{
89 unsigned short disk_num;
90 unsigned short cdir_disk;
91 unsigned short disk_entries;
92 unsigned short cdir_entries;
93 unsigned cdir_size;
94 unsigned cdir_offset;
95 unsigned short comment_size;
96 std::string comment;
100};
101
102#define CDIR_ENTRY_SIG 0x02014b50
103#define LOC_FILE_HEADER_SIG 0x04034b50
104#define CDIR_END_SIG 0x06054b50
105
106// This little lot performs in a truly appalling way without
107// buffering eg. on an IStream.
108
109unsigned short readShort(StreamInterface *stream)
110{
111 if (!stream || stream->stell() == -1)
112 throw IOException(-1);
113 unsigned short tmpBuf;
114 unsigned long numBytesRead = stream->sread(
115 reinterpret_cast<unsigned char *>( &tmpBuf ), 2);
116 if (numBytesRead != 2)
117 throw IOException(-1);
118 return tmpBuf;
119}
120
121unsigned readInt(StreamInterface *stream)
122{
123 if (!stream || stream->stell() == -1)
124 throw IOException(-1);
125 unsigned tmpBuf;
126 unsigned long numBytesRead = stream->sread(
127 reinterpret_cast<unsigned char *>( &tmpBuf ), 4);
128 if (numBytesRead != 4)
129 throw IOException(-1);
130 return tmpBuf;
131}
132
133std::string readString(StreamInterface *stream, unsigned long size)
134{
135 if (!stream || stream->stell() == -1)
136 throw IOException(-1);
137 auto tmp = std::make_unique<unsigned char[]>(size);
138 unsigned long numBytesRead = stream->sread(tmp.get(), size);
139 if (numBytesRead != size)
140 {
141 throw IOException(-1);
142 }
143
144 std::string aStr(reinterpret_cast<char *>(tmp.get()), size);
145 return aStr;
146}
147
148bool readCentralDirectoryEnd(StreamInterface *stream, CentralDirectoryEnd &end)
149{
150 try
151 {
152 unsigned signature = readInt(stream);
153 if (signature != CDIR_END_SIG)
154 return false;
155
156 end.disk_num = readShort(stream);
157 end.cdir_disk = readShort(stream);
158 end.disk_entries = readShort(stream);
159 end.cdir_entries = readShort(stream);
160 end.cdir_size = readInt(stream);
161 end.cdir_offset = readInt(stream);
162 end.comment_size = readShort(stream);
163 end.comment.assign(readString(stream, end.comment_size));
164 }
165 catch (...)
166 {
167 return false;
168 }
169 return true;
170}
171
172bool readCentralDirectoryEntry(StreamInterface *stream, CentralDirectoryEntry &entry)
173{
174 try
175 {
176 unsigned signature = readInt(stream);
177 if (signature != CDIR_ENTRY_SIG)
178 return false;
179
180 entry.creator_version = readShort(stream);
181 entry.min_version = readShort(stream);
182 entry.general_flag = readShort(stream);
183 entry.compression = readShort(stream);
184 entry.lastmod_time = readShort(stream);
185 entry.lastmod_date = readShort(stream);
186 entry.crc32 = readInt(stream);
187 entry.compressed_size = readInt(stream);
188 entry.uncompressed_size = readInt(stream);
189 entry.filename_size = readShort(stream);
190 entry.extra_field_size = readShort(stream);
191 entry.file_comment_size = readShort(stream);
192 entry.disk_num = readShort(stream);
193 entry.internal_attr = readShort(stream);
194 entry.external_attr = readInt(stream);
195 entry.offset = readInt(stream);
196 entry.filename.assign(readString(stream, entry.filename_size));
197 entry.extra_field.assign(readString(stream, entry.extra_field_size));
198 entry.file_comment.assign(readString(stream, entry.file_comment_size));
199 }
200 catch (...)
201 {
202 return false;
203 }
204 return true;
205}
206
207bool readLocalFileHeader(StreamInterface *stream, LocalFileHeader &header)
208{
209 try
210 {
211 unsigned signature = readInt(stream);
212 if (signature != LOC_FILE_HEADER_SIG)
213 return false;
214
215 header.min_version = readShort(stream);
216 header.general_flag = readShort(stream);
217 header.compression = readShort(stream);
218 header.lastmod_time = readShort(stream);
219 header.lastmod_date = readShort(stream);
220 header.crc32 = readInt(stream);
221 header.compressed_size = readInt(stream);
222 header.uncompressed_size = readInt(stream);
223 header.filename_size = readShort(stream);
224 header.extra_field_size = readShort(stream);
225 header.filename.assign(readString(stream, header.filename_size));
226 header.extra_field.assign(readString(stream, header.extra_field_size));
227 }
228 catch (...)
229 {
230 return false;
231 }
232 return true;
233}
234
235bool areHeadersConsistent(const LocalFileHeader &header, const CentralDirectoryEntry &entry)
236{
237 if (header.min_version != entry.min_version)
238 return false;
239 if (header.general_flag != entry.general_flag)
240 return false;
241 if (header.compression != entry.compression)
242 return false;
243 if (!(header.general_flag & 0x08))
244 {
245 if (header.crc32 != entry.crc32)
246 return false;
247 if (header.compressed_size != entry.compressed_size)
248 return false;
249 if (header.uncompressed_size != entry.uncompressed_size)
250 return false;
251 }
252 return true;
253}
254
255#define BLOCK_SIZE 0x800
256
257bool findSignatureAtOffset(StreamInterface *stream, unsigned long nOffset)
258{
259 // read in reasonably sized chunk, and read more, to get overlapping sigs
260 unsigned char aBuffer[ BLOCK_SIZE + 4 ];
261
262 stream->sseek(nOffset, SEEK_SET);
263
264 unsigned long nBytesRead = stream->sread(aBuffer, sizeof(aBuffer));
265
266 for (long n = nBytesRead - 4; n >= 0; n--)
267 {
268 if (aBuffer[n ] == 0x50 && aBuffer[n+1] == 0x4b &&
269 aBuffer[n+2] == 0x05 && aBuffer[n+3] == 0x06)
270 { // a palpable hit ...
271 stream->sseek(nOffset + n, SEEK_SET);
272 return true;
273 }
274 }
275
276 return false;
277}
278
279bool findCentralDirectoryEnd(StreamInterface *stream)
280{
281 if (!stream)
282 return false;
283
284 stream->sseek(0,SEEK_END);
285
286 long nLength = stream->stell();
287 if (nLength == -1)
288 return false;
289
290 try
291 {
292 for (long nOffset = nLength - BLOCK_SIZE - 4;
293 nOffset > 0; nOffset -= BLOCK_SIZE)
294 {
295 if (findSignatureAtOffset(stream, nOffset))
296 return true;
297 }
298 return findSignatureAtOffset(stream, 0);
299 }
300 catch (...)
301 {
302 return false;
303 }
304}
305
306bool isZipStream(StreamInterface *stream)
307{
308 if (!findCentralDirectoryEnd(stream))
309 return false;
311 if (!readCentralDirectoryEnd(stream, end))
312 return false;
313 stream->sseek(end.cdir_offset, SEEK_SET);
315 if (!readCentralDirectoryEntry(stream, entry))
316 return false;
317 stream->sseek(entry.offset, SEEK_SET);
319 if (!readLocalFileHeader(stream, header))
320 return false;
321 if (!areHeadersConsistent(header, entry))
322 return false;
323 return true;
324}
325
326} // anonymous namespace
327
328namespace internal
329{
330
331namespace {
332
333/* for case in-sensitive string comparison */
334struct stricmp
335{
336 explicit stricmp(const std::string &str) : str_(str)
337 {}
338
339 bool operator() (const std::string &other)
340 {
341 return (0 == _stricmp(str_.c_str(), other.c_str()));
342 }
343
344 std::string str_;
345};
346
347}
348
349} // namespace internal
350
364bool ZipFile::IsZipFile(const Filepath_t& /*FileName*/)
365{
366 return true;
367}
368
369bool ZipFile::IsZipFile(void* /*stream*/)
370{
371 return true;
372}
373
374
390bool ZipFile::IsValidZipFileVersionNumber(const Filepath_t& /*FileName*/)
391{
392 return true;
393}
394
395bool ZipFile::IsValidZipFileVersionNumber(void* /* stream*/)
396{
397 return true;
398}
399
400
413ZipFile::ZipFile(const Filepath_t &FileName) :
414 m_pStream(nullptr),
415 m_bShouldFree(true)
416{
417 m_pStream = new FileStream(FileName.c_str());
418 if (!isZipStream(m_pStream))
419 {
420 delete m_pStream;
421 m_pStream = nullptr;
422 }
423}
424
427 m_bShouldFree(false)
428{
429 if (!isZipStream(stream))
430 m_pStream = nullptr;
431}
432
433
437{
438 if (m_pStream && m_bShouldFree)
439 delete m_pStream;
440}
441
447void ZipFile::GetUncompressedContent(
448 const std::string &ContentName, /*inout*/ ZipContentBuffer_t &ContentBuffer)
449{
450 if (!findCentralDirectoryEnd(m_pStream))
451 return;
453 if (!readCentralDirectoryEnd(m_pStream, end))
454 return;
455 m_pStream->sseek(end.cdir_offset, SEEK_SET);
457 while (m_pStream->stell() != -1 && o3tl::make_unsigned(m_pStream->stell()) < end.cdir_offset + end.cdir_size)
458 {
459 if (!readCentralDirectoryEntry(m_pStream, entry))
460 return;
461 if (ContentName.length() == entry.filename_size && !_stricmp(entry.filename.c_str(), ContentName.c_str()))
462 break;
463 }
464 if (ContentName.length() != entry.filename_size)
465 return;
466 if (_stricmp(entry.filename.c_str(), ContentName.c_str()))
467 return;
468 m_pStream->sseek(entry.offset, SEEK_SET);
470 if (!readLocalFileHeader(m_pStream, header))
471 return;
472 if (!areHeadersConsistent(header, entry))
473 return;
474 ContentBuffer.clear();
475 ContentBuffer = ZipContentBuffer_t(entry.uncompressed_size);
476 if (!entry.compression)
477 m_pStream->sread(reinterpret_cast<unsigned char *>(ContentBuffer.data()), entry.uncompressed_size);
478 else
479 {
480 int ret;
481 z_stream strm;
482
483 /* allocate inflate state */
484 strm.zalloc = Z_NULL;
485 strm.zfree = Z_NULL;
486 strm.opaque = Z_NULL;
487 strm.avail_in = 0;
488 strm.next_in = Z_NULL;
489 ret = inflateInit2(&strm,-MAX_WBITS);
490 if (ret != Z_OK)
491 return;
492
493 std::vector<unsigned char> tmpBuffer(entry.compressed_size);
494 if (entry.compressed_size != m_pStream->sread(tmpBuffer.data(), entry.compressed_size))
495 return;
496
497 strm.avail_in = entry.compressed_size;
498 strm.next_in = reinterpret_cast<Bytef *>(tmpBuffer.data());
499
500 strm.avail_out = entry.uncompressed_size;
501 strm.next_out = reinterpret_cast<Bytef *>(ContentBuffer.data());
502 ret = inflate(&strm, Z_FINISH);
503 switch (ret)
504 {
505 case Z_NEED_DICT:
506 case Z_DATA_ERROR:
507 case Z_MEM_ERROR:
508 (void)inflateEnd(&strm);
509 ContentBuffer.clear();
510 return;
511 }
512 (void)inflateEnd(&strm);
513 }
514}
515
519ZipFile::DirectoryPtr_t ZipFile::GetDirectory() const
520{
521 DirectoryPtr_t dir(new Directory_t());
522 if (!findCentralDirectoryEnd(m_pStream))
523 return dir;
525 if (!readCentralDirectoryEnd(m_pStream, end))
526 return dir;
527 m_pStream->sseek(end.cdir_offset, SEEK_SET);
529 while (m_pStream->stell() != -1 && o3tl::make_unsigned(m_pStream->stell()) < end.cdir_offset + end.cdir_size)
530 {
531 if (!readCentralDirectoryEntry(m_pStream, entry))
532 return dir;
533 if (entry.filename_size)
534 dir->push_back(entry.filename);
535 }
536 return dir;
537}
538
542bool ZipFile::HasContent(const std::string &ContentName) const
543{
544 //#i34314# we need to compare package content names
545 //case in-sensitive as it is not defined that such
546 //names must be handled case sensitive
547 DirectoryPtr_t dir = GetDirectory();
548
549 return std::any_of(dir->begin(), dir->end(), internal::stricmp(ContentName));
550}
551
552/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
constexpr sal_Int8 header[]
virtual unsigned long sread(unsigned char *vuf, unsigned long size)=0
virtual long stell()=0
virtual long sseek(long offset, int origin)=0
~ZipFile()
Definition: zipfile.cxx:436
ZipFile(rtl::Reference< comphelper::RefCountedMutex > aMutexHolder, css::uno::Reference< css::io::XInputStream > const &xInput, css::uno::Reference< css::uno::XComponentContext > xContext, bool bInitialise)
Reference< XOutputStream > stream
std::string Filepath_t
Definition: filepath.hxx:22
GtkMediaStream * m_pStream
sal_Int64 n
aStr
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
end
sal_uInt32 readString(const sal_uInt8 *buffer, sal_Unicode *v, sal_uInt32 maxSize)
unsigned short disk_entries
unsigned short disk_num
unsigned short cdir_entries
unsigned short cdir_disk
unsigned short comment_size
unsigned short creator_version
unsigned short min_version
unsigned short lastmod_date
unsigned short internal_attr
unsigned short extra_field_size
unsigned short filename_size
unsigned short lastmod_time
unsigned short compression
unsigned short file_comment_size
unsigned short disk_num
unsigned short general_flag
unsigned uncompressed_size
NSString * filename
unsigned short filename_size
unsigned short extra_field_size
unsigned short compression
unsigned short lastmod_time
NSString * extra_field
unsigned compressed_size
unsigned short general_flag
unsigned short min_version
unsigned short lastmod_date
std::unique_ptr< char[]> aBuffer
#define stricmp
sal_Int32 nLength
#define CDIR_END_SIG
Definition: zipfile.cxx:104
std::string str_
Definition: zipfile.cxx:344
#define LOC_FILE_HEADER_SIG
Definition: zipfile.cxx:103
#define BLOCK_SIZE
Definition: zipfile.cxx:255
#define CDIR_ENTRY_SIG
Definition: zipfile.cxx:102