1 |
/* |
2 |
Copyright (C) 2005-2014 Sergey A. Tachenov |
3 |
|
4 |
This file is part of QuaZIP. |
5 |
|
6 |
QuaZIP is free software: you can redistribute it and/or modify |
7 |
it under the terms of the GNU Lesser General Public License as published by |
8 |
the Free Software Foundation, either version 2.1 of the License, or |
9 |
(at your option) any later version. |
10 |
|
11 |
QuaZIP is distributed in the hope that it will be useful, |
12 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 |
GNU Lesser General Public License for more details. |
15 |
|
16 |
You should have received a copy of the GNU Lesser General Public License |
17 |
along with QuaZIP. If not, see <http://www.gnu.org/licenses/>. |
18 |
|
19 |
See COPYING file for the full LGPL text. |
20 |
|
21 |
Original ZIP package is copyrighted by Gilles Vollant, see |
22 |
quazip/(un)zip.h files for details, basically it's zlib license. |
23 |
**/ |
24 |
|
25 |
#include "quazipfile.h" |
26 |
|
27 |
using namespace std; |
28 |
|
29 |
/// The implementation class for QuaZip. |
30 |
/** |
31 |
\internal |
32 |
|
33 |
This class contains all the private stuff for the QuaZipFile class, thus |
34 |
allowing to preserve binary compatibility between releases, the |
35 |
technique known as the Pimpl (private implementation) idiom. |
36 |
*/ |
37 |
class QuaZipFilePrivate { |
38 |
friend class QuaZipFile; |
39 |
private: |
40 |
Q_DISABLE_COPY(QuaZipFilePrivate) |
41 |
/// The pointer to the associated QuaZipFile instance. |
42 |
QuaZipFile *q; |
43 |
/// The QuaZip object to work with. |
44 |
QuaZip *zip; |
45 |
/// The file name. |
46 |
QString fileName; |
47 |
/// Case sensitivity mode. |
48 |
QuaZip::CaseSensitivity caseSensitivity; |
49 |
/// Whether this file is opened in the raw mode. |
50 |
bool raw; |
51 |
/// Write position to keep track of. |
52 |
/** |
53 |
QIODevice::pos() is broken for non-seekable devices, so we need |
54 |
our own position. |
55 |
*/ |
56 |
qint64 writePos; |
57 |
/// Uncompressed size to write along with a raw file. |
58 |
quint64 uncompressedSize; |
59 |
/// CRC to write along with a raw file. |
60 |
quint32 crc; |
61 |
/// Whether \ref zip points to an internal QuaZip instance. |
62 |
/** |
63 |
This is true if the archive was opened by name, rather than by |
64 |
supplying an existing QuaZip instance. |
65 |
*/ |
66 |
bool internal; |
67 |
/// The last error. |
68 |
int zipError; |
69 |
/// Resets \ref zipError. |
70 |
inline void resetZipError() const {setZipError(UNZ_OK);} |
71 |
/// Sets the zip error. |
72 |
/** |
73 |
This function is marked as const although it changes one field. |
74 |
This allows to call it from const functions that don't change |
75 |
anything by themselves. |
76 |
*/ |
77 |
void setZipError(int zipError) const; |
78 |
/// The constructor for the corresponding QuaZipFile constructor. |
79 |
inline QuaZipFilePrivate(QuaZipFile *q): |
80 |
q(q), |
81 |
zip(NULL), |
82 |
caseSensitivity(QuaZip::csDefault), |
83 |
raw(false), |
84 |
writePos(0), |
85 |
uncompressedSize(0), |
86 |
crc(0), |
87 |
internal(true), |
88 |
zipError(UNZ_OK) {} |
89 |
/// The constructor for the corresponding QuaZipFile constructor. |
90 |
inline QuaZipFilePrivate(QuaZipFile *q, const QString &zipName): |
91 |
q(q), |
92 |
caseSensitivity(QuaZip::csDefault), |
93 |
raw(false), |
94 |
writePos(0), |
95 |
uncompressedSize(0), |
96 |
crc(0), |
97 |
internal(true), |
98 |
zipError(UNZ_OK) |
99 |
{ |
100 |
zip=new QuaZip(zipName); |
101 |
} |
102 |
/// The constructor for the corresponding QuaZipFile constructor. |
103 |
inline QuaZipFilePrivate(QuaZipFile *q, const QString &zipName, const QString &fileName, |
104 |
QuaZip::CaseSensitivity cs): |
105 |
q(q), |
106 |
raw(false), |
107 |
writePos(0), |
108 |
uncompressedSize(0), |
109 |
crc(0), |
110 |
internal(true), |
111 |
zipError(UNZ_OK) |
112 |
{ |
113 |
zip=new QuaZip(zipName); |
114 |
this->fileName=fileName; |
115 |
if (this->fileName.startsWith('/')) |
116 |
this->fileName = this->fileName.mid(1); |
117 |
this->caseSensitivity=cs; |
118 |
} |
119 |
/// The constructor for the QuaZipFile constructor accepting a file name. |
120 |
inline QuaZipFilePrivate(QuaZipFile *q, QuaZip *zip): |
121 |
q(q), |
122 |
zip(zip), |
123 |
raw(false), |
124 |
writePos(0), |
125 |
uncompressedSize(0), |
126 |
crc(0), |
127 |
internal(false), |
128 |
zipError(UNZ_OK) {} |
129 |
/// The destructor. |
130 |
inline ~QuaZipFilePrivate() |
131 |
{ |
132 |
if (internal) |
133 |
delete zip; |
134 |
} |
135 |
}; |
136 |
|
137 |
QuaZipFile::QuaZipFile(): |
138 |
p(new QuaZipFilePrivate(this)) |
139 |
{ |
140 |
} |
141 |
|
142 |
QuaZipFile::QuaZipFile(QObject *parent): |
143 |
QIODevice(parent), |
144 |
p(new QuaZipFilePrivate(this)) |
145 |
{ |
146 |
} |
147 |
|
148 |
QuaZipFile::QuaZipFile(const QString& zipName, QObject *parent): |
149 |
QIODevice(parent), |
150 |
p(new QuaZipFilePrivate(this, zipName)) |
151 |
{ |
152 |
} |
153 |
|
154 |
QuaZipFile::QuaZipFile(const QString& zipName, const QString& fileName, |
155 |
QuaZip::CaseSensitivity cs, QObject *parent): |
156 |
QIODevice(parent), |
157 |
p(new QuaZipFilePrivate(this, zipName, fileName, cs)) |
158 |
{ |
159 |
} |
160 |
|
161 |
QuaZipFile::QuaZipFile(QuaZip *zip, QObject *parent): |
162 |
QIODevice(parent), |
163 |
p(new QuaZipFilePrivate(this, zip)) |
164 |
{ |
165 |
} |
166 |
|
167 |
QuaZipFile::~QuaZipFile() |
168 |
{ |
169 |
if (isOpen()) |
170 |
close(); |
171 |
delete p; |
172 |
} |
173 |
|
174 |
QString QuaZipFile::getZipName() const |
175 |
{ |
176 |
return p->zip==NULL ? QString() : p->zip->getZipName(); |
177 |
} |
178 |
|
179 |
QuaZip *QuaZipFile::getZip() const |
180 |
{ |
181 |
return p->internal ? NULL : p->zip; |
182 |
} |
183 |
|
184 |
QString QuaZipFile::getActualFileName()const |
185 |
{ |
186 |
p->setZipError(UNZ_OK); |
187 |
if (p->zip == NULL || (openMode() & WriteOnly)) |
188 |
return QString(); |
189 |
QString name=p->zip->getCurrentFileName(); |
190 |
if(name.isNull()) |
191 |
p->setZipError(p->zip->getZipError()); |
192 |
return name; |
193 |
} |
194 |
|
195 |
void QuaZipFile::setZipName(const QString& zipName) |
196 |
{ |
197 |
if(isOpen()) { |
198 |
qWarning("QuaZipFile::setZipName(): file is already open - can not set ZIP name"); |
199 |
return; |
200 |
} |
201 |
if(p->zip!=NULL && p->internal) |
202 |
delete p->zip; |
203 |
p->zip=new QuaZip(zipName); |
204 |
p->internal=true; |
205 |
} |
206 |
|
207 |
void QuaZipFile::setZip(QuaZip *zip) |
208 |
{ |
209 |
if(isOpen()) { |
210 |
qWarning("QuaZipFile::setZip(): file is already open - can not set ZIP"); |
211 |
return; |
212 |
} |
213 |
if(p->zip!=NULL && p->internal) |
214 |
delete p->zip; |
215 |
p->zip=zip; |
216 |
p->fileName=QString(); |
217 |
p->internal=false; |
218 |
} |
219 |
|
220 |
void QuaZipFile::setFileName(const QString& fileName, QuaZip::CaseSensitivity cs) |
221 |
{ |
222 |
if(p->zip==NULL) { |
223 |
qWarning("QuaZipFile::setFileName(): call setZipName() first"); |
224 |
return; |
225 |
} |
226 |
if(!p->internal) { |
227 |
qWarning("QuaZipFile::setFileName(): should not be used when not using internal QuaZip"); |
228 |
return; |
229 |
} |
230 |
if(isOpen()) { |
231 |
qWarning("QuaZipFile::setFileName(): can not set file name for already opened file"); |
232 |
return; |
233 |
} |
234 |
p->fileName=fileName; |
235 |
if (p->fileName.startsWith('/')) |
236 |
p->fileName = p->fileName.mid(1); |
237 |
p->caseSensitivity=cs; |
238 |
} |
239 |
|
240 |
void QuaZipFilePrivate::setZipError(int zipError) const |
241 |
{ |
242 |
QuaZipFilePrivate *fakeThis = const_cast<QuaZipFilePrivate*>(this); // non-const |
243 |
fakeThis->zipError=zipError; |
244 |
if(zipError==UNZ_OK) |
245 |
q->setErrorString(QString()); |
246 |
else |
247 |
q->setErrorString(QuaZipFile::tr("ZIP/UNZIP API error %1").arg(zipError)); |
248 |
} |
249 |
|
250 |
bool QuaZipFile::open(OpenMode mode) |
251 |
{ |
252 |
return open(mode, NULL); |
253 |
} |
254 |
|
255 |
bool QuaZipFile::open(OpenMode mode, int *method, int *level, bool raw, const char *password) |
256 |
{ |
257 |
p->resetZipError(); |
258 |
if(isOpen()) { |
259 |
qWarning("QuaZipFile::open(): already opened"); |
260 |
return false; |
261 |
} |
262 |
if(mode&Unbuffered) { |
263 |
qWarning("QuaZipFile::open(): Unbuffered mode is not supported"); |
264 |
return false; |
265 |
} |
266 |
if((mode&ReadOnly)&&!(mode&WriteOnly)) { |
267 |
if(p->internal) { |
268 |
if(!p->zip->open(QuaZip::mdUnzip)) { |
269 |
p->setZipError(p->zip->getZipError()); |
270 |
return false; |
271 |
} |
272 |
if(!p->zip->setCurrentFile(p->fileName, p->caseSensitivity)) { |
273 |
p->setZipError(p->zip->getZipError()); |
274 |
p->zip->close(); |
275 |
return false; |
276 |
} |
277 |
} else { |
278 |
if(p->zip==NULL) { |
279 |
qWarning("QuaZipFile::open(): zip is NULL"); |
280 |
return false; |
281 |
} |
282 |
if(p->zip->getMode()!=QuaZip::mdUnzip) { |
283 |
qWarning("QuaZipFile::open(): file open mode %d incompatible with ZIP open mode %d", |
284 |
(int)mode, (int)p->zip->getMode()); |
285 |
return false; |
286 |
} |
287 |
if(!p->zip->hasCurrentFile()) { |
288 |
qWarning("QuaZipFile::open(): zip does not have current file"); |
289 |
return false; |
290 |
} |
291 |
} |
292 |
p->setZipError(unzOpenCurrentFile3(p->zip->getUnzFile(), method, level, (int)raw, password)); |
293 |
if(p->zipError==UNZ_OK) { |
294 |
setOpenMode(mode); |
295 |
p->raw=raw; |
296 |
return true; |
297 |
} else |
298 |
return false; |
299 |
} |
300 |
qWarning("QuaZipFile::open(): open mode %d not supported by this function", (int)mode); |
301 |
return false; |
302 |
} |
303 |
|
304 |
bool QuaZipFile::open(OpenMode mode, const QuaZipNewInfo& info, |
305 |
const char *password, quint32 crc, |
306 |
int method, int level, bool raw, |
307 |
int windowBits, int memLevel, int strategy) |
308 |
{ |
309 |
zip_fileinfo info_z; |
310 |
p->resetZipError(); |
311 |
if(isOpen()) { |
312 |
qWarning("QuaZipFile::open(): already opened"); |
313 |
return false; |
314 |
} |
315 |
if((mode&WriteOnly)&&!(mode&ReadOnly)) { |
316 |
if(p->internal) { |
317 |
qWarning("QuaZipFile::open(): write mode is incompatible with internal QuaZip approach"); |
318 |
return false; |
319 |
} |
320 |
if(p->zip==NULL) { |
321 |
qWarning("QuaZipFile::open(): zip is NULL"); |
322 |
return false; |
323 |
} |
324 |
if(p->zip->getMode()!=QuaZip::mdCreate&&p->zip->getMode()!=QuaZip::mdAppend&&p->zip->getMode()!=QuaZip::mdAdd) { |
325 |
qWarning("QuaZipFile::open(): file open mode %d incompatible with ZIP open mode %d", |
326 |
(int)mode, (int)p->zip->getMode()); |
327 |
return false; |
328 |
} |
329 |
info_z.tmz_date.tm_year=info.dateTime.date().year(); |
330 |
info_z.tmz_date.tm_mon=info.dateTime.date().month() - 1; |
331 |
info_z.tmz_date.tm_mday=info.dateTime.date().day(); |
332 |
info_z.tmz_date.tm_hour=info.dateTime.time().hour(); |
333 |
info_z.tmz_date.tm_min=info.dateTime.time().minute(); |
334 |
info_z.tmz_date.tm_sec=info.dateTime.time().second(); |
335 |
info_z.dosDate = 0; |
336 |
info_z.internal_fa=(uLong)info.internalAttr; |
337 |
info_z.external_fa=(uLong)info.externalAttr; |
338 |
if (p->zip->isDataDescriptorWritingEnabled()) |
339 |
zipSetFlags(p->zip->getZipFile(), ZIP_WRITE_DATA_DESCRIPTOR); |
340 |
else |
341 |
zipClearFlags(p->zip->getZipFile(), ZIP_WRITE_DATA_DESCRIPTOR); |
342 |
p->setZipError(zipOpenNewFileInZip3_64(p->zip->getZipFile(), |
343 |
p->zip->getFileNameCodec()->fromUnicode(info.name).constData(), &info_z, |
344 |
info.extraLocal.constData(), info.extraLocal.length(), |
345 |
info.extraGlobal.constData(), info.extraGlobal.length(), |
346 |
p->zip->getCommentCodec()->fromUnicode(info.comment).constData(), |
347 |
method, level, (int)raw, |
348 |
windowBits, memLevel, strategy, |
349 |
password, (uLong)crc, p->zip->isZip64Enabled())); |
350 |
if(p->zipError==UNZ_OK) { |
351 |
p->writePos=0; |
352 |
setOpenMode(mode); |
353 |
p->raw=raw; |
354 |
if(raw) { |
355 |
p->crc=crc; |
356 |
p->uncompressedSize=info.uncompressedSize; |
357 |
} |
358 |
return true; |
359 |
} else |
360 |
return false; |
361 |
} |
362 |
qWarning("QuaZipFile::open(): open mode %d not supported by this function", (int)mode); |
363 |
return false; |
364 |
} |
365 |
|
366 |
bool QuaZipFile::isSequential()const |
367 |
{ |
368 |
return true; |
369 |
} |
370 |
|
371 |
qint64 QuaZipFile::pos()const |
372 |
{ |
373 |
if(p->zip==NULL) { |
374 |
qWarning("QuaZipFile::pos(): call setZipName() or setZip() first"); |
375 |
return -1; |
376 |
} |
377 |
if(!isOpen()) { |
378 |
qWarning("QuaZipFile::pos(): file is not open"); |
379 |
return -1; |
380 |
} |
381 |
if(openMode()&ReadOnly) |
382 |
// QIODevice::pos() is broken for sequential devices, |
383 |
// but thankfully bytesAvailable() returns the number of |
384 |
// bytes buffered, so we know how far ahead we are. |
385 |
return unztell(p->zip->getUnzFile()) - QIODevice::bytesAvailable(); |
386 |
else |
387 |
return p->writePos; |
388 |
} |
389 |
|
390 |
bool QuaZipFile::atEnd()const |
391 |
{ |
392 |
if(p->zip==NULL) { |
393 |
qWarning("QuaZipFile::atEnd(): call setZipName() or setZip() first"); |
394 |
return false; |
395 |
} |
396 |
if(!isOpen()) { |
397 |
qWarning("QuaZipFile::atEnd(): file is not open"); |
398 |
return false; |
399 |
} |
400 |
if(openMode()&ReadOnly) |
401 |
// the same problem as with pos() |
402 |
return QIODevice::bytesAvailable() == 0 |
403 |
&& unzeof(p->zip->getUnzFile())==1; |
404 |
else |
405 |
return true; |
406 |
} |
407 |
|
408 |
qint64 QuaZipFile::size()const |
409 |
{ |
410 |
if(!isOpen()) { |
411 |
qWarning("QuaZipFile::atEnd(): file is not open"); |
412 |
return -1; |
413 |
} |
414 |
if(openMode()&ReadOnly) |
415 |
return p->raw?csize():usize(); |
416 |
else |
417 |
return p->writePos; |
418 |
} |
419 |
|
420 |
qint64 QuaZipFile::csize()const |
421 |
{ |
422 |
unz_file_info64 info_z; |
423 |
p->setZipError(UNZ_OK); |
424 |
if(p->zip==NULL||p->zip->getMode()!=QuaZip::mdUnzip) return -1; |
425 |
p->setZipError(unzGetCurrentFileInfo64(p->zip->getUnzFile(), &info_z, NULL, 0, NULL, 0, NULL, 0)); |
426 |
if(p->zipError!=UNZ_OK) |
427 |
return -1; |
428 |
return info_z.compressed_size; |
429 |
} |
430 |
|
431 |
qint64 QuaZipFile::usize()const |
432 |
{ |
433 |
unz_file_info64 info_z; |
434 |
p->setZipError(UNZ_OK); |
435 |
if(p->zip==NULL||p->zip->getMode()!=QuaZip::mdUnzip) return -1; |
436 |
p->setZipError(unzGetCurrentFileInfo64(p->zip->getUnzFile(), &info_z, NULL, 0, NULL, 0, NULL, 0)); |
437 |
if(p->zipError!=UNZ_OK) |
438 |
return -1; |
439 |
return info_z.uncompressed_size; |
440 |
} |
441 |
|
442 |
bool QuaZipFile::getFileInfo(QuaZipFileInfo *info) |
443 |
{ |
444 |
QuaZipFileInfo64 info64; |
445 |
if (getFileInfo(&info64)) { |
446 |
info64.toQuaZipFileInfo(*info); |
447 |
return true; |
448 |
} else { |
449 |
return false; |
450 |
} |
451 |
} |
452 |
|
453 |
bool QuaZipFile::getFileInfo(QuaZipFileInfo64 *info) |
454 |
{ |
455 |
if(p->zip==NULL||p->zip->getMode()!=QuaZip::mdUnzip) return false; |
456 |
p->zip->getCurrentFileInfo(info); |
457 |
p->setZipError(p->zip->getZipError()); |
458 |
return p->zipError==UNZ_OK; |
459 |
} |
460 |
|
461 |
void QuaZipFile::close() |
462 |
{ |
463 |
p->resetZipError(); |
464 |
if(p->zip==NULL||!p->zip->isOpen()) return; |
465 |
if(!isOpen()) { |
466 |
qWarning("QuaZipFile::close(): file isn't open"); |
467 |
return; |
468 |
} |
469 |
if(openMode()&ReadOnly) |
470 |
p->setZipError(unzCloseCurrentFile(p->zip->getUnzFile())); |
471 |
else if(openMode()&WriteOnly) |
472 |
if(isRaw()) p->setZipError(zipCloseFileInZipRaw64(p->zip->getZipFile(), p->uncompressedSize, p->crc)); |
473 |
else p->setZipError(zipCloseFileInZip(p->zip->getZipFile())); |
474 |
else { |
475 |
qWarning("Wrong open mode: %d", (int)openMode()); |
476 |
return; |
477 |
} |
478 |
if(p->zipError==UNZ_OK) setOpenMode(QIODevice::NotOpen); |
479 |
else return; |
480 |
if(p->internal) { |
481 |
p->zip->close(); |
482 |
p->setZipError(p->zip->getZipError()); |
483 |
} |
484 |
} |
485 |
|
486 |
qint64 QuaZipFile::readData(char *data, qint64 maxSize) |
487 |
{ |
488 |
p->setZipError(UNZ_OK); |
489 |
qint64 bytesRead=unzReadCurrentFile(p->zip->getUnzFile(), data, (unsigned)maxSize); |
490 |
if (bytesRead < 0) { |
491 |
p->setZipError((int) bytesRead); |
492 |
return -1; |
493 |
} |
494 |
return bytesRead; |
495 |
} |
496 |
|
497 |
qint64 QuaZipFile::writeData(const char* data, qint64 maxSize) |
498 |
{ |
499 |
p->setZipError(ZIP_OK); |
500 |
p->setZipError(zipWriteInFileInZip(p->zip->getZipFile(), data, (uint)maxSize)); |
501 |
if(p->zipError!=ZIP_OK) return -1; |
502 |
else { |
503 |
p->writePos+=maxSize; |
504 |
return maxSize; |
505 |
} |
506 |
} |
507 |
|
508 |
QString QuaZipFile::getFileName() const |
509 |
{ |
510 |
return p->fileName; |
511 |
} |
512 |
|
513 |
QuaZip::CaseSensitivity QuaZipFile::getCaseSensitivity() const |
514 |
{ |
515 |
return p->caseSensitivity; |
516 |
} |
517 |
|
518 |
bool QuaZipFile::isRaw() const |
519 |
{ |
520 |
return p->raw; |
521 |
} |
522 |
|
523 |
int QuaZipFile::getZipError() const |
524 |
{ |
525 |
return p->zipError; |
526 |
} |
527 |
|
528 |
qint64 QuaZipFile::bytesAvailable() const |
529 |
{ |
530 |
return size() - pos(); |
531 |
} |