comparison OrthancFramework/Sources/Compression/ZipReader.cpp @ 4355:460a71988208

new class: ZipReader
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 08 Dec 2020 12:38:59 +0100
parents
children 18c94a82f3d4
comparison
equal deleted inserted replaced
4354:bcfb53d1bc56 4355:460a71988208
1 /**
2 * Orthanc - A Lightweight, RESTful DICOM Store
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
4 * Department, University Hospital of Liege, Belgium
5 * Copyright (C) 2017-2020 Osimis S.A., Belgium
6 *
7 * This program is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public License
9 * as published by the Free Software Foundation, either version 3 of
10 * the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this program. If not, see
19 * <http://www.gnu.org/licenses/>.
20 **/
21
22
23 #include "../PrecompiledHeaders.h"
24
25 #ifndef NOMINMAX
26 #define NOMINMAX
27 #endif
28
29 #include "ZipReader.h"
30
31 #include "../OrthancException.h"
32 #include "../../Resources/ThirdParty/minizip/unzip.h"
33
34 #if ORTHANC_SANDBOXED != 1
35 # include "../SystemToolbox.h"
36 #endif
37
38 #include <string.h>
39
40
41 namespace Orthanc
42 {
43 // ZPOS64_T corresponds to "uint64_t"
44
45 class ZipReader::MemoryBuffer : public boost::noncopyable
46 {
47 private:
48 const uint8_t* content_;
49 size_t size_;
50 size_t pos_;
51
52 public:
53 MemoryBuffer(const void* p,
54 size_t size) :
55 content_(reinterpret_cast<const uint8_t*>(p)),
56 size_(size),
57 pos_(0)
58 {
59 }
60
61 MemoryBuffer(const std::string& s) :
62 content_(s.empty() ? NULL : reinterpret_cast<const uint8_t*>(s.c_str())),
63 size_(s.size()),
64 pos_(0)
65 {
66 }
67
68 // Returns the number of bytes actually read
69 uLong Read(void *target,
70 uLong size)
71 {
72 if (size <= 0)
73 {
74 return 0;
75 }
76 else
77 {
78 size_t s = static_cast<size_t>(size);
79 if (s + pos_ > size_)
80 {
81 s = size_ - pos_;
82 }
83
84 if (s != 0)
85 {
86 memcpy(target, content_ + pos_, s);
87 }
88
89 pos_ += s;
90 return s;
91 }
92 }
93
94 ZPOS64_T Tell() const
95 {
96 return static_cast<ZPOS64_T>(pos_);
97 }
98
99 long Seek(ZPOS64_T offset,
100 int origin)
101 {
102 ssize_t next;
103
104 switch (origin)
105 {
106 case ZLIB_FILEFUNC_SEEK_CUR:
107 next = static_cast<ssize_t>(offset) + static_cast<ssize_t>(pos_);
108 break;
109
110 case ZLIB_FILEFUNC_SEEK_SET:
111 next = static_cast<ssize_t>(offset);
112 break;
113
114 case ZLIB_FILEFUNC_SEEK_END:
115 next = static_cast<ssize_t>(offset) + static_cast<ssize_t>(size_);
116 break;
117
118 default: // Should never occur
119 return 1; // Error
120 }
121
122 if (next < 0)
123 {
124 pos_ = 0;
125 }
126 else if (next >= static_cast<long>(size_))
127 {
128 pos_ = size_;
129 }
130 else
131 {
132 pos_ = static_cast<long>(next);
133 }
134
135 return 0;
136 }
137
138
139 static voidpf OpenWrapper(voidpf opaque,
140 const void* filename,
141 int mode)
142 {
143 // Don't return NULL to make "unzip.c" happy
144 return opaque;
145 }
146
147 static uLong ReadWrapper(voidpf opaque,
148 voidpf stream,
149 void* buf,
150 uLong size)
151 {
152 assert(opaque != NULL);
153 return reinterpret_cast<MemoryBuffer*>(opaque)->Read(buf, size);
154 }
155
156 static ZPOS64_T TellWrapper(voidpf opaque,
157 voidpf stream)
158 {
159 assert(opaque != NULL);
160 return reinterpret_cast<MemoryBuffer*>(opaque)->Tell();
161 }
162
163 static long SeekWrapper(voidpf opaque,
164 voidpf stream,
165 ZPOS64_T offset,
166 int origin)
167 {
168 assert(opaque != NULL);
169 return reinterpret_cast<MemoryBuffer*>(opaque)->Seek(offset, origin);
170 }
171
172 static int CloseWrapper(voidpf opaque,
173 voidpf stream)
174 {
175 return 0;
176 }
177
178 static int TestErrorWrapper(voidpf opaque,
179 voidpf stream)
180 {
181 return 0; // ??
182 }
183 };
184
185
186
187 ZipReader* ZipReader::CreateFromMemory(const std::string& buffer)
188 {
189 if (buffer.empty())
190 {
191 return CreateFromMemory(NULL, 0);
192 }
193 else
194 {
195 return CreateFromMemory(buffer.c_str(), buffer.size());
196 }
197 }
198
199
200 bool ZipReader::IsZipMemoryBuffer(const void* buffer,
201 size_t size)
202 {
203 if (size < 4)
204 {
205 return false;
206 }
207 else
208 {
209 const uint8_t* c = reinterpret_cast<const uint8_t*>(buffer);
210 return (c[0] == 0x50 && // 'P'
211 c[1] == 0x4b && // 'K'
212 ((c[2] == 0x03 && c[3] == 0x04) ||
213 (c[2] == 0x05 && c[3] == 0x06) ||
214 (c[2] == 0x07 && c[3] == 0x08)));
215 }
216 }
217
218
219 bool ZipReader::IsZipMemoryBuffer(const std::string& content)
220 {
221 if (content.empty())
222 {
223 return false;
224 }
225 else
226 {
227 return IsZipMemoryBuffer(content.c_str(), content.size());
228 }
229 }
230
231
232 #if ORTHANC_SANDBOXED != 1
233 bool ZipReader::IsZipFile(const std::string& path)
234 {
235 std::string content;
236 SystemToolbox::ReadFileRange(content, path, 0, 4,
237 false /* don't throw if file is too small */);
238
239 return IsZipMemoryBuffer(content);
240 }
241 #endif
242
243
244 struct ZipReader::PImpl
245 {
246 unzFile unzip_;
247 std::unique_ptr<MemoryBuffer> reader_;
248 bool done_;
249
250 PImpl() :
251 unzip_(NULL),
252 done_(true)
253 {
254 }
255 };
256
257
258 ZipReader::ZipReader() :
259 pimpl_(new PImpl)
260 {
261 }
262
263
264 ZipReader::~ZipReader()
265 {
266 if (pimpl_->unzip_ != NULL)
267 {
268 unzClose(pimpl_->unzip_);
269 pimpl_->unzip_ = NULL;
270 }
271 }
272
273
274 uint64_t ZipReader::GetFilesCount() const
275 {
276 assert(pimpl_->unzip_ != NULL);
277
278 unz_global_info64_s info;
279
280 if (unzGetGlobalInfo64(pimpl_->unzip_, &info) == 0)
281 {
282 return info.number_entry;
283 }
284 else
285 {
286 throw OrthancException(ErrorCode_BadFileFormat);
287 }
288 }
289
290
291 void ZipReader::SeekFirst()
292 {
293 assert(pimpl_->unzip_ != NULL);
294 pimpl_->done_ = (unzGoToFirstFile(pimpl_->unzip_) != 0);
295 }
296
297
298 bool ZipReader::ReadNextFile(std::string& filename,
299 std::string& content)
300 {
301 assert(pimpl_->unzip_ != NULL);
302
303 if (pimpl_->done_)
304 {
305 return false;
306 }
307 else
308 {
309 unz_file_info64_s info;
310 if (unzGetCurrentFileInfo64(pimpl_->unzip_, &info, NULL, 0, NULL, 0, NULL, 0) != 0)
311 {
312 throw OrthancException(ErrorCode_BadFileFormat);
313 }
314
315 filename.resize(info.size_filename);
316 if (!filename.empty() &&
317 unzGetCurrentFileInfo64(pimpl_->unzip_, &info, &filename[0], filename.size(), NULL, 0, NULL, 0) != 0)
318 {
319 throw OrthancException(ErrorCode_BadFileFormat);
320 }
321
322 content.resize(info.uncompressed_size);
323
324 if (!content.empty())
325 {
326 if (unzOpenCurrentFile(pimpl_->unzip_) == 0)
327 {
328 bool success = (unzReadCurrentFile(pimpl_->unzip_, &content[0], content.size()) != 0);
329
330 if (unzCloseCurrentFile(pimpl_->unzip_) != 0 ||
331 !success)
332 {
333 throw OrthancException(ErrorCode_BadFileFormat);
334 }
335 }
336 else
337 {
338 throw OrthancException(ErrorCode_BadFileFormat);
339 }
340 }
341
342 pimpl_->done_ = (unzGoToNextFile(pimpl_->unzip_) != 0);
343
344 return true;
345 }
346 }
347
348
349 ZipReader* ZipReader::CreateFromMemory(const void* buffer,
350 size_t size)
351 {
352 if (!IsZipMemoryBuffer(buffer, size))
353 {
354 throw OrthancException(ErrorCode_BadFileFormat, "The memory buffer doesn't contain a ZIP archive");
355 }
356 else
357 {
358 std::unique_ptr<ZipReader> reader(new ZipReader);
359
360 reader->pimpl_->reader_.reset(new MemoryBuffer(buffer, size));
361 if (reader->pimpl_->reader_.get() == NULL)
362 {
363 throw OrthancException(ErrorCode_InternalError);
364 }
365
366 zlib_filefunc64_def funcs;
367 memset(&funcs, 0, sizeof(funcs));
368
369 funcs.opaque = reader->pimpl_->reader_.get();
370 funcs.zopen64_file = MemoryBuffer::OpenWrapper;
371 funcs.zread_file = MemoryBuffer::ReadWrapper;
372 funcs.ztell64_file = MemoryBuffer::TellWrapper;
373 funcs.zseek64_file = MemoryBuffer::SeekWrapper;
374 funcs.zclose_file = MemoryBuffer::CloseWrapper;
375 funcs.zerror_file = MemoryBuffer::TestErrorWrapper;
376
377 reader->pimpl_->unzip_ = unzOpen2_64(NULL, &funcs);
378 if (reader->pimpl_->unzip_ == NULL)
379 {
380 throw OrthancException(ErrorCode_BadFileFormat, "Cannot open ZIP archive from memory buffer");
381 }
382 else
383 {
384 reader->SeekFirst();
385 return reader.release();
386 }
387 }
388 }
389
390
391 #if ORTHANC_SANDBOXED != 1
392 ZipReader* ZipReader::CreateFromFile(const std::string& path)
393 {
394 if (!IsZipFile(path))
395 {
396 throw OrthancException(ErrorCode_BadFileFormat, "The file doesn't contain a ZIP archive: " + path);
397 }
398 else
399 {
400 std::unique_ptr<ZipReader> reader(new ZipReader);
401
402 reader->pimpl_->unzip_ = unzOpen64(path.c_str());
403 if (reader->pimpl_->unzip_ == NULL)
404 {
405 throw OrthancException(ErrorCode_BadFileFormat, "Cannot open ZIP archive from file: " + path);
406 }
407 else
408 {
409 reader->SeekFirst();
410 return reader.release();
411 }
412 }
413 }
414 #endif
415 }