FFSM++  1.1.0
French Forest Sector Model ++
unzip.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 ** Filename: unzip.cpp
3 ** Last updated [dd/mm/yyyy]: 28/01/2007
4 **
5 ** pkzip 2.0 decompression.
6 **
7 ** Some of the code has been inspired by other open source projects,
8 ** (mainly Info-Zip and Gilles Vollant's minizip).
9 ** Compression and decompression actually uses the zlib library.
10 **
11 ** Copyright (C) 2007 Angius Fabrizio. All rights reserved.
12 **
13 ** This file is part of the OSDaB project (http://osdab.sourceforge.net/).
14 **
15 ** This file may be distributed and/or modified under the terms of the
16 ** GNU General Public License version 2 as published by the Free Software
17 ** Foundation and appearing in the file LICENSE.GPL included in the
18 ** packaging of this file.
19 **
20 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
21 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
22 **
23 ** See the file LICENSE.GPL that came with this software distribution or
24 ** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information.
25 **
26 **********************************************************************/
27 
28 #include "unzip.h"
29 #include "unzip_p.h"
30 #include "zipentry_p.h"
31 
32 #include <QString>
33 #include <QStringList>
34 #include <QDir>
35 #include <QFile>
36 #include <QCoreApplication>
37 
38 // You can remove this #include if you replace the qDebug() statements.
39 #include <QtDebug>
40 
41 /*!
42  \class UnZip unzip.h
43 
44  \brief PKZip 2.0 file decompression.
45  Compatibility with later versions is not ensured as they may use
46  unsupported compression algorithms.
47  Versions after 2.7 may have an incompatible header format and thus be
48  completely incompatible.
49 */
50 
51 /*! \enum UnZip::ErrorCode The result of a decompression operation.
52  \value UnZip::Ok No error occurred.
53  \value UnZip::ZlibInit Failed to init or load the zlib library.
54  \value UnZip::ZlibError The zlib library returned some error.
55  \value UnZip::OpenFailed Unable to create or open a device.
56  \value UnZip::PartiallyCorrupted Corrupted zip archive - some files could be extracted.
57  \value UnZip::Corrupted Corrupted or invalid zip archive.
58  \value UnZip::WrongPassword Unable to decrypt a password protected file.
59  \value UnZip::NoOpenArchive No archive has been opened yet.
60  \value UnZip::FileNotFound Unable to find the requested file in the archive.
61  \value UnZip::ReadFailed Reading of a file failed.
62  \value UnZip::WriteFailed Writing of a file failed.
63  \value UnZip::SeekFailed Seek failed.
64  \value UnZip::CreateDirFailed Could not create a directory.
65  \value UnZip::InvalidDevice A null device has been passed as parameter.
66  \value UnZip::InvalidArchive This is not a valid (or supported) ZIP archive.
67  \value UnZip::HeaderConsistencyError Local header record info does not match with the central directory record info. The archive may be corrupted.
68 
69  \value UnZip::Skip Internal use only.
70  \value UnZip::SkipAll Internal use only.
71 */
72 
73 /*! \enum UnZip::ExtractionOptions Some options for the file extraction methods.
74  \value UnZip::ExtractPaths Default. Does not ignore the path of the zipped files.
75  \value UnZip::SkipPaths Default. Ignores the path of the zipped files and extracts them all to the same root directory.
76 */
77 
78 //! Local header size (excluding signature, excluding variable length fields)
79 #define UNZIP_LOCAL_HEADER_SIZE 26
80 //! Central Directory file entry size (excluding signature, excluding variable length fields)
81 #define UNZIP_CD_ENTRY_SIZE_NS 42
82 //! Data descriptor size (excluding signature)
83 #define UNZIP_DD_SIZE 12
84 //! End Of Central Directory size (including signature, excluding variable length fields)
85 #define UNZIP_EOCD_SIZE 22
86 //! Local header entry encryption header size
87 #define UNZIP_LOCAL_ENC_HEADER_SIZE 12
88 
89 // Some offsets inside a CD record (excluding signature)
90 #define UNZIP_CD_OFF_VERSION 0
91 #define UNZIP_CD_OFF_GPFLAG 4
92 #define UNZIP_CD_OFF_CMETHOD 6
93 #define UNZIP_CD_OFF_MODT 8
94 #define UNZIP_CD_OFF_MODD 10
95 #define UNZIP_CD_OFF_CRC32 12
96 #define UNZIP_CD_OFF_CSIZE 16
97 #define UNZIP_CD_OFF_USIZE 20
98 #define UNZIP_CD_OFF_NAMELEN 24
99 #define UNZIP_CD_OFF_XLEN 26
100 #define UNZIP_CD_OFF_COMMLEN 28
101 #define UNZIP_CD_OFF_LHOFFSET 38
102 
103 // Some offsets inside a local header record (excluding signature)
104 #define UNZIP_LH_OFF_VERSION 0
105 #define UNZIP_LH_OFF_GPFLAG 2
106 #define UNZIP_LH_OFF_CMETHOD 4
107 #define UNZIP_LH_OFF_MODT 6
108 #define UNZIP_LH_OFF_MODD 8
109 #define UNZIP_LH_OFF_CRC32 10
110 #define UNZIP_LH_OFF_CSIZE 14
111 #define UNZIP_LH_OFF_USIZE 18
112 #define UNZIP_LH_OFF_NAMELEN 22
113 #define UNZIP_LH_OFF_XLEN 24
114 
115 // Some offsets inside a data descriptor record (excluding signature)
116 #define UNZIP_DD_OFF_CRC32 0
117 #define UNZIP_DD_OFF_CSIZE 4
118 #define UNZIP_DD_OFF_USIZE 8
119 
120 // Some offsets inside a EOCD record
121 #define UNZIP_EOCD_OFF_ENTRIES 6
122 #define UNZIP_EOCD_OFF_CDOFF 12
123 #define UNZIP_EOCD_OFF_COMMLEN 16
124 
125 /*!
126  Max version handled by this API.
127  0x1B = 2.7 --> full compatibility only up to version 2.0 (0x14)
128  versions from 2.1 to 2.7 may use unsupported compression methods
129  versions after 2.7 may have an incompatible header format
130 */
131 #define UNZIP_VERSION 0x1B
132 //! Full compatibility granted until this version
133 #define UNZIP_VERSION_STRICT 0x14
134 
135 //! CRC32 routine
136 #define CRC32(c, b) crcTable[((int)c^b) & 0xff] ^ (c >> 8)
137 
138 //! Checks if some file has been already extracted.
139 #define UNZIP_CHECK_FOR_VALID_DATA \
140  {\
141  if (headers != 0)\
142  {\
143  qDebug() << "Corrupted zip archive. Some files might be extracted.";\
144  ec = headers->size() != 0 ? UnZip::PartiallyCorrupted : UnZip::Corrupted;\
145  break;\
146  }\
147  else\
148  {\
149  delete device;\
150  device = 0;\
151  qDebug() << "Corrupted or invalid zip archive";\
152  ec = UnZip::Corrupted;\
153  break;\
154  }\
155  }
156 
157 
158 /************************************************************************
159  Public interface
160 *************************************************************************/
161 
162 /*!
163  Creates a new Zip file decompressor.
164 */
166 {
167  d = new UnzipPrivate;
168 }
169 
170 /*!
171  Closes any open archive and releases used resources.
172 */
174 {
175  closeArchive();
176  delete d;
177 }
178 
179 /*!
180  Returns true if there is an open archive.
181 */
182 bool UnZip::isOpen() const
183 {
184  return d->device != 0;
185 }
186 
187 /*!
188  Opens a zip archive and reads the files list. Closes any previously opened archive.
189 */
190 UnZip::ErrorCode UnZip::openArchive(const QString& filename)
191 {
192  QFile* file = new QFile(filename);
193 
194  if (!file->exists()) {
195  delete file;
196  return UnZip::FileNotFound;
197  }
198 
199  if (!file->open(QIODevice::ReadOnly)) {
200  delete file;
201  return UnZip::OpenFailed;
202  }
203 
204  return openArchive(file);
205 }
206 
207 /*!
208  Opens a zip archive and reads the entries list.
209  Closes any previously opened archive.
210  \warning The class takes ownership of the device so don't delete it!
211 */
213 {
214  if (device == 0)
215  {
216  qDebug() << "Invalid device.";
217  return UnZip::InvalidDevice;
218  }
219 
220  return d->openArchive(device);
221 }
222 
223 /*!
224  Closes the archive and releases all the used resources (like cached passwords).
225 */
227 {
228  d->closeArchive();
229 }
230 
231 QString UnZip::archiveComment() const
232 {
233  if (d->device == 0)
234  return QString();
235  return d->comment;
236 }
237 
238 /*!
239  Returns a locale translated error string for a given error code.
240 */
242 {
243  switch (c)
244  {
245  case Ok: return QCoreApplication::translate("UnZip", "ZIP operation completed successfully."); break;
246  case ZlibInit: return QCoreApplication::translate("UnZip", "Failed to initialize or load zlib library."); break;
247  case ZlibError: return QCoreApplication::translate("UnZip", "zlib library error."); break;
248  case OpenFailed: return QCoreApplication::translate("UnZip", "Unable to create or open file."); break;
249  case PartiallyCorrupted: return QCoreApplication::translate("UnZip", "Partially corrupted archive. Some files might be extracted."); break;
250  case Corrupted: return QCoreApplication::translate("UnZip", "Corrupted archive."); break;
251  case WrongPassword: return QCoreApplication::translate("UnZip", "Wrong password."); break;
252  case NoOpenArchive: return QCoreApplication::translate("UnZip", "No archive has been created yet."); break;
253  case FileNotFound: return QCoreApplication::translate("UnZip", "File or directory does not exist."); break;
254  case ReadFailed: return QCoreApplication::translate("UnZip", "File read error."); break;
255  case WriteFailed: return QCoreApplication::translate("UnZip", "File write error."); break;
256  case SeekFailed: return QCoreApplication::translate("UnZip", "File seek error."); break;
257  case CreateDirFailed: return QCoreApplication::translate("UnZip", "Unable to create a directory."); break;
258  case InvalidDevice: return QCoreApplication::translate("UnZip", "Invalid device."); break;
259  case InvalidArchive: return QCoreApplication::translate("UnZip", "Invalid or incompatible zip archive."); break;
260  case HeaderConsistencyError: return QCoreApplication::translate("UnZip", "Inconsistent headers. Archive might be corrupted."); break;
261  default: ;
262  }
263 
264  return QCoreApplication::translate("UnZip", "Unknown error.");
265 }
266 
267 /*!
268  Returns true if the archive contains a file with the given path and name.
269 */
270 bool UnZip::contains(const QString& file) const
271 {
272  if (d->headers == 0)
273  return false;
274 
275  return d->headers->contains(file);
276 }
277 
278 /*!
279  Returns complete paths of files and directories in this archive.
280 */
281 QStringList UnZip::fileList() const
282 {
283  return d->headers == 0 ? QStringList() : d->headers->keys();
284 }
285 
286 /*!
287  Returns information for each (correctly parsed) entry of this archive.
288 */
289 QList<UnZip::ZipEntry> UnZip::entryList() const
290 {
291  QList<UnZip::ZipEntry> list;
292 
293  if (d->headers != 0)
294  {
295  for (QMap<QString,ZipEntryP*>::ConstIterator it = d->headers->constBegin(); it != d->headers->constEnd(); ++it)
296  {
297  const ZipEntryP* entry = it.value();
298  Q_ASSERT(entry != 0);
299 
300  ZipEntry z;
301 
302  z.filename = it.key();
303  if (!entry->comment.isEmpty())
304  z.comment = entry->comment;
305  z.compressedSize = entry->szComp;
306  z.uncompressedSize = entry->szUncomp;
307  z.crc32 = entry->crc;
308  z.lastModified = d->convertDateTime(entry->modDate, entry->modTime);
309 
310  z.compression = entry->compMethod == 0 ? NoCompression : entry->compMethod == 8 ? Deflated : UnknownCompression;
311  z.type = z.filename.endsWith("/") ? Directory : File;
312 
313  z.encrypted = entry->isEncrypted();
314 
315  list.append(z);
316  }
317  }
318 
319  return list;
320 }
321 
322 /*!
323  Extracts the whole archive to a directory.
324 */
325 UnZip::ErrorCode UnZip::extractAll(const QString& dirname, ExtractionOptions options)
326 {
327  return extractAll(QDir(dirname), options);
328 }
329 
330 /*!
331  Extracts the whole archive to a directory.
332 */
333 UnZip::ErrorCode UnZip::extractAll(const QDir& dir, ExtractionOptions options)
334 {
335  // this should only happen if we didn't call openArchive() yet
336  if (d->device == 0)
337  return NoOpenArchive;
338 
339  if (d->headers == 0)
340  return Ok;
341 
342  bool end = false;
343  for (QMap<QString,ZipEntryP*>::Iterator itr = d->headers->begin(); itr != d->headers->end(); ++itr)
344  {
345  ZipEntryP* entry = itr.value();
346  Q_ASSERT(entry != 0);
347 
348  if ((entry->isEncrypted()) && d->skipAllEncrypted)
349  continue;
350 
351  switch (d->extractFile(itr.key(), *entry, dir, options))
352  {
353  case Corrupted:
354  qDebug() << "Removing corrupted entry" << itr.key();
355  d->headers->erase(itr++);
356  if (itr == d->headers->end())
357  end = true;
358  break;
359  case CreateDirFailed:
360  break;
361  case Skip:
362  break;
363  case SkipAll:
364  d->skipAllEncrypted = true;
365  break;
366  default:
367  ;
368  }
369 
370  if (end)
371  break;
372  }
373 
374  return Ok;
375 }
376 
377 /*!
378  Extracts a single file to a directory.
379 */
380 UnZip::ErrorCode UnZip::extractFile(const QString& filename, const QString& dirname, ExtractionOptions options)
381 {
382  return extractFile(filename, QDir(dirname), options);
383 }
384 
385 /*!
386  Extracts a single file to a directory.
387 */
388 UnZip::ErrorCode UnZip::extractFile(const QString& filename, const QDir& dir, ExtractionOptions options)
389 {
390  QMap<QString,ZipEntryP*>::Iterator itr = d->headers->find(filename);
391  if (itr != d->headers->end())
392  {
393  ZipEntryP* entry = itr.value();
394  Q_ASSERT(entry != 0);
395  return d->extractFile(itr.key(), *entry, dir, options);
396  }
397 
398  return FileNotFound;
399 }
400 
401 /*!
402  Extracts a single file to a directory.
403 */
404 UnZip::ErrorCode UnZip::extractFile(const QString& filename, QIODevice* dev, ExtractionOptions options)
405 {
406  if (dev == 0)
407  return InvalidDevice;
408 
409  QMap<QString,ZipEntryP*>::Iterator itr = d->headers->find(filename);
410  if (itr != d->headers->end()) {
411  ZipEntryP* entry = itr.value();
412  Q_ASSERT(entry != 0);
413  return d->extractFile(itr.key(), *entry, dev, options);
414  }
415 
416  return FileNotFound;
417 }
418 
419 /*!
420  Extracts a list of files.
421  Stops extraction at the first error (but continues if a file does not exist in the archive).
422  */
423 UnZip::ErrorCode UnZip::extractFiles(const QStringList& filenames, const QString& dirname, ExtractionOptions options)
424 {
425  QDir dir(dirname);
426  ErrorCode ec;
427 
428  for (QStringList::ConstIterator itr = filenames.constBegin(); itr != filenames.constEnd(); ++itr)
429  {
430  ec = extractFile(*itr, dir, options);
431  if (ec == FileNotFound)
432  continue;
433  if (ec != Ok)
434  return ec;
435  }
436 
437  return Ok;
438 }
439 
440 /*!
441  Extracts a list of files.
442  Stops extraction at the first error (but continues if a file does not exist in the archive).
443  */
444 UnZip::ErrorCode UnZip::extractFiles(const QStringList& filenames, const QDir& dir, ExtractionOptions options)
445 {
446  ErrorCode ec;
447 
448  for (QStringList::ConstIterator itr = filenames.constBegin(); itr != filenames.constEnd(); ++itr)
449  {
450  ec = extractFile(*itr, dir, options);
451  if (ec == FileNotFound)
452  continue;
453  if (ec != Ok)
454  return ec;
455  }
456 
457  return Ok;
458 }
459 
460 /*!
461  Remove/replace this method to add your own password retrieval routine.
462 */
463 void UnZip::setPassword(const QString& pwd)
464 {
465  d->password = pwd;
466 }
467 
468 /*!
469  ZipEntry constructor - initialize data. Type is set to File.
470 */
472 {
473  compressedSize = uncompressedSize = crc32 = 0;
474  compression = NoCompression;
475  type = File;
476  encrypted = false;
477 }
478 
479 
480 /************************************************************************
481  Private interface
482 *************************************************************************/
483 
484 //! \internal
486 {
487  skipAllEncrypted = false;
488  headers = 0;
489  device = 0;
490 
491  uBuffer = (unsigned char*) buffer1;
492  crcTable = (quint32*) get_crc_table();
493 
494  cdOffset = eocdOffset = 0;
495  cdEntryCount = 0;
496  unsupportedEntryCount = 0;
497 }
498 
499 //! \internal Parses a Zip archive.
501 {
502  Q_ASSERT(dev != 0);
503 
504  if (device != 0)
505  closeArchive();
506 
507  device = dev;
508 
509  if (!(device->isOpen() || device->open(QIODevice::ReadOnly)))
510  {
511  delete device;
512  device = 0;
513 
514  qDebug() << "Unable to open device for reading";
515  return UnZip::OpenFailed;
516  }
517 
518  UnZip::ErrorCode ec;
519 
520  ec = seekToCentralDirectory();
521  if (ec != UnZip::Ok)
522  {
523  closeArchive();
524  return ec;
525  }
526 
527  //! \todo Ignore CD entry count? CD may be corrupted.
528  if (cdEntryCount == 0)
529  {
530  return UnZip::Ok;
531  }
532 
533  bool continueParsing = true;
534 
535  while (continueParsing)
536  {
537  if (device->read(buffer1, 4) != 4)
539 
540  if (! (buffer1[0] == 'P' && buffer1[1] == 'K' && buffer1[2] == 0x01 && buffer1[3] == 0x02) )
541  break;
542 
543  if ( (ec = parseCentralDirectoryRecord()) != UnZip::Ok )
544  break;
545  }
546 
547  if (ec != UnZip::Ok)
548  closeArchive();
549 
550  return ec;
551 }
552 
553 /*
554  \internal Parses a local header record and makes some consistency check
555  with the information stored in the Central Directory record for this entry
556  that has been previously parsed.
557  \todo Optional consistency check (as a ExtractionOptions flag)
558 
559  local file header signature 4 bytes (0x04034b50)
560  version needed to extract 2 bytes
561  general purpose bit flag 2 bytes
562  compression method 2 bytes
563  last mod file time 2 bytes
564  last mod file date 2 bytes
565  crc-32 4 bytes
566  compressed size 4 bytes
567  uncompressed size 4 bytes
568  file name length 2 bytes
569  extra field length 2 bytes
570 
571  file name (variable size)
572  extra field (variable size)
573 */
575 {
576  if (!device->seek(entry.lhOffset))
577  return UnZip::SeekFailed;
578 
579  // Test signature
580  if (device->read(buffer1, 4) != 4)
581  return UnZip::ReadFailed;
582 
583  if ((buffer1[0] != 'P') || (buffer1[1] != 'K') || (buffer1[2] != 0x03) || (buffer1[3] != 0x04))
584  return UnZip::InvalidArchive;
585 
586  if (device->read(buffer1, UNZIP_LOCAL_HEADER_SIZE) != UNZIP_LOCAL_HEADER_SIZE)
587  return UnZip::ReadFailed;
588 
589  /*
590  Check 3rd general purpose bit flag.
591 
592  "bit 3: If this bit is set, the fields crc-32, compressed size
593  and uncompressed size are set to zero in the local
594  header. The correct values are put in the data descriptor
595  immediately following the compressed data."
596  */
597  bool hasDataDescriptor = entry.hasDataDescriptor();
598 
599  bool checkFailed = false;
600 
601  if (!checkFailed)
602  checkFailed = entry.compMethod != getUShort(uBuffer, UNZIP_LH_OFF_CMETHOD);
603  if (!checkFailed)
604  checkFailed = entry.gpFlag[0] != uBuffer[UNZIP_LH_OFF_GPFLAG];
605  if (!checkFailed)
606  checkFailed = entry.gpFlag[1] != uBuffer[UNZIP_LH_OFF_GPFLAG + 1];
607  if (!checkFailed)
608  checkFailed = entry.modTime[0] != uBuffer[UNZIP_LH_OFF_MODT];
609  if (!checkFailed)
610  checkFailed = entry.modTime[1] != uBuffer[UNZIP_LH_OFF_MODT + 1];
611  if (!checkFailed)
612  checkFailed = entry.modDate[0] != uBuffer[UNZIP_LH_OFF_MODD];
613  if (!checkFailed)
614  checkFailed = entry.modDate[1] != uBuffer[UNZIP_LH_OFF_MODD + 1];
615  if (!hasDataDescriptor)
616  {
617  if (!checkFailed)
618  checkFailed = entry.crc != getULong(uBuffer, UNZIP_LH_OFF_CRC32);
619  if (!checkFailed)
620  checkFailed = entry.szComp != getULong(uBuffer, UNZIP_LH_OFF_CSIZE);
621  if (!checkFailed)
622  checkFailed = entry.szUncomp != getULong(uBuffer, UNZIP_LH_OFF_USIZE);
623  }
624 
625  if (checkFailed)
627 
628  // Check filename
629  quint16 szName = getUShort(uBuffer, UNZIP_LH_OFF_NAMELEN);
630  if (szName == 0)
632 
633  if (device->read(buffer2, szName) != szName)
634  return UnZip::ReadFailed;
635 
636  //QString filename = QString::fromAscii(buffer2, szName); // Qt4
637  QString filename = QString::fromLatin1(buffer2, szName); // Qt5
638  if (filename != path)
639  {
640  qDebug() << "Filename in local header mismatches.";
642  }
643 
644  // Skip extra field
645  quint16 szExtra = getUShort(uBuffer, UNZIP_LH_OFF_XLEN);
646  if (szExtra != 0)
647  {
648  if (!device->seek(device->pos() + szExtra))
649  return UnZip::SeekFailed;
650  }
651 
652  entry.dataOffset = device->pos();
653 
654  if (hasDataDescriptor)
655  {
656  /*
657  The data descriptor has this OPTIONAL signature: PK\7\8
658  We try to skip the compressed data relying on the size set in the
659  Central Directory record.
660  */
661  if (!device->seek(device->pos() + entry.szComp))
662  return UnZip::SeekFailed;
663 
664  // Read 4 bytes and check if there is a data descriptor signature
665  if (device->read(buffer2, 4) != 4)
666  return UnZip::ReadFailed;
667 
668  bool hasSignature = buffer2[0] == 'P' && buffer2[1] == 'K' && buffer2[2] == 0x07 && buffer2[3] == 0x08;
669  if (hasSignature)
670  {
671  if (device->read(buffer2, UNZIP_DD_SIZE) != UNZIP_DD_SIZE)
672  return UnZip::ReadFailed;
673  }
674  else
675  {
676  if (device->read(buffer2 + 4, UNZIP_DD_SIZE - 4) != UNZIP_DD_SIZE - 4)
677  return UnZip::ReadFailed;
678  }
679 
680  // DD: crc, compressed size, uncompressed size
681  if (
682  entry.crc != getULong((unsigned char*)buffer2, UNZIP_DD_OFF_CRC32) ||
683  entry.szComp != getULong((unsigned char*)buffer2, UNZIP_DD_OFF_CSIZE) ||
684  entry.szUncomp != getULong((unsigned char*)buffer2, UNZIP_DD_OFF_USIZE)
685  )
687  }
688 
689  return UnZip::Ok;
690 }
691 
692 /*! \internal Attempts to find the start of the central directory record.
693 
694  We seek the file back until we reach the "End Of Central Directory"
695  signature PK\5\6.
696 
697  end of central dir signature 4 bytes (0x06054b50)
698  number of this disk 2 bytes
699  number of the disk with the
700  start of the central directory 2 bytes
701  total number of entries in the
702  central directory on this disk 2 bytes
703  total number of entries in
704  the central directory 2 bytes
705  size of the central directory 4 bytes
706  offset of start of central
707  directory with respect to
708  the starting disk number 4 bytes
709  .ZIP file comment length 2 bytes
710  --- SIZE UNTIL HERE: UNZIP_EOCD_SIZE ---
711  .ZIP file comment (variable size)
712 */
714 {
715  qint64 length = device->size();
716  qint64 offset = length - UNZIP_EOCD_SIZE;
717 
718  if (length < UNZIP_EOCD_SIZE)
719  return UnZip::InvalidArchive;
720 
721  if (!device->seek( offset ))
722  return UnZip::SeekFailed;
723 
724  if (device->read(buffer1, UNZIP_EOCD_SIZE) != UNZIP_EOCD_SIZE)
725  return UnZip::ReadFailed;
726 
727  bool eocdFound = (buffer1[0] == 'P' && buffer1[1] == 'K' && buffer1[2] == 0x05 && buffer1[3] == 0x06);
728 
729  if (eocdFound)
730  {
731  // Zip file has no comment (the only variable length field in the EOCD record)
732  eocdOffset = offset;
733  }
734  else
735  {
736  qint64 read;
737  char* p = 0;
738 
739  offset -= UNZIP_EOCD_SIZE;
740 
741  if (offset <= 0)
742  return UnZip::InvalidArchive;
743 
744  if (!device->seek( offset ))
745  return UnZip::SeekFailed;
746 
747  while ((read = device->read(buffer1, UNZIP_EOCD_SIZE)) >= 0)
748  {
749  if ( (p = strstr(buffer1, "PK\5\6")) != 0)
750  {
751  // Seek to the start of the EOCD record so we can read it fully
752  // Yes... we could simply read the missing bytes and append them to the buffer
753  // but this is far easier so heck it!
754  device->seek( offset + (p - buffer1) );
755  eocdFound = true;
756  eocdOffset = offset + (p - buffer1);
757 
758  // Read EOCD record
759  if (device->read(buffer1, UNZIP_EOCD_SIZE) != UNZIP_EOCD_SIZE)
760  return UnZip::ReadFailed;
761 
762  break;
763  }
764 
765  offset -= UNZIP_EOCD_SIZE;
766  if (offset <= 0)
767  return UnZip::InvalidArchive;
768 
769  if (!device->seek( offset ))
770  return UnZip::SeekFailed;
771  }
772  }
773 
774  if (!eocdFound)
775  return UnZip::InvalidArchive;
776 
777  // Parse EOCD to locate CD offset
778  offset = getULong((const unsigned char*)buffer1, UNZIP_EOCD_OFF_CDOFF + 4);
779 
780  cdOffset = offset;
781 
782  cdEntryCount = getUShort((const unsigned char*)buffer1, UNZIP_EOCD_OFF_ENTRIES + 4);
783 
784  quint16 commentLength = getUShort((const unsigned char*)buffer1, UNZIP_EOCD_OFF_COMMLEN + 4);
785  if (commentLength != 0)
786  {
787  QByteArray c = device->read(commentLength);
788  if (c.count() != commentLength)
789  return UnZip::ReadFailed;
790 
791  comment = c;
792  }
793 
794  // Seek to the start of the CD record
795  if (!device->seek( cdOffset ))
796  return UnZip::SeekFailed;
797 
798  return UnZip::Ok;
799 }
800 
801 /*!
802  \internal Parses a central directory record.
803 
804  Central Directory record structure:
805 
806  [file header 1]
807  .
808  .
809  .
810  [file header n]
811  [digital signature] // PKZip 6.2 or later only
812 
813  File header:
814 
815  central file header signature 4 bytes (0x02014b50)
816  version made by 2 bytes
817  version needed to extract 2 bytes
818  general purpose bit flag 2 bytes
819  compression method 2 bytes
820  last mod file time 2 bytes
821  last mod file date 2 bytes
822  crc-32 4 bytes
823  compressed size 4 bytes
824  uncompressed size 4 bytes
825  file name length 2 bytes
826  extra field length 2 bytes
827  file comment length 2 bytes
828  disk number start 2 bytes
829  internal file attributes 2 bytes
830  external file attributes 4 bytes
831  relative offset of local header 4 bytes
832 
833  file name (variable size)
834  extra field (variable size)
835  file comment (variable size)
836 */
838 {
839  // Read CD record
840  if (device->read(buffer1, UNZIP_CD_ENTRY_SIZE_NS) != UNZIP_CD_ENTRY_SIZE_NS)
841  return UnZip::ReadFailed;
842 
843  bool skipEntry = false;
844 
845  // Get compression type so we can skip non compatible algorithms
846  quint16 compMethod = getUShort(uBuffer, UNZIP_CD_OFF_CMETHOD);
847 
848  // Get variable size fields length so we can skip the whole record
849  // if necessary
850  quint16 szName = getUShort(uBuffer, UNZIP_CD_OFF_NAMELEN);
851  quint16 szExtra = getUShort(uBuffer, UNZIP_CD_OFF_XLEN);
852  quint16 szComment = getUShort(uBuffer, UNZIP_CD_OFF_COMMLEN);
853 
854  quint32 skipLength = szName + szExtra + szComment;
855 
857 
858  if ((compMethod != 0) && (compMethod != 8))
859  {
860  qDebug() << "Unsupported compression method. Skipping file.";
861  skipEntry = true;
862  }
863 
864  // Header parsing may be a problem if version is bigger than UNZIP_VERSION
865  if (!skipEntry && buffer1[UNZIP_CD_OFF_VERSION] > UNZIP_VERSION)
866  {
867  qDebug() << "Unsupported PKZip version. Skipping file.";
868  skipEntry = true;
869  }
870 
871  if (!skipEntry && szName == 0)
872  {
873  qDebug() << "Skipping file with no name.";
874  skipEntry = true;
875  }
876 
877  if (!skipEntry && device->read(buffer2, szName) != szName)
878  {
879  ec = UnZip::ReadFailed;
880  skipEntry = true;
881  }
882 
883  if (skipEntry)
884  {
885  if (ec == UnZip::Ok)
886  {
887  if (!device->seek( device->pos() + skipLength ))
888  ec = UnZip::SeekFailed;
889 
890  unsupportedEntryCount++;
891  }
892 
893  return ec;
894  }
895 
896  //QString filename = QString::fromAscii(buffer2, szName); // Qt4
897  QString filename = QString::fromLatin1(buffer2, szName); // Qt5
898 
899  ZipEntryP* h = new ZipEntryP;
900  h->compMethod = compMethod;
901 
902  h->gpFlag[0] = buffer1[UNZIP_CD_OFF_GPFLAG];
903  h->gpFlag[1] = buffer1[UNZIP_CD_OFF_GPFLAG + 1];
904 
905  h->modTime[0] = buffer1[UNZIP_CD_OFF_MODT];
906  h->modTime[1] = buffer1[UNZIP_CD_OFF_MODT + 1];
907 
908  h->modDate[0] = buffer1[UNZIP_CD_OFF_MODD];
909  h->modDate[1] = buffer1[UNZIP_CD_OFF_MODD + 1];
910 
911  h->crc = getULong(uBuffer, UNZIP_CD_OFF_CRC32);
912  h->szComp = getULong(uBuffer, UNZIP_CD_OFF_CSIZE);
913  h->szUncomp = getULong(uBuffer, UNZIP_CD_OFF_USIZE);
914 
915  // Skip extra field (if any)
916  if (szExtra != 0)
917  {
918  if (!device->seek( device->pos() + szExtra ))
919  {
920  delete h;
921  return UnZip::SeekFailed;
922  }
923  }
924 
925  // Read comment field (if any)
926  if (szComment != 0)
927  {
928  if (device->read(buffer2, szComment) != szComment)
929  {
930  delete h;
931  return UnZip::ReadFailed;
932  }
933 
934  //h->comment = QString::fromAscii(buffer2, szComment); // Qt4
935  h->comment = QString::fromLatin1(buffer2, szComment); // Qt5
936  }
937 
938  h->lhOffset = getULong(uBuffer, UNZIP_CD_OFF_LHOFFSET);
939 
940  if (headers == 0)
941  headers = new QMap<QString, ZipEntryP*>();
942  headers->insert(filename, h);
943 
944  return UnZip::Ok;
945 }
946 
947 //! \internal Closes the archive and resets the internal status.
949 {
950  if (device == 0)
951  return;
952 
953  skipAllEncrypted = false;
954 
955  if (headers != 0)
956  {
957  qDeleteAll(*headers);
958  delete headers;
959  headers = 0;
960  }
961 
962  delete device; device = 0;
963 
964  cdOffset = eocdOffset = 0;
965  cdEntryCount = 0;
966  unsupportedEntryCount = 0;
967 
968  comment.clear();
969 }
970 
971 //! \internal
972 UnZip::ErrorCode UnzipPrivate::extractFile(const QString& path, ZipEntryP& entry, const QDir& dir, UnZip::ExtractionOptions options)
973 {
974  QString name(path);
975  QString dirname;
976  QString directory;
977 
978  int pos = name.lastIndexOf('/');
979 
980  // This entry is for a directory
981  if (pos == name.length() - 1)
982  {
983  if (options.testFlag(UnZip::SkipPaths))
984  return UnZip::Ok;
985 
986  directory = QString("%1/%2").arg(dir.absolutePath()).arg(QDir::cleanPath(name));
987  if (!createDirectory(directory))
988  {
989  qDebug() << QString("Unable to create directory: %1").arg(directory);
990  return UnZip::CreateDirFailed;
991  }
992 
993  return UnZip::Ok;
994  }
995 
996  // Extract path from entry
997  if (pos > 0)
998  {
999  // get directory part
1000  dirname = name.left(pos);
1001  if (options.testFlag(UnZip::SkipPaths))
1002  {
1003  directory = dir.absolutePath();
1004  }
1005  else
1006  {
1007  directory = QString("%1/%2").arg(dir.absolutePath()).arg(QDir::cleanPath(dirname));
1008  if (!createDirectory(directory))
1009  {
1010  qDebug() << QString("Unable to create directory: %1").arg(directory);
1011  return UnZip::CreateDirFailed;
1012  }
1013  }
1014  name = name.right(name.length() - pos - 1);
1015  } else directory = dir.absolutePath();
1016 
1017  name = QString("%1/%2").arg(directory).arg(name);
1018 
1019  QFile outFile(name);
1020 
1021  if (!outFile.open(QIODevice::WriteOnly))
1022  {
1023  qDebug() << QString("Unable to open %1 for writing").arg(name);
1024  return UnZip::OpenFailed;
1025  }
1026 
1027  //! \todo Set creation/last_modified date/time
1028 
1029  UnZip::ErrorCode ec = extractFile(path, entry, &outFile, options);
1030 
1031  outFile.close();
1032 
1033  if (ec != UnZip::Ok)
1034  {
1035  if (!outFile.remove())
1036  qDebug() << QString("Unable to remove corrupted file: %1").arg(name);
1037  }
1038 
1039  return ec;
1040 }
1041 
1042 //! \internal
1043 UnZip::ErrorCode UnzipPrivate::extractFile(const QString& path, ZipEntryP& entry, QIODevice* dev, UnZip::ExtractionOptions options)
1044 {
1045  Q_UNUSED(options);
1046  Q_ASSERT(dev != 0);
1047 
1048  if (!entry.lhEntryChecked)
1049  {
1050  UnZip::ErrorCode ec = parseLocalHeaderRecord(path, entry);
1051  entry.lhEntryChecked = true;
1052 
1053  if (ec != UnZip::Ok)
1054  return ec;
1055  }
1056 
1057  if (!device->seek(entry.dataOffset))
1058  return UnZip::SeekFailed;
1059 
1060  // Encryption keys
1061  quint32 keys[3];
1062 
1063  if (entry.isEncrypted())
1064  {
1065  UnZip::ErrorCode e = testPassword(keys, path, entry);
1066  if (e != UnZip::Ok)
1067  {
1068  qDebug() << QString("Unable to decrypt %1").arg(path);
1069  return e;
1070  }//! Encryption header size
1071  entry.szComp -= UNZIP_LOCAL_ENC_HEADER_SIZE; // remove encryption header size
1072  }
1073 
1074  if (entry.szComp == 0)
1075  {
1076  if (entry.crc != 0)
1077  return UnZip::Corrupted;
1078 
1079  return UnZip::Ok;
1080  }
1081 
1082  uInt rep = entry.szComp / UNZIP_READ_BUFFER;
1083  uInt rem = entry.szComp % UNZIP_READ_BUFFER;
1084  uInt cur = 0;
1085 
1086  // extract data
1087  qint64 read;
1088  quint64 tot = 0;
1089 
1090  quint32 myCRC = crc32(0L, Z_NULL, 0);
1091 
1092  if (entry.compMethod == 0)
1093  {
1094  while ( (read = device->read(buffer1, cur < rep ? UNZIP_READ_BUFFER : rem)) > 0 )
1095  {
1096  if (entry.isEncrypted())
1097  decryptBytes(keys, buffer1, read);
1098 
1099  myCRC = crc32(myCRC, uBuffer, read);
1100 
1101  if (dev->write(buffer1, read) != read)
1102  return UnZip::WriteFailed;
1103 
1104  cur++;
1105  tot += read;
1106 
1107  if (tot == entry.szComp)
1108  break;
1109  }
1110 
1111  if (read < 0)
1112  return UnZip::ReadFailed;
1113  }
1114  else if (entry.compMethod == 8)
1115  {
1116  /* Allocate inflate state */
1117  z_stream zstr;
1118  zstr.zalloc = Z_NULL;
1119  zstr.zfree = Z_NULL;
1120  zstr.opaque = Z_NULL;
1121  zstr.next_in = Z_NULL;
1122  zstr.avail_in = 0;
1123 
1124  int zret;
1125 
1126  // Use inflateInit2 with negative windowBits to get raw decompression
1127  if ( (zret = inflateInit2_(&zstr, -MAX_WBITS, ZLIB_VERSION, sizeof(z_stream))) != Z_OK )
1128  return UnZip::ZlibError;
1129 
1130  int szDecomp;
1131 
1132  // Decompress until deflate stream ends or end of file
1133  do
1134  {
1135  read = device->read(buffer1, cur < rep ? UNZIP_READ_BUFFER : rem);
1136  if (read == 0)
1137  break;
1138  if (read < 0)
1139  {
1140  (void)inflateEnd(&zstr);
1141  return UnZip::ReadFailed;
1142  }
1143 
1144  if (entry.isEncrypted())
1145  decryptBytes(keys, buffer1, read);
1146 
1147  cur++;
1148  tot += read;
1149 
1150  zstr.avail_in = (uInt) read;
1151  zstr.next_in = (Bytef*) buffer1;
1152 
1153 
1154  // Run inflate() on input until output buffer not full
1155  do {
1156  zstr.avail_out = UNZIP_READ_BUFFER;
1157  zstr.next_out = (Bytef*) buffer2;;
1158 
1159  zret = inflate(&zstr, Z_NO_FLUSH);
1160 
1161  switch (zret) {
1162  case Z_NEED_DICT:
1163  case Z_DATA_ERROR:
1164  case Z_MEM_ERROR:
1165  inflateEnd(&zstr);
1166  return UnZip::WriteFailed;
1167  default:
1168  ;
1169  }
1170 
1171  szDecomp = UNZIP_READ_BUFFER - zstr.avail_out;
1172  if (dev->write(buffer2, szDecomp) != szDecomp)
1173  {
1174  inflateEnd(&zstr);
1175  return UnZip::ZlibError;
1176  }
1177 
1178  myCRC = crc32(myCRC, (const Bytef*) buffer2, szDecomp);
1179 
1180  } while (zstr.avail_out == 0);
1181 
1182  }
1183  while (zret != Z_STREAM_END);
1184 
1185  inflateEnd(&zstr);
1186  }
1187 
1188  if (myCRC != entry.crc)
1189  return UnZip::Corrupted;
1190 
1191  return UnZip::Ok;
1192 }
1193 
1194 //! \internal Creates a new directory and all the needed parent directories.
1195 bool UnzipPrivate::createDirectory(const QString& path)
1196 {
1197  QDir d(path);
1198  if (!d.exists())
1199  {
1200  int sep = path.lastIndexOf("/");
1201  if (sep <= 0) return true;
1202 
1203  if (!createDirectory(path.left(sep)))
1204  return false;
1205 
1206  if (!d.mkdir(path))
1207  {
1208  qDebug() << QString("Unable to create directory: %1").arg(path);
1209  return false;
1210  }
1211  }
1212 
1213  return true;
1214 }
1215 
1216 /*!
1217  \internal Reads an quint32 (4 bytes) from a byte array starting at given offset.
1218 */
1219 quint32 UnzipPrivate::getULong(const unsigned char* data, quint32 offset) const
1220 {
1221  quint32 res = (quint32) data[offset];
1222  res |= (((quint32)data[offset+1]) << 8);
1223  res |= (((quint32)data[offset+2]) << 16);
1224  res |= (((quint32)data[offset+3]) << 24);
1225 
1226  return res;
1227 }
1228 
1229 /*!
1230  \internal Reads an quint64 (8 bytes) from a byte array starting at given offset.
1231 */
1232 quint64 UnzipPrivate::getULLong(const unsigned char* data, quint32 offset) const
1233 {
1234  quint64 res = (quint64) data[offset];
1235  res |= (((quint64)data[offset+1]) << 8);
1236  res |= (((quint64)data[offset+2]) << 16);
1237  res |= (((quint64)data[offset+3]) << 24);
1238  res |= (((quint64)data[offset+1]) << 32);
1239  res |= (((quint64)data[offset+2]) << 40);
1240  res |= (((quint64)data[offset+3]) << 48);
1241  res |= (((quint64)data[offset+3]) << 56);
1242 
1243  return res;
1244 }
1245 
1246 /*!
1247  \internal Reads an quint16 (2 bytes) from a byte array starting at given offset.
1248 */
1249 quint16 UnzipPrivate::getUShort(const unsigned char* data, quint32 offset) const
1250 {
1251  return (quint16) data[offset] | (((quint16)data[offset+1]) << 8);
1252 }
1253 
1254 /*!
1255  \internal Return the next byte in the pseudo-random sequence
1256  */
1257 int UnzipPrivate::decryptByte(quint32 key2) const
1258 {
1259  quint16 temp = ((quint16)(key2) & 0xffff) | 2;
1260  return (int)(((temp * (temp ^ 1)) >> 8) & 0xff);
1261 }
1262 
1263 /*!
1264  \internal Update the encryption keys with the next byte of plain text
1265  */
1266 void UnzipPrivate::updateKeys(quint32* keys, int c) const
1267 {
1268  keys[0] = CRC32(keys[0], c);
1269  keys[1] += keys[0] & 0xff;
1270  keys[1] = keys[1] * 134775813L + 1;
1271  keys[2] = CRC32(keys[2], ((int)keys[1]) >> 24);
1272 }
1273 
1274 /*!
1275  \internal Initialize the encryption keys and the random header according to
1276  the given password.
1277  */
1278 void UnzipPrivate::initKeys(const QString& pwd, quint32* keys) const
1279 {
1280  keys[0] = 305419896L;
1281  keys[1] = 591751049L;
1282  keys[2] = 878082192L;
1283 
1284  //QByteArray pwdBytes = pwd.toAscii(); // Qt4
1285  QByteArray pwdBytes = pwd.toLatin1(); // Qt5
1286  int sz = pwdBytes.size();
1287  const char* ascii = pwdBytes.data();
1288 
1289  for (int i=0; i<sz; ++i)
1290  updateKeys(keys, (int)ascii[i]);
1291 }
1292 
1293 /*!
1294  \internal Attempts to test a password without actually extracting a file.
1295  The \p file parameter can be used in the user interface or for debugging purposes
1296  as it is the name of the encrypted file for wich the password is being tested.
1297 */
1298 UnZip::ErrorCode UnzipPrivate::testPassword(quint32* keys, const QString& file, const ZipEntryP& header)
1299 {
1300  Q_UNUSED(file);
1301 
1302  // read encryption keys
1303  if (device->read(buffer1, 12) != 12)
1304  return UnZip::Corrupted;
1305 
1306  // Replace this code if you want to i.e. call some dialog and ask the user for a password
1307  initKeys(password, keys);
1308  if (testKeys(header, keys))
1309  return UnZip::Ok;
1310 
1311  return UnZip::Skip;
1312 }
1313 
1314 /*!
1315  \internal Tests a set of keys on the encryption header.
1316 */
1317 bool UnzipPrivate::testKeys(const ZipEntryP& header, quint32* keys)
1318 {
1319  char lastByte;
1320 
1321  // decrypt encryption header
1322  for (int i=0; i<11; ++i)
1323  updateKeys(keys, lastByte = buffer1[i] ^ decryptByte(keys[2]));
1324  updateKeys(keys, lastByte = buffer1[11] ^ decryptByte(keys[2]));
1325 
1326  // if there is an extended header (bit in the gp flag) buffer[11] is a byte from the file time
1327  // with no extended header we have to check the crc high-order byte
1328  char c = ((header.gpFlag[0] & 0x08) == 8) ? header.modTime[1] : header.crc >> 24;
1329 
1330  return (lastByte == c);
1331 }
1332 
1333 /*!
1334  \internal Decrypts an array of bytes long \p read.
1335 */
1336 void UnzipPrivate::decryptBytes(quint32* keys, char* buffer, qint64 read)
1337 {
1338  for (int i=0; i<(int)read; ++i)
1339  updateKeys(keys, buffer[i] ^= decryptByte(keys[2]));
1340 }
1341 
1342 /*!
1343  \internal Converts date and time values from ZIP format to a QDateTime object.
1344 */
1345 QDateTime UnzipPrivate::convertDateTime(const unsigned char date[2], const unsigned char time[2]) const
1346 {
1347  QDateTime dt;
1348 
1349  // Usual PKZip low-byte to high-byte order
1350 
1351  // Date: 7 bits = years from 1980, 4 bits = month, 5 bits = day
1352  quint16 year = (date[1] >> 1) & 127;
1353  quint16 month = ((date[1] << 3) & 14) | ((date[0] >> 5) & 7);
1354  quint16 day = date[0] & 31;
1355 
1356  // Time: 5 bits hour, 6 bits minutes, 5 bits seconds with a 2sec precision
1357  quint16 hour = (time[1] >> 3) & 31;
1358  quint16 minutes = ((time[1] << 3) & 56) | ((time[0] >> 5) & 7);
1359  quint16 seconds = (time[0] & 31) * 2;
1360 
1361  dt.setDate(QDate(1980 + year, month, day));
1362  dt.setTime(QTime(hour, minutes, seconds));
1363  return dt;
1364 }
quint16 getUShort(const unsigned char *data, quint32 offset) const
Definition: unzip.cpp:1249
#define UNZIP_EOCD_OFF_CDOFF
Definition: unzip.cpp:122
UnZip::ErrorCode testPassword(quint32 *keys, const QString &file, const ZipEntryP &header)
Definition: unzip.cpp:1298
bool hasDataDescriptor() const
Definition: zipentry_p.h:75
#define UNZIP_CD_OFF_CSIZE
Definition: unzip.cpp:96
QDateTime lastModified
Definition: unzip.h:100
quint16 compMethod
Definition: zipentry_p.h:64
ErrorCode extractAll(const QString &dirname, ExtractionOptions options=ExtractPaths)
Definition: unzip.cpp:325
#define UNZIP_LH_OFF_GPFLAG
Definition: unzip.cpp:105
ErrorCode
Definition: unzip.h:48
QDateTime convertDateTime(const unsigned char date[2], const unsigned char time[2]) const
Definition: unzip.cpp:1345
bool testKeys(const ZipEntryP &header, quint32 *keys)
Definition: unzip.cpp:1317
QString filename
Definition: unzip.h:93
bool isEncrypted() const
Definition: zipentry_p.h:74
#define UNZIP_DD_OFF_CRC32
Definition: unzip.cpp:116
QStringList fileList() const
Definition: unzip.cpp:281
UnZip::ErrorCode parseLocalHeaderRecord(const QString &path, ZipEntryP &entry)
Definition: unzip.cpp:574
bool skipAllEncrypted
Definition: unzip_p.h:59
bool isOpen() const
Definition: unzip.cpp:182
void closeArchive()
Definition: unzip.cpp:226
unsigned char modTime[2]
Definition: zipentry_p.h:65
quint32 lhOffset
Definition: zipentry_p.h:61
#define UNZIP_CD_OFF_CMETHOD
Definition: unzip.cpp:92
#define UNZIP_CD_OFF_LHOFFSET
Definition: unzip.cpp:101
QList< ZipEntry > entryList() const
Definition: unzip.cpp:289
bool createDirectory(const QString &path)
Definition: unzip.cpp:1195
#define UNZIP_CD_OFF_MODT
Definition: unzip.cpp:93
#define UNZIP_READ_BUFFER
Definition: unzip_p.h:49
quint32 crc32
Definition: unzip.h:98
CompressionMethod compression
Definition: unzip.h:102
QString password
Definition: unzip_p.h:57
#define UNZIP_DD_SIZE
Data descriptor size (excluding signature)
Definition: unzip.cpp:83
QString comment
Definition: unzip_p.h:82
#define UNZIP_LOCAL_ENC_HEADER_SIZE
Local header entry encryption header size.
Definition: unzip.cpp:87
QMap< QString, ZipEntryP * > * headers
Definition: unzip_p.h:61
void initKeys(const QString &pwd, quint32 *keys) const
Definition: unzip.cpp:1278
#define UNZIP_CD_ENTRY_SIZE_NS
Central Directory file entry size (excluding signature, excluding variable length fields) ...
Definition: unzip.cpp:81
bool lhEntryChecked
Definition: zipentry_p.h:72
UnzipPrivate * d
Definition: unzip.h:139
virtual ~UnZip()
Definition: unzip.cpp:173
#define UNZIP_VERSION
Definition: unzip.cpp:131
#define UNZIP_DD_OFF_USIZE
Definition: unzip.cpp:118
#define UNZIP_CHECK_FOR_VALID_DATA
Checks if some file has been already extracted.
Definition: unzip.cpp:139
#define UNZIP_LH_OFF_USIZE
Definition: unzip.cpp:111
QString comment
Definition: zipentry_p.h:70
QIODevice * device
Definition: unzip_p.h:63
void updateKeys(quint32 *keys, int c) const
Definition: unzip.cpp:1266
void decryptBytes(quint32 *keys, char *buffer, qint64 read)
Definition: unzip.cpp:1336
bool contains(const QString &file) const
Definition: unzip.cpp:270
#define UNZIP_EOCD_OFF_COMMLEN
Definition: unzip.cpp:123
quint32 uncompressedSize
Definition: unzip.h:97
UnZip::ErrorCode openArchive(QIODevice *device)
Definition: unzip.cpp:500
#define CRC32(c, b)
CRC32 routine.
Definition: unzip.cpp:136
bool encrypted
Definition: unzip.h:105
QString archiveComment() const
Definition: unzip.cpp:231
Ignores paths and extracts all the files to the same directory.
Definition: unzip.h:75
QString formatError(UnZip::ErrorCode c) const
Definition: unzip.cpp:241
#define UNZIP_LOCAL_HEADER_SIZE
Local header size (excluding signature, excluding variable length fields)
Definition: unzip.cpp:79
UnZip::ErrorCode seekToCentralDirectory()
Definition: unzip.cpp:713
#define UNZIP_CD_OFF_COMMLEN
Definition: unzip.cpp:100
#define UNZIP_CD_OFF_XLEN
Definition: unzip.cpp:99
#define UNZIP_EOCD_OFF_ENTRIES
Definition: unzip.cpp:121
#define UNZIP_LH_OFF_XLEN
Definition: unzip.cpp:113
#define UNZIP_CD_OFF_CRC32
Definition: unzip.cpp:95
quint32 compressedSize
Definition: unzip.h:96
#define UNZIP_LH_OFF_CMETHOD
Definition: unzip.cpp:106
FileType type
Definition: unzip.h:103
#define UNZIP_CD_OFF_GPFLAG
Definition: unzip.cpp:91
ErrorCode extractFile(const QString &filename, const QString &dirname, ExtractionOptions options=ExtractPaths)
Definition: unzip.cpp:380
int decryptByte(quint32 key2) const
Definition: unzip.cpp:1257
UnZip::ErrorCode extractFile(const QString &path, ZipEntryP &entry, const QDir &dir, UnZip::ExtractionOptions options)
Definition: unzip.cpp:972
QString comment
Definition: unzip.h:94
#define UNZIP_LH_OFF_MODD
Definition: unzip.cpp:108
#define UNZIP_CD_OFF_VERSION
Definition: unzip.cpp:90
ErrorCode extractFiles(const QStringList &filenames, const QString &dirname, ExtractionOptions options=ExtractPaths)
Definition: unzip.cpp:423
ErrorCode openArchive(const QString &filename)
Definition: unzip.cpp:190
quint64 getULLong(const unsigned char *data, quint32 offset) const
Definition: unzip.cpp:1232
#define UNZIP_DD_OFF_CSIZE
Definition: unzip.cpp:117
unsigned char gpFlag[2]
Definition: zipentry_p.h:63
#define UNZIP_LH_OFF_CRC32
Definition: unzip.cpp:109
quint32 szUncomp
Definition: zipentry_p.h:69
#define UNZIP_CD_OFF_NAMELEN
Definition: unzip.cpp:98
#define UNZIP_CD_OFF_MODD
Definition: unzip.cpp:94
#define UNZIP_EOCD_SIZE
End Of Central Directory size (including signature, excluding variable length fields) ...
Definition: unzip.cpp:85
UnZip::ErrorCode parseCentralDirectoryRecord()
Definition: unzip.cpp:837
unsigned char modDate[2]
Definition: zipentry_p.h:66
#define UNZIP_CD_OFF_USIZE
Definition: unzip.cpp:97
UnzipPrivate()
Definition: unzip.cpp:485
#define UNZIP_LH_OFF_MODT
Definition: unzip.cpp:107
quint32 dataOffset
Definition: zipentry_p.h:62
void setPassword(const QString &pwd)
Definition: unzip.cpp:463
#define UNZIP_LH_OFF_NAMELEN
Definition: unzip.cpp:112
quint32 getULong(const unsigned char *data, quint32 offset) const
Definition: unzip.cpp:1219
void closeArchive()
Definition: unzip.cpp:948
quint32 szComp
Definition: zipentry_p.h:68
UnZip()
Definition: unzip.cpp:165
#define UNZIP_LH_OFF_CSIZE
Definition: unzip.cpp:110
quint32 crc
Definition: zipentry_p.h:67