diff options
Diffstat (limited to 'rbutil/rbutilqt/zip/unzip.cpp')
-rw-r--r-- | rbutil/rbutilqt/zip/unzip.cpp | 2720 |
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 | */ |
165 | UnZip::UnZip() | 165 | UnZip::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 | */ |
173 | UnZip::~UnZip() | 173 | UnZip::~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 | */ |
182 | bool UnZip::isOpen() const | 182 | bool 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 | */ |
190 | UnZip::ErrorCode UnZip::openArchive(const QString& filename) | 190 | UnZip::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 | */ |
212 | UnZip::ErrorCode UnZip::openArchive(QIODevice* device) | 212 | UnZip::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 | */ |
226 | void UnZip::closeArchive() | 226 | void UnZip::closeArchive() |
227 | { | 227 | { |
228 | d->closeArchive(); | 228 | d->closeArchive(); |
229 | } | 229 | } |
230 | 230 | ||
231 | QString UnZip::archiveComment() const | 231 | QString 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 | */ |
241 | QString UnZip::formatError(UnZip::ErrorCode c) const | 241 | QString 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 | */ |
270 | bool UnZip::contains(const QString& file) const | 270 | bool 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 | */ |
281 | QStringList UnZip::fileList() const | 281 | QStringList 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 | */ |
289 | QList<UnZip::ZipEntry> UnZip::entryList() const | 289 | QList<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 | */ |
325 | UnZip::ErrorCode UnZip::extractAll(const QString& dirname, ExtractionOptions options) | 325 | UnZip::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 | */ |
333 | UnZip::ErrorCode UnZip::extractAll(const QDir& dir, ExtractionOptions options) | 333 | UnZip::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 | */ |
380 | UnZip::ErrorCode UnZip::extractFile(const QString& filename, const QString& dirname, ExtractionOptions options) | 380 | UnZip::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 | */ |
388 | UnZip::ErrorCode UnZip::extractFile(const QString& filename, const QDir& dir, ExtractionOptions options) | 388 | UnZip::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 | */ |
404 | UnZip::ErrorCode UnZip::extractFile(const QString& filename, QIODevice* dev, ExtractionOptions options) | 404 | UnZip::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 | */ |
423 | UnZip::ErrorCode UnZip::extractFiles(const QStringList& filenames, const QString& dirname, ExtractionOptions options) | 423 | UnZip::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 | */ |
444 | UnZip::ErrorCode UnZip::extractFiles(const QStringList& filenames, const QDir& dir, ExtractionOptions options) | 444 | UnZip::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 | */ |
463 | void UnZip::setPassword(const QString& pwd) | 463 | void 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 | */ |
471 | UnZip::ZipEntry::ZipEntry() | 471 | UnZip::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 |
485 | UnzipPrivate::UnzipPrivate() | 485 | UnzipPrivate::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. |
500 | UnZip::ErrorCode UnzipPrivate::openArchive(QIODevice* dev) | 500 | UnZip::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 | */ |
574 | UnZip::ErrorCode UnzipPrivate::parseLocalHeaderRecord(const QString& path, ZipEntryP& entry) | 574 | UnZip::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 | */ |
712 | UnZip::ErrorCode UnzipPrivate::seekToCentralDirectory() | 712 | UnZip::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 | */ |
836 | UnZip::ErrorCode UnzipPrivate::parseCentralDirectoryRecord() | 836 | UnZip::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. |
945 | void UnzipPrivate::closeArchive() | 945 | void 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 |
969 | UnZip::ErrorCode UnzipPrivate::extractFile(const QString& path, ZipEntryP& entry, const QDir& dir, UnZip::ExtractionOptions options) | 969 | UnZip::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 |
1040 | UnZip::ErrorCode UnzipPrivate::extractFile(const QString& path, ZipEntryP& entry, QIODevice* dev, UnZip::ExtractionOptions options) | 1040 | UnZip::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. |
1192 | bool UnzipPrivate::createDirectory(const QString& path) | 1192 | bool 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 | */ |
1216 | quint32 UnzipPrivate::getULong(const unsigned char* data, quint32 offset) const | 1216 | quint32 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 | */ |
1229 | quint64 UnzipPrivate::getULLong(const unsigned char* data, quint32 offset) const | 1229 | quint64 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 | */ |
1246 | quint16 UnzipPrivate::getUShort(const unsigned char* data, quint32 offset) const | 1246 | quint16 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 | */ |
1254 | int UnzipPrivate::decryptByte(quint32 key2) const | 1254 | int 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 | */ |
1263 | void UnzipPrivate::updateKeys(quint32* keys, int c) const | 1263 | void 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 | */ |
1275 | void UnzipPrivate::initKeys(const QString& pwd, quint32* keys) const | 1275 | void 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 | */ |
1294 | UnZip::ErrorCode UnzipPrivate::testPassword(quint32* keys, const QString& file, const ZipEntryP& header) | 1294 | UnZip::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 | */ |
1313 | bool UnzipPrivate::testKeys(const ZipEntryP& header, quint32* keys) | 1313 | bool 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 | */ |
1332 | void UnzipPrivate::decryptBytes(quint32* keys, char* buffer, qint64 read) | 1332 | void 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 | */ |
1341 | QDateTime UnzipPrivate::convertDateTime(const unsigned char date[2], const unsigned char time[2]) const | 1341 | QDateTime 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 | } |