FFSM++  1.1.0
French Forest Sector Model ++
zip.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 ** Filename: zip.cpp
3 ** Last updated [dd/mm/yyyy]: 01/02/2007
4 **
5 ** pkzip 2.0 file compression.
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 "zip.h"
29 #include "zip_p.h"
30 #include "zipentry_p.h"
31 
32 // we only use this to seed the random number generator
33 #include <time.h>
34 
35 #include <QMap>
36 #include <QString>
37 #include <QStringList>
38 #include <QDir>
39 #include <QFile>
40 #include <QDateTime>
41 #include <QCoreApplication>
42 
43 // You can remove this #include if you replace the qDebug() statements.
44 #include <QtDebug>
45 
46 //! Local header size (including signature, excluding variable length fields)
47 #define ZIP_LOCAL_HEADER_SIZE 30
48 //! Encryption header size
49 #define ZIP_LOCAL_ENC_HEADER_SIZE 12
50 //! Data descriptor size (signature included)
51 #define ZIP_DD_SIZE_WS 16
52 //! Central Directory record size (signature included)
53 #define ZIP_CD_SIZE 46
54 //! End of Central Directory record size (signature included)
55 #define ZIP_EOCD_SIZE 22
56 
57 // Some offsets inside a local header record (signature included)
58 #define ZIP_LH_OFF_VERS 4
59 #define ZIP_LH_OFF_GPFLAG 6
60 #define ZIP_LH_OFF_CMET 8
61 #define ZIP_LH_OFF_MODT 10
62 #define ZIP_LH_OFF_MODD 12
63 #define ZIP_LH_OFF_CRC 14
64 #define ZIP_LH_OFF_CSIZE 18
65 #define ZIP_LH_OFF_USIZE 22
66 #define ZIP_LH_OFF_NAMELEN 26
67 #define ZIP_LH_OFF_XLEN 28
68 
69 // Some offsets inside a data descriptor record (including signature)
70 #define ZIP_DD_OFF_CRC32 4
71 #define ZIP_DD_OFF_CSIZE 8
72 #define ZIP_DD_OFF_USIZE 12
73 
74 // Some offsets inside a Central Directory record (including signature)
75 #define ZIP_CD_OFF_MADEBY 4
76 #define ZIP_CD_OFF_VERSION 6
77 #define ZIP_CD_OFF_GPFLAG 8
78 #define ZIP_CD_OFF_CMET 10
79 #define ZIP_CD_OFF_MODT 12
80 #define ZIP_CD_OFF_MODD 14
81 #define ZIP_CD_OFF_CRC 16
82 #define ZIP_CD_OFF_CSIZE 20
83 #define ZIP_CD_OFF_USIZE 24
84 #define ZIP_CD_OFF_NAMELEN 28
85 #define ZIP_CD_OFF_XLEN 30
86 #define ZIP_CD_OFF_COMMLEN 32
87 #define ZIP_CD_OFF_DISKSTART 34
88 #define ZIP_CD_OFF_IATTR 36
89 #define ZIP_CD_OFF_EATTR 38
90 #define ZIP_CD_OFF_LHOFF 42
91 
92 // Some offsets inside a EOCD record (including signature)
93 #define ZIP_EOCD_OFF_DISKNUM 4
94 #define ZIP_EOCD_OFF_CDDISKNUM 6
95 #define ZIP_EOCD_OFF_ENTRIES 8
96 #define ZIP_EOCD_OFF_CDENTRIES 10
97 #define ZIP_EOCD_OFF_CDSIZE 12
98 #define ZIP_EOCD_OFF_CDOFF 16
99 #define ZIP_EOCD_OFF_COMMLEN 20
100 
101 //! PKZip version for archives created by this API
102 #define ZIP_VERSION 0x14
103 
104 //! Do not store very small files as the compression headers overhead would be to big
105 #define ZIP_COMPRESSION_THRESHOLD 60
106 
107 //! This macro updates a one-char-only CRC; it's the Info-Zip macro re-adapted
108 #define CRC32(c, b) crcTable[((int)c^b) & 0xff] ^ (c >> 8)
109 
110 /*!
111  \class Zip zip.h
112 
113  \brief Zip file compression.
114 
115  Some quick usage examples.
116 
117  \verbatim
118  Suppose you have this directory structure:
119 
120  /root/dir1/
121  /root/dir1/file1.1
122  /root/dir1/file1.2
123  /root/dir1/dir1.1/
124  /root/dir1/dir1.2/file1.2.1
125 
126  EXAMPLE 1:
127  myZipInstance.addDirectory("/root/dir1");
128 
129  RESULT:
130  Beheaves like any common zip software and creates a zip file with this structure:
131 
132  dir1/
133  dir1/file1.1
134  dir1/file1.2
135  dir1/dir1.1/
136  dir1/dir1.2/file1.2.1
137 
138  EXAMPLE 2:
139  myZipInstance.addDirectory("/root/dir1", "myRoot/myFolder");
140 
141  RESULT:
142  Adds a custom root to the paths and creates a zip file with this structure:
143 
144  myRoot/myFolder/dir1/
145  myRoot/myFolder/dir1/file1.1
146  myRoot/myFolder/dir1/file1.2
147  myRoot/myFolder/dir1/dir1.1/
148  myRoot/myFolder/dir1/dir1.2/file1.2.1
149 
150  EXAMPLE 3:
151  myZipInstance.addDirectory("/root/dir1", Zip::AbsolutePaths);
152 
153  NOTE:
154  Same as calling addDirectory(SOME_PATH, PARENT_PATH_of_SOME_PATH).
155 
156  RESULT:
157  Preserves absolute paths and creates a zip file with this structure:
158 
159  /root/dir1/
160  /root/dir1/file1.1
161  /root/dir1/file1.2
162  /root/dir1/dir1.1/
163  /root/dir1/dir1.2/file1.2.1
164 
165  EXAMPLE 4:
166  myZipInstance.setPassword("hellopass");
167  myZipInstance.addDirectory("/root/dir1", "/");
168 
169  RESULT:
170  Adds and encrypts the files in /root/dir1, creating the following zip structure:
171 
172  /dir1/
173  /dir1/file1.1
174  /dir1/file1.2
175  /dir1/dir1.1/
176  /dir1/dir1.2/file1.2.1
177 
178  \endverbatim
179 */
180 
181 /*! \enum Zip::ErrorCode The result of a compression operation.
182  \value Zip::Ok No error occurred.
183  \value Zip::ZlibInit Failed to init or load the zlib library.
184  \value Zip::ZlibError The zlib library returned some error.
185  \value Zip::FileExists The file already exists and will not be overwritten.
186  \value Zip::OpenFailed Unable to create or open a device.
187  \value Zip::NoOpenArchive CreateArchive() has not been called yet.
188  \value Zip::FileNotFound File or directory does not exist.
189  \value Zip::ReadFailed Reading of a file failed.
190  \value Zip::WriteFailed Writing of a file failed.
191  \value Zip::SeekFailed Seek failed.
192 */
193 
194 /*! \enum Zip::CompressionLevel Returns the result of a decompression operation.
195  \value Zip::Store No compression.
196  \value Zip::Deflate1 Deflate compression level 1(lowest compression).
197  \value Zip::Deflate1 Deflate compression level 2.
198  \value Zip::Deflate1 Deflate compression level 3.
199  \value Zip::Deflate1 Deflate compression level 4.
200  \value Zip::Deflate1 Deflate compression level 5.
201  \value Zip::Deflate1 Deflate compression level 6.
202  \value Zip::Deflate1 Deflate compression level 7.
203  \value Zip::Deflate1 Deflate compression level 8.
204  \value Zip::Deflate1 Deflate compression level 9 (maximum compression).
205  \value Zip::AutoCPU Adapt compression level to CPU speed (faster CPU => better compression).
206  \value Zip::AutoMIME Adapt compression level to MIME type of the file being compressed.
207  \value Zip::AutoFull Use both CPU and MIME type detection.
208 */
209 
210 
211 /************************************************************************
212  Public interface
213 *************************************************************************/
214 
215 /*!
216  Creates a new Zip file compressor.
217 */
219 {
220  d = new ZipPrivate;
221 }
222 
223 /*!
224  Closes any open archive and releases used resources.
225 */
227 {
228  closeArchive();
229  delete d;
230 }
231 
232 /*!
233  Returns true if there is an open archive.
234 */
235 bool Zip::isOpen() const
236 {
237  return d->device != 0;
238 }
239 
240 /*!
241  Sets the password to be used for the next files being added!
242  Files added before calling this method will use the previously
243  set password (if any).
244  Closing the archive won't clear the password!
245 */
246 void Zip::setPassword(const QString& pwd)
247 {
248  d->password = pwd;
249 }
250 
251 //! Convenience method, clears the current password.
253 {
254  d->password.clear();
255 }
256 
257 //! Returns the currently used password.
258 QString Zip::password() const
259 {
260  return d->password;
261 }
262 
263 /*!
264  Attempts to create a new Zip archive. If \p overwrite is true and the file
265  already exist it will be overwritten.
266  Any open archive will be closed.
267  */
268 Zip::ErrorCode Zip::createArchive(const QString& filename, bool overwrite)
269 {
270  QFile* file = new QFile(filename);
271 
272  if (file->exists() && !overwrite) {
273  delete file;
274  return Zip::FileExists;
275  }
276 
277  if (!file->open(QIODevice::WriteOnly)) {
278  delete file;
279  return Zip::OpenFailed;
280  }
281 
282  Zip::ErrorCode ec = createArchive(file);
283  if (ec != Zip::Ok) {
284  file->remove();
285  }
286 
287  return ec;
288 }
289 
290 /*!
291  Attempts to create a new Zip archive. If there is another open archive this will be closed.
292  \warning The class takes ownership of the device!
293  */
295 {
296  if (device == 0)
297  {
298  qDebug() << "Invalid device.";
299  return Zip::OpenFailed;
300  }
301 
302  return d->createArchive(device);
303 }
304 
305 /*!
306  Returns the current archive comment.
307 */
308 QString Zip::archiveComment() const
309 {
310  return d->comment;
311 }
312 
313 /*!
314  Sets the comment for this archive. Note: createArchive() should have been
315  called before.
316 */
317 void Zip::setArchiveComment(const QString& comment)
318 {
319  if (d->device != 0)
320  d->comment = comment;
321 }
322 
323 /*!
324  Convenience method, same as calling
325  Zip::addDirectory(const QString&,const QString&,CompressionLevel)
326  with an empty \p root parameter (or with the parent directory of \p path if the
327  AbsolutePaths options is set).
328 
329  The ExtractionOptions are checked in the order they are defined in the zip.h heaser file.
330  This means that the last one overwrites the previous one (if some conflict occurs), i.e.
331  Zip::IgnorePaths | Zip::AbsolutePaths would be interpreted as Zip::IgnorePaths.
332  */
333 Zip::ErrorCode Zip::addDirectory(const QString& path, CompressionOptions options, CompressionLevel level)
334 {
335  return addDirectory(path, QString(), options, level);
336 }
337 
338 /*!
339  Convenience method, same as calling Zip::addDirectory(const QString&,const QString&,CompressionOptions,CompressionLevel)
340  with the Zip::RelativePaths flag as compression option.
341  */
342 Zip::ErrorCode Zip::addDirectory(const QString& path, const QString& root, CompressionLevel level)
343 {
344  return addDirectory(path, root, Zip::RelativePaths, level);
345 }
346 
347 /*!
348  Convenience method, same as calling Zip::addDirectory(const QString&,const QString&,CompressionOptions,CompressionLevel)
349  with the Zip::IgnorePaths flag as compression option and an empty \p root parameter.
350 */
352 {
353  return addDirectory(path, QString(), IgnorePaths, level);
354 }
355 
356 /*!
357  Convenience method, same as calling Zip::addDirectory(const QString&,const QString&,CompressionOptions,CompressionLevel)
358  with the Zip::IgnorePaths flag as compression option.
359 */
360 Zip::ErrorCode Zip::addDirectoryContents(const QString& path, const QString& root, CompressionLevel level)
361 {
362  return addDirectory(path, root, IgnorePaths, level);
363 }
364 
365 /*!
366  Recursively adds files contained in \p dir to the archive, using \p root as name for the root folder.
367  Stops adding files if some error occurs.
368 
369  The ExtractionOptions are checked in the order they are defined in the zip.h heaser file.
370  This means that the last one overwrites the previous one (if some conflict occurs), i.e.
371  Zip::IgnorePaths | Zip::AbsolutePaths would be interpreted as Zip::IgnorePaths.
372 
373  The \p root parameter is ignored with the Zip::IgnorePaths parameter and used as path prefix (a trailing /
374  is always added as directory separator!) otherwise (even with Zip::AbsolutePaths set!).
375 */
376 Zip::ErrorCode Zip::addDirectory(const QString& path, const QString& root, CompressionOptions options, CompressionLevel level)
377 {
378  // qDebug() << QString("addDir(path=%1, root=%2)").arg(path, root);
379 
380  // Bad boy didn't call createArchive() yet :)
381  if (d->device == 0)
382  return Zip::NoOpenArchive;
383 
384  QDir dir(path);
385  if (!dir.exists())
386  return Zip::FileNotFound;
387 
388  // Remove any trailing separator
389  QString actualRoot = root.trimmed();
390 
391  // Preserve Unix root
392  if (actualRoot != "/")
393  {
394  while (actualRoot.endsWith("/") || actualRoot.endsWith("\\"))
395  actualRoot.truncate(actualRoot.length() - 1);
396  }
397 
398  // QDir::cleanPath() fixes some issues with QDir::dirName()
399  QFileInfo current(QDir::cleanPath(path));
400 
401  if (!actualRoot.isEmpty() && actualRoot != "/")
402  actualRoot.append("/");
403 
404  /* This part is quite confusing and needs some test or check */
405  /* An attempt to compress the / root directory evtl. using a root prefix should be a good test */
406  if (options.testFlag(AbsolutePaths) && !options.testFlag(IgnorePaths))
407  {
408  QString absolutePath = d->extractRoot(path);
409  if (!absolutePath.isEmpty() && absolutePath != "/")
410  absolutePath.append("/");
411  actualRoot.append(absolutePath);
412  }
413 
414  if (!options.testFlag(IgnorePaths))
415  {
416  actualRoot = actualRoot.append(QDir(current.absoluteFilePath()).dirName());
417  actualRoot.append("/");
418  }
419 
420  // actualRoot now contains the path of the file relative to the zip archive
421  // with a trailing /
422 
423  QFileInfoList list = dir.entryInfoList(
424  QDir::Files |
425  QDir::Dirs |
426  QDir::NoDotAndDotDot |
427  QDir::NoSymLinks);
428 
429  ErrorCode ec = Zip::Ok;
430  bool filesAdded = false;
431 
432  CompressionOptions recursionOptions;
433  if (options.testFlag(IgnorePaths))
434  recursionOptions |= IgnorePaths;
435  else recursionOptions |= RelativePaths;
436 
437  for (int i = 0; i < list.size() && ec == Zip::Ok; ++i)
438  {
439  QFileInfo info = list.at(i);
440 
441  if (info.isDir())
442  {
443  // Recursion :)
444  ec = addDirectory(info.absoluteFilePath(), actualRoot, recursionOptions, level);
445  }
446  else
447  {
448  ec = d->createEntry(info, actualRoot, level);
449  filesAdded = true;
450  }
451  }
452 
453 
454  // We need an explicit record for this dir
455  // Non-empty directories don't need it because they have a path component in the filename
456  if (!filesAdded && !options.testFlag(IgnorePaths))
457  ec = d->createEntry(current, actualRoot, level);
458 
459  return ec;
460 }
461 
462 /*!
463  Closes the archive and writes any pending data.
464 */
466 {
467  Zip::ErrorCode ec = d->closeArchive();
468  d->reset();
469  return ec;
470 }
471 
472 /*!
473  Returns a locale translated error string for a given error code.
474 */
476 {
477  switch (c)
478  {
479  case Ok: return QCoreApplication::translate("Zip", "ZIP operation completed successfully."); break;
480  case ZlibInit: return QCoreApplication::translate("Zip", "Failed to initialize or load zlib library."); break;
481  case ZlibError: return QCoreApplication::translate("Zip", "zlib library error."); break;
482  case OpenFailed: return QCoreApplication::translate("Zip", "Unable to create or open file."); break;
483  case NoOpenArchive: return QCoreApplication::translate("Zip", "No archive has been created yet."); break;
484  case FileNotFound: return QCoreApplication::translate("Zip", "File or directory does not exist."); break;
485  case ReadFailed: return QCoreApplication::translate("Zip", "File read error."); break;
486  case WriteFailed: return QCoreApplication::translate("Zip", "File write error."); break;
487  case SeekFailed: return QCoreApplication::translate("Zip", "File seek error."); break;
488  default: ;
489  }
490 
491  return QCoreApplication::translate("Zip", "Unknown error.");
492 }
493 
494 
495 /************************************************************************
496  Private interface
497 *************************************************************************/
498 
499 //! \internal
501 {
502  headers = 0;
503  device = 0;
504 
505  // keep an unsigned pointer so we avoid to over bloat the code with casts
506  uBuffer = (unsigned char*) buffer1;
507  crcTable = (quint32*) get_crc_table();
508 }
509 
510 //! \internal
512 {
513  closeArchive();
514 }
515 
516 //! \internal
518 {
519  Q_ASSERT(dev != 0);
520 
521  if (device != 0)
522  closeArchive();
523 
524  device = dev;
525 
526  if (!device->isOpen())
527  {
528  if (!device->open(QIODevice::ReadOnly)) {
529  delete device;
530  device = 0;
531  qDebug() << "Unable to open device for writing.";
532  return Zip::OpenFailed;
533  }
534  }
535 
536  headers = new QMap<QString,ZipEntryP*>;
537  return Zip::Ok;
538 }
539 
540 //! \internal Writes a new entry in the zip file.
541 Zip::ErrorCode ZipPrivate::createEntry(const QFileInfo& file, const QString& root, Zip::CompressionLevel level)
542 {
543  //! \todo Automatic level detection (cpu, extension & file size)
544 
545  // Directories and very small files are always stored
546  // (small files would get bigger due to the compression headers overhead)
547 
548  // Need this for zlib
549  bool isPNGFile = false;
550  bool dirOnly = file.isDir();
551 
552  QString entryName = root;
553 
554  // Directory entry
555  if (dirOnly)
556  level = Zip::Store;
557  else
558  {
559  entryName.append(file.fileName());
560 
561  QString ext = file.completeSuffix().toLower();
562  isPNGFile = ext == "png";
563 
564  if (file.size() < ZIP_COMPRESSION_THRESHOLD)
565  level = Zip::Store;
566  else
567  switch (level)
568  {
569  case Zip::AutoCPU:
570  level = Zip::Deflate5;
571  break;
572  case Zip::AutoMIME:
573  level = detectCompressionByMime(ext);
574  break;
575  case Zip::AutoFull:
576  level = detectCompressionByMime(ext);
577  break;
578  default:
579  ;
580  }
581  }
582 
583  // entryName contains the path as it should be written
584  // in the zip file records
585  // qDebug() << QString("addDir(file=%1, root=%2, entry=%3)").arg(file.absoluteFilePath(), root, entryName);
586 
587  // create header and store it to write a central directory later
588  ZipEntryP* h = new ZipEntryP;
589 
590  h->compMethod = (level == Zip::Store) ? 0 : 0x0008;
591 
592  // Set encryption bit and set the data descriptor bit
593  // so we can use mod time instead of crc for password check
594  bool encrypt = !dirOnly && !password.isEmpty();
595  if (encrypt)
596  h->gpFlag[0] |= 9;
597 
598  QDateTime dt = file.lastModified();
599  QDate d = dt.date();
600  h->modDate[1] = ((d.year() - 1980) << 1) & 254;
601  h->modDate[1] |= ((d.month() >> 3) & 1);
602  h->modDate[0] = ((d.month() & 7) << 5) & 224;
603  h->modDate[0] |= d.day();
604 
605  QTime t = dt.time();
606  h->modTime[1] = (t.hour() << 3) & 248;
607  h->modTime[1] |= ((t.minute() >> 3) & 7);
608  h->modTime[0] = ((t.minute() & 7) << 5) & 224;
609  h->modTime[0] |= t.second() / 2;
610 
611  h->szUncomp = dirOnly ? 0 : file.size();
612 
613  // **** Write local file header ****
614 
615  // signature
616  buffer1[0] = 'P'; buffer1[1] = 'K';
617  buffer1[2] = 0x3; buffer1[3] = 0x4;
618 
619  // version needed to extract
620  buffer1[ZIP_LH_OFF_VERS] = ZIP_VERSION;
621  buffer1[ZIP_LH_OFF_VERS + 1] = 0;
622 
623  // general purpose flag
624  buffer1[ZIP_LH_OFF_GPFLAG] = h->gpFlag[0];
625  buffer1[ZIP_LH_OFF_GPFLAG + 1] = h->gpFlag[1];
626 
627  // compression method
628  buffer1[ZIP_LH_OFF_CMET] = h->compMethod & 0xFF;
629  buffer1[ZIP_LH_OFF_CMET + 1] = (h->compMethod>>8) & 0xFF;
630 
631  // last mod file time
632  buffer1[ZIP_LH_OFF_MODT] = h->modTime[0];
633  buffer1[ZIP_LH_OFF_MODT + 1] = h->modTime[1];
634 
635  // last mod file date
636  buffer1[ZIP_LH_OFF_MODD] = h->modDate[0];
637  buffer1[ZIP_LH_OFF_MODD + 1] = h->modDate[1];
638 
639  // skip crc (4bytes) [14,15,16,17]
640 
641  // skip compressed size but include evtl. encryption header (4bytes: [18,19,20,21])
642  buffer1[ZIP_LH_OFF_CSIZE] =
643  buffer1[ZIP_LH_OFF_CSIZE + 1] =
644  buffer1[ZIP_LH_OFF_CSIZE + 2] =
645  buffer1[ZIP_LH_OFF_CSIZE + 3] = 0;
646 
647  h->szComp = encrypt ? ZIP_LOCAL_ENC_HEADER_SIZE : 0;
648 
649  // uncompressed size [22,23,24,25]
650  setULong(h->szUncomp, buffer1, ZIP_LH_OFF_USIZE);
651 
652  // filename length
653  //QByteArray entryNameBytes = entryName.toAscii();
654  QByteArray entryNameBytes = entryName.toLatin1(); // Qt5
655  int sz = entryNameBytes.size();
656 
657  buffer1[ZIP_LH_OFF_NAMELEN] = sz & 0xFF;
658  buffer1[ZIP_LH_OFF_NAMELEN + 1] = (sz >> 8) & 0xFF;
659 
660  // extra field length
661  buffer1[ZIP_LH_OFF_XLEN] = buffer1[ZIP_LH_OFF_XLEN + 1] = 0;
662 
663  // Store offset to write crc and compressed size
664  h->lhOffset = device->pos();
665  quint32 crcOffset = h->lhOffset + ZIP_LH_OFF_CRC;
666 
667  if (device->write(buffer1, ZIP_LOCAL_HEADER_SIZE) != ZIP_LOCAL_HEADER_SIZE)
668  {
669  delete h;
670  return Zip::WriteFailed;
671  }
672 
673  // Write out filename
674  if (device->write(entryNameBytes) != sz)
675  {
676  delete h;
677  return Zip::WriteFailed;
678  }
679 
680  // Encryption keys
681  quint32 keys[3] = { 0, 0, 0 };
682 
683  if (encrypt)
684  {
685  // **** encryption header ****
686 
687  // XOR with PI to ensure better random numbers
688  // with poorly implemented rand() as suggested by Info-Zip
689  srand(time(NULL) ^ 3141592654UL);
690  int randByte;
691 
692  initKeys(keys);
693  for (int i=0; i<10; ++i)
694  {
695  randByte = (rand() >> 7) & 0xff;
696  buffer1[i] = decryptByte(keys[2]) ^ randByte;
697  updateKeys(keys, randByte);
698  }
699 
700  // Encrypt encryption header
701  initKeys(keys);
702  for (int i=0; i<10; ++i)
703  {
704  randByte = decryptByte(keys[2]);
705  updateKeys(keys, buffer1[i]);
706  buffer1[i] ^= randByte;
707  }
708 
709  // We don't know the CRC at this time, so we use the modification time
710  // as the last two bytes
711  randByte = decryptByte(keys[2]);
712  updateKeys(keys, h->modTime[0]);
713  buffer1[10] ^= randByte;
714 
715  randByte = decryptByte(keys[2]);
716  updateKeys(keys, h->modTime[1]);
717  buffer1[11] ^= randByte;
718 
719  // Write out encryption header
720  if (device->write(buffer1, ZIP_LOCAL_ENC_HEADER_SIZE) != ZIP_LOCAL_ENC_HEADER_SIZE)
721  {
722  delete h;
723  return Zip::WriteFailed;
724  }
725  }
726 
727  qint64 written = 0;
728  quint32 crc = crc32(0L, Z_NULL, 0);
729 
730  if (!dirOnly)
731  {
732  QFile actualFile(file.absoluteFilePath());
733  if (!actualFile.open(QIODevice::ReadOnly))
734  {
735  qDebug() << QString("An error occurred while opening %1").arg(file.absoluteFilePath());
736  return Zip::OpenFailed;
737  }
738 
739  // Write file data
740  qint64 read = 0;
741  qint64 totRead = 0;
742  qint64 toRead = actualFile.size();
743 
744  if (level == Zip::Store)
745  {
746  while ( (read = actualFile.read(buffer1, ZIP_READ_BUFFER)) > 0 )
747  {
748  crc = crc32(crc, uBuffer, read);
749 
750  if (password != 0)
751  encryptBytes(keys, buffer1, read);
752 
753  if ( (written = device->write(buffer1, read)) != read )
754  {
755  actualFile.close();
756  delete h;
757  return Zip::WriteFailed;
758  }
759  }
760  }
761  else
762  {
763  z_stream zstr;
764 
765  // Initialize zalloc, zfree and opaque before calling the init function
766  zstr.zalloc = Z_NULL;
767  zstr.zfree = Z_NULL;
768  zstr.opaque = Z_NULL;
769 
770  int zret;
771 
772  // Use deflateInit2 with negative windowBits to get raw compression
773  if ((zret = deflateInit2_(
774  &zstr,
775  (int)level,
776  Z_DEFLATED,
777  -MAX_WBITS,
778  8,
779  isPNGFile ? Z_RLE : Z_DEFAULT_STRATEGY,
780  ZLIB_VERSION,
781  sizeof(z_stream)
782  )) != Z_OK )
783  {
784  actualFile.close();
785  qDebug() << "Could not initialize zlib for compression";
786  delete h;
787  return Zip::ZlibError;
788  }
789 
790  qint64 compressed;
791 
792  int flush = Z_NO_FLUSH;
793 
794  do
795  {
796  read = actualFile.read(buffer1, ZIP_READ_BUFFER);
797  totRead += read;
798 
799  if (read == 0)
800  break;
801  if (read < 0)
802  {
803  actualFile.close();
804  deflateEnd(&zstr);
805  qDebug() << QString("Error while reading %1").arg(file.absoluteFilePath());
806  delete h;
807  return Zip::ReadFailed;
808  }
809 
810  crc = crc32(crc, uBuffer, read);
811 
812  zstr.next_in = (Bytef*) buffer1;
813  zstr.avail_in = (uInt)read;
814 
815  // Tell zlib if this is the last chunk we want to encode
816  // by setting the flush parameter to Z_FINISH
817  flush = (totRead == toRead) ? Z_FINISH : Z_NO_FLUSH;
818 
819  // Run deflate() on input until output buffer not full
820  // finish compression if all of source has been read in
821  do
822  {
823  zstr.next_out = (Bytef*) buffer2;
824  zstr.avail_out = ZIP_READ_BUFFER;
825 
826  zret = deflate(&zstr, flush);
827  // State not clobbered
828  Q_ASSERT(zret != Z_STREAM_ERROR);
829 
830  // Write compressed data to file and empty buffer
831  compressed = ZIP_READ_BUFFER - zstr.avail_out;
832 
833  if (password != 0)
834  encryptBytes(keys, buffer2, compressed);
835 
836  if (device->write(buffer2, compressed) != compressed)
837  {
838  deflateEnd(&zstr);
839  actualFile.close();
840  qDebug() << QString("Error while writing %1").arg(file.absoluteFilePath());
841  delete h;
842  return Zip::WriteFailed;
843  }
844 
845  written += compressed;
846 
847  } while (zstr.avail_out == 0);
848 
849  // All input will be used
850  Q_ASSERT(zstr.avail_in == 0);
851 
852  } while (flush != Z_FINISH);
853 
854  // Stream will be complete
855  Q_ASSERT(zret == Z_STREAM_END);
856 
857  deflateEnd(&zstr);
858 
859  } // if (level != STORE)
860 
861  actualFile.close();
862  }
863 
864  // Store end of entry offset
865  quint32 current = device->pos();
866 
867  // Update crc and compressed size in local header
868  if (!device->seek(crcOffset))
869  {
870  delete h;
871  return Zip::SeekFailed;
872  }
873 
874  h->crc = dirOnly ? 0 : crc;
875  h->szComp += written;
876 
877  setULong(h->crc, buffer1, 0);
878  setULong(h->szComp, buffer1, 4);
879  if ( device->write(buffer1, 8) != 8)
880  {
881  delete h;
882  return Zip::WriteFailed;
883  }
884 
885  // Seek to end of entry
886  if (!device->seek(current))
887  {
888  delete h;
889  return Zip::SeekFailed;
890  }
891 
892  if ((h->gpFlag[0] & 8) == 8)
893  {
894  // Write data descriptor
895 
896  // Signature: PK\7\8
897  buffer1[0] = 'P';
898  buffer1[1] = 'K';
899  buffer1[2] = 0x07;
900  buffer1[3] = 0x08;
901 
902  // CRC
903  setULong(h->crc, buffer1, ZIP_DD_OFF_CRC32);
904 
905  // Compressed size
906  setULong(h->szComp, buffer1, ZIP_DD_OFF_CSIZE);
907 
908  // Uncompressed size
909  setULong(h->szUncomp, buffer1, ZIP_DD_OFF_USIZE);
910 
911  if (device->write(buffer1, ZIP_DD_SIZE_WS) != ZIP_DD_SIZE_WS)
912  {
913  delete h;
914  return Zip::WriteFailed;
915  }
916  }
917 
918  headers->insert(entryName, h);
919  return Zip::Ok;
920 }
921 
922 //! \internal
923 int ZipPrivate::decryptByte(quint32 key2) const
924 {
925  quint16 temp = ((quint16)(key2) & 0xffff) | 2;
926  return (int)(((temp * (temp ^ 1)) >> 8) & 0xff);
927 }
928 
929 //! \internal Writes an quint32 (4 bytes) to a byte array at given offset.
930 void ZipPrivate::setULong(quint32 v, char* buffer, unsigned int offset)
931 {
932  buffer[offset+3] = ((v >> 24) & 0xFF);
933  buffer[offset+2] = ((v >> 16) & 0xFF);
934  buffer[offset+1] = ((v >> 8) & 0xFF);
935  buffer[offset] = (v & 0xFF);
936 }
937 
938 //! \internal Initializes decryption keys using a password.
939 void ZipPrivate::initKeys(quint32* keys) const
940 {
941  // Encryption keys initialization constants are taken from the
942  // PKZip file format specification docs
943  keys[0] = 305419896L;
944  keys[1] = 591751049L;
945  keys[2] = 878082192L;
946 
947  //QByteArray pwdBytes = password.toAscii();
948  QByteArray pwdBytes = password.toLatin1();
949  int sz = pwdBytes.size();
950  const char* ascii = pwdBytes.data();
951 
952  for (int i=0; i<sz; ++i)
953  updateKeys(keys, (int)ascii[i]);
954 }
955 
956 //! \internal Updates encryption keys.
957 void ZipPrivate::updateKeys(quint32* keys, int c) const
958 {
959  keys[0] = CRC32(keys[0], c);
960  keys[1] += keys[0] & 0xff;
961  keys[1] = keys[1] * 134775813L + 1;
962  keys[2] = CRC32(keys[2], ((int)keys[1]) >> 24);
963 }
964 
965 //! \internal Encrypts a byte array.
966 void ZipPrivate::encryptBytes(quint32* keys, char* buffer, qint64 read)
967 {
968  char t;
969 
970  for (int i=0; i<(int)read; ++i)
971  {
972  t = buffer[i];
973  buffer[i] ^= decryptByte(keys[2]);
974  updateKeys(keys, t);
975  }
976 }
977 
978 //! \internal Detects the best compression level for a given file extension.
980 {
981  // files really hard to compress
982  if ((ext == "png") ||
983  (ext == "jpg") ||
984  (ext == "jpeg") ||
985  (ext == "mp3") ||
986  (ext == "ogg") ||
987  (ext == "ogm") ||
988  (ext == "avi") ||
989  (ext == "mov") ||
990  (ext == "rm") ||
991  (ext == "ra") ||
992  (ext == "zip") ||
993  (ext == "rar") ||
994  (ext == "bz2") ||
995  (ext == "gz") ||
996  (ext == "7z") ||
997  (ext == "z") ||
998  (ext == "jar")
999  ) return Zip::Store;
1000 
1001  // files slow and hard to compress
1002  if ((ext == "exe") ||
1003  (ext == "bin") ||
1004  (ext == "rpm") ||
1005  (ext == "deb")
1006  ) return Zip::Deflate2;
1007 
1008  return Zip::Deflate9;
1009 }
1010 
1011 /*!
1012  Closes the current archive and writes out pending data.
1013 */
1015 {
1016  // Close current archive by writing out central directory
1017  // and free up resources
1018 
1019  if (device == 0)
1020  return Zip::Ok;
1021 
1022  if (headers == 0)
1023  return Zip::Ok;
1024 
1025  const ZipEntryP* h;
1026 
1027  unsigned int sz;
1028  quint32 szCentralDir = 0;
1029  quint32 offCentralDir = device->pos();
1030 
1031  for (QMap<QString,ZipEntryP*>::ConstIterator itr = headers->constBegin(); itr != headers->constEnd(); ++itr)
1032  {
1033  h = itr.value();
1034 
1035  // signature
1036  buffer1[0] = 'P';
1037  buffer1[1] = 'K';
1038  buffer1[2] = 0x01;
1039  buffer1[3] = 0x02;
1040 
1041  // version made by (currently only MS-DOS/FAT - no symlinks or other stuff supported)
1042  buffer1[ZIP_CD_OFF_MADEBY] = buffer1[ZIP_CD_OFF_MADEBY + 1] = 0;
1043 
1044  // version needed to extract
1045  buffer1[ZIP_CD_OFF_VERSION] = ZIP_VERSION;
1046  buffer1[ZIP_CD_OFF_VERSION + 1] = 0;
1047 
1048  // general purpose flag
1049  buffer1[ZIP_CD_OFF_GPFLAG] = h->gpFlag[0];
1050  buffer1[ZIP_CD_OFF_GPFLAG + 1] = h->gpFlag[1];
1051 
1052  // compression method
1053  buffer1[ZIP_CD_OFF_CMET] = h->compMethod & 0xFF;
1054  buffer1[ZIP_CD_OFF_CMET + 1] = (h->compMethod >> 8) & 0xFF;
1055 
1056  // last mod file time
1057  buffer1[ZIP_CD_OFF_MODT] = h->modTime[0];
1058  buffer1[ZIP_CD_OFF_MODT + 1] = h->modTime[1];
1059 
1060  // last mod file date
1061  buffer1[ZIP_CD_OFF_MODD] = h->modDate[0];
1062  buffer1[ZIP_CD_OFF_MODD + 1] = h->modDate[1];
1063 
1064  // crc (4bytes) [16,17,18,19]
1065  setULong(h->crc, buffer1, ZIP_CD_OFF_CRC);
1066 
1067  // compressed size (4bytes: [20,21,22,23])
1068  setULong(h->szComp, buffer1, ZIP_CD_OFF_CSIZE);
1069 
1070  // uncompressed size [24,25,26,27]
1071  setULong(h->szUncomp, buffer1, ZIP_CD_OFF_USIZE);
1072 
1073  // filename
1074  //QByteArray fileNameBytes = itr.key().toAscii();
1075  QByteArray fileNameBytes = itr.key().toLatin1();
1076  sz = fileNameBytes.size();
1077  buffer1[ZIP_CD_OFF_NAMELEN] = sz & 0xFF;
1078  buffer1[ZIP_CD_OFF_NAMELEN + 1] = (sz >> 8) & 0xFF;
1079 
1080  // extra field length
1081  buffer1[ZIP_CD_OFF_XLEN] = buffer1[ZIP_CD_OFF_XLEN + 1] = 0;
1082 
1083  // file comment length
1084  buffer1[ZIP_CD_OFF_COMMLEN] = buffer1[ZIP_CD_OFF_COMMLEN + 1] = 0;
1085 
1086  // disk number start
1087  buffer1[ZIP_CD_OFF_DISKSTART] = buffer1[ZIP_CD_OFF_DISKSTART + 1] = 0;
1088 
1089  // internal file attributes
1090  buffer1[ZIP_CD_OFF_IATTR] = buffer1[ZIP_CD_OFF_IATTR + 1] = 0;
1091 
1092  // external file attributes
1093  buffer1[ZIP_CD_OFF_EATTR] =
1094  buffer1[ZIP_CD_OFF_EATTR + 1] =
1095  buffer1[ZIP_CD_OFF_EATTR + 2] =
1096  buffer1[ZIP_CD_OFF_EATTR + 3] = 0;
1097 
1098  // relative offset of local header [42->45]
1099  setULong(h->lhOffset, buffer1, ZIP_CD_OFF_LHOFF);
1100 
1101  if (device->write(buffer1, ZIP_CD_SIZE) != ZIP_CD_SIZE)
1102  {
1103  //! \todo See if we can detect QFile objects using the Qt Meta Object System
1104  /*
1105  if (!device->remove())
1106  qDebug() << tr("Unable to delete corrupted archive: %1").arg(device->fileName());
1107  */
1108  return Zip::WriteFailed;
1109  }
1110 
1111  // Write out filename
1112  if ((unsigned int)device->write(fileNameBytes) != sz)
1113  {
1114  //! \todo SAME AS ABOVE: See if we can detect QFile objects using the Qt Meta Object System
1115  /*
1116  if (!device->remove())
1117  qDebug() << tr("Unable to delete corrupted archive: %1").arg(device->fileName());
1118  */
1119  return Zip::WriteFailed;
1120  }
1121 
1122  szCentralDir += (ZIP_CD_SIZE + sz);
1123 
1124  } // central dir headers loop
1125 
1126 
1127  // Write end of central directory
1128 
1129  // signature
1130  buffer1[0] = 'P';
1131  buffer1[1] = 'K';
1132  buffer1[2] = 0x05;
1133  buffer1[3] = 0x06;
1134 
1135  // number of this disk
1136  buffer1[ZIP_EOCD_OFF_DISKNUM] = buffer1[ZIP_EOCD_OFF_DISKNUM + 1] = 0;
1137 
1138  // number of disk with central directory
1139  buffer1[ZIP_EOCD_OFF_CDDISKNUM] = buffer1[ZIP_EOCD_OFF_CDDISKNUM + 1] = 0;
1140 
1141  // number of entries in this disk
1142  sz = headers->count();
1143  buffer1[ZIP_EOCD_OFF_ENTRIES] = sz & 0xFF;
1144  buffer1[ZIP_EOCD_OFF_ENTRIES + 1] = (sz >> 8) & 0xFF;
1145 
1146  // total number of entries
1147  buffer1[ZIP_EOCD_OFF_CDENTRIES] = buffer1[ZIP_EOCD_OFF_ENTRIES];
1148  buffer1[ZIP_EOCD_OFF_CDENTRIES + 1] = buffer1[ZIP_EOCD_OFF_ENTRIES + 1];
1149 
1150  // size of central directory [12->15]
1151  setULong(szCentralDir, buffer1, ZIP_EOCD_OFF_CDSIZE);
1152 
1153  // central dir offset [16->19]
1154  setULong(offCentralDir, buffer1, ZIP_EOCD_OFF_CDOFF);
1155 
1156  // ZIP file comment length
1157  //QByteArray commentBytes = comment.toAscii();
1158  QByteArray commentBytes = comment.toLatin1();
1159  quint16 commentLength = commentBytes.size();
1160 
1161  if (commentLength == 0)
1162  {
1163  buffer1[ZIP_EOCD_OFF_COMMLEN] = buffer1[ZIP_EOCD_OFF_COMMLEN + 1] = 0;
1164  }
1165  else
1166  {
1167  buffer1[ZIP_EOCD_OFF_COMMLEN] = commentLength & 0xFF;
1168  buffer1[ZIP_EOCD_OFF_COMMLEN + 1] = (commentLength >> 8) & 0xFF;
1169  }
1170 
1171  if (device->write(buffer1, ZIP_EOCD_SIZE) != ZIP_EOCD_SIZE)
1172  {
1173  //! \todo SAME AS ABOVE: See if we can detect QFile objects using the Qt Meta Object System
1174  /*
1175  if (!device->remove())
1176  qDebug() << tr("Unable to delete corrupted archive: %1").arg(device->fileName());
1177  */
1178  return Zip::WriteFailed;
1179  }
1180 
1181  if (commentLength != 0)
1182  {
1183  if ((unsigned int)device->write(commentBytes) != commentLength)
1184  {
1185  //! \todo SAME AS ABOVE: See if we can detect QFile objects using the Qt Meta Object System
1186  /*
1187  if (!device->remove())
1188  qDebug() << tr("Unable to delete corrupted archive: %1").arg(device->fileName());
1189  */
1190  return Zip::WriteFailed;
1191  }
1192  }
1193 
1194  return Zip::Ok;
1195 }
1196 
1197 //! \internal
1199 {
1200  comment.clear();
1201 
1202  if (headers != 0)
1203  {
1204  qDeleteAll(*headers);
1205  delete headers;
1206  headers = 0;
1207  }
1208 
1209  delete device; device = 0;
1210 }
1211 
1212 //! \internal Returns the path of the parent directory
1213 QString ZipPrivate::extractRoot(const QString& p)
1214 {
1215  QDir d(QDir::cleanPath(p));
1216  if (!d.exists())
1217  return QString();
1218 
1219  if (!d.cdUp())
1220  return QString();
1221 
1222  return d.absolutePath();
1223 }
#define ZIP_CD_OFF_USIZE
Definition: zip.cpp:83
#define ZIP_READ_BUFFER
Definition: zip_p.h:52
#define ZIP_CD_OFF_LHOFF
Definition: zip.cpp:90
#define ZIP_LH_OFF_CSIZE
Definition: zip.cpp:64
#define ZIP_LH_OFF_MODT
Definition: zip.cpp:61
Definition: zip.h:50
quint16 compMethod
Definition: zipentry_p.h:64
ErrorCode createArchive(const QString &file, bool overwrite=true)
Definition: zip.cpp:268
#define ZIP_EOCD_OFF_CDDISKNUM
Definition: zip.cpp:94
ZipPrivate()
Definition: zip.cpp:500
void initKeys(quint32 *keys) const
Definition: zip.cpp:939
#define ZIP_LH_OFF_MODD
Definition: zip.cpp:62
void setPassword(const QString &pwd)
Definition: zip.cpp:246
unsigned char modTime[2]
Definition: zipentry_p.h:65
quint32 lhOffset
Definition: zipentry_p.h:61
void updateKeys(quint32 *keys, int c) const
Definition: zip.cpp:957
Zip::ErrorCode createEntry(const QFileInfo &file, const QString &root, Zip::CompressionLevel level)
Definition: zip.cpp:541
#define ZIP_CD_OFF_IATTR
Definition: zip.cpp:88
#define ZIP_EOCD_OFF_DISKNUM
Definition: zip.cpp:93
void setArchiveComment(const QString &comment)
Definition: zip.cpp:317
#define ZIP_EOCD_OFF_CDENTRIES
Definition: zip.cpp:96
#define ZIP_CD_OFF_MADEBY
Definition: zip.cpp:75
QString archiveComment() const
Definition: zip.cpp:308
#define ZIP_EOCD_OFF_CDSIZE
Definition: zip.cpp:97
#define ZIP_COMPRESSION_THRESHOLD
Do not store very small files as the compression headers overhead would be to big.
Definition: zip.cpp:105
bool isOpen() const
Definition: zip.cpp:235
#define ZIP_LH_OFF_CRC
Definition: zip.cpp:63
Do not store paths. All the files are put in the (evtl. user defined) root of the zip file...
Definition: zip.h:77
#define ZIP_EOCD_OFF_ENTRIES
Definition: zip.cpp:95
#define ZIP_LH_OFF_CMET
Definition: zip.cpp:60
Zip()
Definition: zip.cpp:218
#define ZIP_EOCD_OFF_CDOFF
Definition: zip.cpp:98
#define ZIP_LH_OFF_GPFLAG
Definition: zip.cpp:59
QString comment
Definition: zip_p.h:71
#define ZIP_CD_OFF_CRC
Definition: zip.cpp:81
#define ZIP_CD_OFF_MODT
Definition: zip.cpp:79
Zip::ErrorCode createArchive(QIODevice *device)
Definition: zip.cpp:517
#define ZIP_LH_OFF_USIZE
Definition: zip.cpp:65
#define ZIP_CD_OFF_CMET
Definition: zip.cpp:78
#define ZIP_DD_OFF_USIZE
Definition: zip.cpp:72
#define ZIP_EOCD_SIZE
End of Central Directory record size (signature included)
Definition: zip.cpp:55
#define ZIP_CD_OFF_XLEN
Definition: zip.cpp:85
virtual ~Zip()
Definition: zip.cpp:226
#define ZIP_LOCAL_ENC_HEADER_SIZE
Encryption header size.
Definition: zip.cpp:49
#define ZIP_VERSION
PKZip version for archives created by this API.
Definition: zip.cpp:102
ErrorCode closeArchive()
Definition: zip.cpp:465
QString password() const
Returns the currently used password.
Definition: zip.cpp:258
QIODevice * device
Definition: zip_p.h:62
ZipPrivate * d
Definition: zip.h:108
QString password
Definition: zip_p.h:72
ErrorCode
Definition: zip.h:48
#define ZIP_CD_OFF_EATTR
Definition: zip.cpp:89
#define ZIP_CD_SIZE
Central Directory record size (signature included)
Definition: zip.cpp:53
Definition: zip.h:64
QString extractRoot(const QString &p)
Definition: zip.cpp:1213
#define ZIP_EOCD_OFF_COMMLEN
Definition: zip.cpp:99
#define CRC32(c, b)
This macro updates a one-char-only CRC; it&#39;s the Info-Zip macro re-adapted.
Definition: zip.cpp:108
Zip::CompressionLevel detectCompressionByMime(const QString &ext)
Definition: zip.cpp:979
#define ZIP_CD_OFF_DISKSTART
Definition: zip.cpp:87
#define ZIP_CD_OFF_MODD
Definition: zip.cpp:80
#define ZIP_DD_OFF_CRC32
Definition: zip.cpp:70
CompressionLevel
Definition: zip.h:62
virtual ~ZipPrivate()
Definition: zip.cpp:511
#define ZIP_CD_OFF_VERSION
Definition: zip.cpp:76
void reset()
Definition: zip.cpp:1198
void encryptBytes(quint32 *keys, char *buffer, qint64 read)
Definition: zip.cpp:966
ErrorCode addDirectory(const QString &path, CompressionOptions options=RelativePaths, CompressionLevel level=AutoFull)
Definition: zip.cpp:333
#define ZIP_LH_OFF_VERS
Definition: zip.cpp:58
int decryptByte(quint32 key2) const
Definition: zip.cpp:923
ErrorCode addDirectoryContents(const QString &path, CompressionLevel level=AutoFull)
Definition: zip.cpp:351
#define ZIP_CD_OFF_GPFLAG
Definition: zip.cpp:77
unsigned char gpFlag[2]
Definition: zipentry_p.h:63
#define ZIP_LH_OFF_XLEN
Definition: zip.cpp:67
quint32 szUncomp
Definition: zipentry_p.h:69
Zip::ErrorCode closeArchive()
Definition: zip.cpp:1014
#define ZIP_LOCAL_HEADER_SIZE
Local header size (including signature, excluding variable length fields)
Definition: zip.cpp:47
#define ZIP_DD_OFF_CSIZE
Definition: zip.cpp:71
unsigned char modDate[2]
Definition: zipentry_p.h:66
void setULong(quint32 v, char *buffer, unsigned int offset)
Definition: zip.cpp:930
Preserve absolute paths.
Definition: zip.h:75
#define ZIP_CD_OFF_NAMELEN
Definition: zip.cpp:84
#define ZIP_LH_OFF_NAMELEN
Definition: zip.cpp:66
#define ZIP_CD_OFF_COMMLEN
Definition: zip.cpp:86
#define ZIP_CD_OFF_CSIZE
Definition: zip.cpp:82
void clearPassword()
Convenience method, clears the current password.
Definition: zip.cpp:252
#define ZIP_DD_SIZE_WS
Data descriptor size (signature included)
Definition: zip.cpp:51
quint32 szComp
Definition: zipentry_p.h:68
QString formatError(ErrorCode c) const
Definition: zip.cpp:475
Does not preserve absolute paths in the zip file when adding a file/directory (default) ...
Definition: zip.h:73
quint32 crc
Definition: zipentry_p.h:67