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