00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #include "unzip.h"
00029 #include "unzip_p.h"
00030 #include "zipentry_p.h"
00031
00032 #include <QString>
00033 #include <QStringList>
00034 #include <QDir>
00035 #include <QFile>
00036 #include <QCoreApplication>
00037
00038
00039 #include <QtDebug>
00040
00078
00079 #define UNZIP_LOCAL_HEADER_SIZE 26
00081 #define UNZIP_CD_ENTRY_SIZE_NS 42
00083 #define UNZIP_DD_SIZE 12
00085 #define UNZIP_EOCD_SIZE 22
00087 #define UNZIP_LOCAL_ENC_HEADER_SIZE 12
00088
00089
00090 #define UNZIP_CD_OFF_VERSION 0
00091 #define UNZIP_CD_OFF_GPFLAG 4
00092 #define UNZIP_CD_OFF_CMETHOD 6
00093 #define UNZIP_CD_OFF_MODT 8
00094 #define UNZIP_CD_OFF_MODD 10
00095 #define UNZIP_CD_OFF_CRC32 12
00096 #define UNZIP_CD_OFF_CSIZE 16
00097 #define UNZIP_CD_OFF_USIZE 20
00098 #define UNZIP_CD_OFF_NAMELEN 24
00099 #define UNZIP_CD_OFF_XLEN 26
00100 #define UNZIP_CD_OFF_COMMLEN 28
00101 #define UNZIP_CD_OFF_LHOFFSET 38
00102
00103
00104 #define UNZIP_LH_OFF_VERSION 0
00105 #define UNZIP_LH_OFF_GPFLAG 2
00106 #define UNZIP_LH_OFF_CMETHOD 4
00107 #define UNZIP_LH_OFF_MODT 6
00108 #define UNZIP_LH_OFF_MODD 8
00109 #define UNZIP_LH_OFF_CRC32 10
00110 #define UNZIP_LH_OFF_CSIZE 14
00111 #define UNZIP_LH_OFF_USIZE 18
00112 #define UNZIP_LH_OFF_NAMELEN 22
00113 #define UNZIP_LH_OFF_XLEN 24
00114
00115
00116 #define UNZIP_DD_OFF_CRC32 0
00117 #define UNZIP_DD_OFF_CSIZE 4
00118 #define UNZIP_DD_OFF_USIZE 8
00119
00120
00121 #define UNZIP_EOCD_OFF_ENTRIES 6
00122 #define UNZIP_EOCD_OFF_CDOFF 12
00123 #define UNZIP_EOCD_OFF_COMMLEN 16
00124
00131 #define UNZIP_VERSION 0x1B
00133 #define UNZIP_VERSION_STRICT 0x14
00134
00136 #define CRC32(c, b) crcTable[((int)c^b) & 0xff] ^ (c >> 8)
00137
00139 #define UNZIP_CHECK_FOR_VALID_DATA \
00140 {\
00141 if (headers != 0)\
00142 {\
00143 qDebug() << "Corrupted zip archive. Some files might be extracted.";\
00144 ec = headers->size() != 0 ? UnZip::PartiallyCorrupted : UnZip::Corrupted;\
00145 break;\
00146 }\
00147 else\
00148 {\
00149 delete device;\
00150 device = 0;\
00151 qDebug() << "Corrupted or invalid zip archive";\
00152 ec = UnZip::Corrupted;\
00153 break;\
00154 }\
00155 }
00156
00157
00158
00159
00160
00161
00165 UnZip::UnZip()
00166 {
00167 d = new UnzipPrivate;
00168 }
00169
00173 UnZip::~UnZip()
00174 {
00175 closeArchive();
00176 delete d;
00177 }
00178
00182 bool UnZip::isOpen() const
00183 {
00184 return d->device != 0;
00185 }
00186
00190 UnZip::ErrorCode UnZip::openArchive(const QString& filename)
00191 {
00192 QFile* file = new QFile(filename);
00193
00194 if (!file->exists()) {
00195 delete file;
00196 return UnZip::FileNotFound;
00197 }
00198
00199 if (!file->open(QIODevice::ReadOnly)) {
00200 delete file;
00201 return UnZip::OpenFailed;
00202 }
00203
00204 return openArchive(file);
00205 }
00206
00212 UnZip::ErrorCode UnZip::openArchive(QIODevice* device)
00213 {
00214 if (device == 0)
00215 {
00216 qDebug() << "Invalid device.";
00217 return UnZip::InvalidDevice;
00218 }
00219
00220 return d->openArchive(device);
00221 }
00222
00226 void UnZip::closeArchive()
00227 {
00228 d->closeArchive();
00229 }
00230
00231 QString UnZip::archiveComment() const
00232 {
00233 if (d->device == 0)
00234 return QString();
00235 return d->comment;
00236 }
00237
00241 QString UnZip::formatError(UnZip::ErrorCode c) const
00242 {
00243 switch (c)
00244 {
00245 case Ok: return QCoreApplication::translate("UnZip", "ZIP operation completed successfully."); break;
00246 case ZlibInit: return QCoreApplication::translate("UnZip", "Failed to initialize or load zlib library."); break;
00247 case ZlibError: return QCoreApplication::translate("UnZip", "zlib library error."); break;
00248 case OpenFailed: return QCoreApplication::translate("UnZip", "Unable to create or open file."); break;
00249 case PartiallyCorrupted: return QCoreApplication::translate("UnZip", "Partially corrupted archive. Some files might be extracted."); break;
00250 case Corrupted: return QCoreApplication::translate("UnZip", "Corrupted archive."); break;
00251 case WrongPassword: return QCoreApplication::translate("UnZip", "Wrong password."); break;
00252 case NoOpenArchive: return QCoreApplication::translate("UnZip", "No archive has been created yet."); break;
00253 case FileNotFound: return QCoreApplication::translate("UnZip", "File or directory does not exist."); break;
00254 case ReadFailed: return QCoreApplication::translate("UnZip", "File read error."); break;
00255 case WriteFailed: return QCoreApplication::translate("UnZip", "File write error."); break;
00256 case SeekFailed: return QCoreApplication::translate("UnZip", "File seek error."); break;
00257 case CreateDirFailed: return QCoreApplication::translate("UnZip", "Unable to create a directory."); break;
00258 case InvalidDevice: return QCoreApplication::translate("UnZip", "Invalid device."); break;
00259 case InvalidArchive: return QCoreApplication::translate("UnZip", "Invalid or incompatible zip archive."); break;
00260 case HeaderConsistencyError: return QCoreApplication::translate("UnZip", "Inconsistent headers. Archive might be corrupted."); break;
00261 default: ;
00262 }
00263
00264 return QCoreApplication::translate("UnZip", "Unknown error.");
00265 }
00266
00270 bool UnZip::contains(const QString& file) const
00271 {
00272 if (d->headers == 0)
00273 return false;
00274
00275 return d->headers->contains(file);
00276 }
00277
00281 QStringList UnZip::fileList() const
00282 {
00283 return d->headers == 0 ? QStringList() : d->headers->keys();
00284 }
00285
00289 QList<UnZip::ZipEntry> UnZip::entryList() const
00290 {
00291 QList<UnZip::ZipEntry> list;
00292
00293 if (d->headers != 0)
00294 {
00295 for (QMap<QString,ZipEntryP*>::ConstIterator it = d->headers->constBegin(); it != d->headers->constEnd(); ++it)
00296 {
00297 const ZipEntryP* entry = it.value();
00298 Q_ASSERT(entry != 0);
00299
00300 ZipEntry z;
00301
00302 z.filename = it.key();
00303 if (!entry->comment.isEmpty())
00304 z.comment = entry->comment;
00305 z.compressedSize = entry->szComp;
00306 z.uncompressedSize = entry->szUncomp;
00307 z.crc32 = entry->crc;
00308 z.lastModified = d->convertDateTime(entry->modDate, entry->modTime);
00309
00310 z.compression = entry->compMethod == 0 ? NoCompression : entry->compMethod == 8 ? Deflated : UnknownCompression;
00311 z.type = z.filename.endsWith("/") ? Directory : File;
00312
00313 z.encrypted = entry->isEncrypted();
00314
00315 list.append(z);
00316 }
00317 }
00318
00319 return list;
00320 }
00321
00325 UnZip::ErrorCode UnZip::extractAll(const QString& dirname, ExtractionOptions options)
00326 {
00327 return extractAll(QDir(dirname), options);
00328 }
00329
00333 UnZip::ErrorCode UnZip::extractAll(const QDir& dir, ExtractionOptions options)
00334 {
00335
00336 if (d->device == 0)
00337 return NoOpenArchive;
00338
00339 if (d->headers == 0)
00340 return Ok;
00341
00342 bool end = false;
00343 for (QMap<QString,ZipEntryP*>::Iterator itr = d->headers->begin(); itr != d->headers->end(); ++itr)
00344 {
00345 ZipEntryP* entry = itr.value();
00346 Q_ASSERT(entry != 0);
00347
00348 if ((entry->isEncrypted()) && d->skipAllEncrypted)
00349 continue;
00350
00351 switch (d->extractFile(itr.key(), *entry, dir, options))
00352 {
00353 case Corrupted:
00354 qDebug() << "Removing corrupted entry" << itr.key();
00355 d->headers->erase(itr++);
00356 if (itr == d->headers->end())
00357 end = true;
00358 break;
00359 case CreateDirFailed:
00360 break;
00361 case Skip:
00362 break;
00363 case SkipAll:
00364 d->skipAllEncrypted = true;
00365 break;
00366 default:
00367 ;
00368 }
00369
00370 if (end)
00371 break;
00372 }
00373
00374 return Ok;
00375 }
00376
00380 UnZip::ErrorCode UnZip::extractFile(const QString& filename, const QString& dirname, ExtractionOptions options)
00381 {
00382 return extractFile(filename, QDir(dirname), options);
00383 }
00384
00388 UnZip::ErrorCode UnZip::extractFile(const QString& filename, const QDir& dir, ExtractionOptions options)
00389 {
00390 QMap<QString,ZipEntryP*>::Iterator itr = d->headers->find(filename);
00391 if (itr != d->headers->end())
00392 {
00393 ZipEntryP* entry = itr.value();
00394 Q_ASSERT(entry != 0);
00395 return d->extractFile(itr.key(), *entry, dir, options);
00396 }
00397
00398 return FileNotFound;
00399 }
00400
00404 UnZip::ErrorCode UnZip::extractFile(const QString& filename, QIODevice* dev, ExtractionOptions options)
00405 {
00406 if (dev == 0)
00407 return InvalidDevice;
00408
00409 QMap<QString,ZipEntryP*>::Iterator itr = d->headers->find(filename);
00410 if (itr != d->headers->end()) {
00411 ZipEntryP* entry = itr.value();
00412 Q_ASSERT(entry != 0);
00413 return d->extractFile(itr.key(), *entry, dev, options);
00414 }
00415
00416 return FileNotFound;
00417 }
00418
00423 UnZip::ErrorCode UnZip::extractFiles(const QStringList& filenames, const QString& dirname, ExtractionOptions options)
00424 {
00425 QDir dir(dirname);
00426 ErrorCode ec;
00427
00428 for (QStringList::ConstIterator itr = filenames.constBegin(); itr != filenames.constEnd(); ++itr)
00429 {
00430 ec = extractFile(*itr, dir, options);
00431 if (ec == FileNotFound)
00432 continue;
00433 if (ec != Ok)
00434 return ec;
00435 }
00436
00437 return Ok;
00438 }
00439
00444 UnZip::ErrorCode UnZip::extractFiles(const QStringList& filenames, const QDir& dir, ExtractionOptions options)
00445 {
00446 ErrorCode ec;
00447
00448 for (QStringList::ConstIterator itr = filenames.constBegin(); itr != filenames.constEnd(); ++itr)
00449 {
00450 ec = extractFile(*itr, dir, options);
00451 if (ec == FileNotFound)
00452 continue;
00453 if (ec != Ok)
00454 return ec;
00455 }
00456
00457 return Ok;
00458 }
00459
00463 void UnZip::setPassword(const QString& pwd)
00464 {
00465 d->password = pwd;
00466 }
00467
00471 UnZip::ZipEntry::ZipEntry()
00472 {
00473 compressedSize = uncompressedSize = crc32 = 0;
00474 compression = NoCompression;
00475 type = File;
00476 encrypted = false;
00477 }
00478
00479
00480
00481
00482
00483
00485 UnzipPrivate::UnzipPrivate()
00486 {
00487 skipAllEncrypted = false;
00488 headers = 0;
00489 device = 0;
00490
00491 uBuffer = (unsigned char*) buffer1;
00492 crcTable = (quint32*) get_crc_table();
00493
00494 cdOffset = eocdOffset = 0;
00495 cdEntryCount = 0;
00496 unsupportedEntryCount = 0;
00497 }
00498
00500 UnZip::ErrorCode UnzipPrivate::openArchive(QIODevice* dev)
00501 {
00502 Q_ASSERT(dev != 0);
00503
00504 if (device != 0)
00505 closeArchive();
00506
00507 device = dev;
00508
00509 if (!(device->isOpen() || device->open(QIODevice::ReadOnly)))
00510 {
00511 delete device;
00512 device = 0;
00513
00514 qDebug() << "Unable to open device for reading";
00515 return UnZip::OpenFailed;
00516 }
00517
00518 UnZip::ErrorCode ec;
00519
00520 ec = seekToCentralDirectory();
00521 if (ec != UnZip::Ok)
00522 {
00523 closeArchive();
00524 return ec;
00525 }
00526
00528 if (cdEntryCount == 0)
00529 {
00530 return UnZip::Ok;
00531 }
00532
00533 bool continueParsing = true;
00534
00535 while (continueParsing)
00536 {
00537 if (device->read(buffer1, 4) != 4)
00538 UNZIP_CHECK_FOR_VALID_DATA
00539
00540 if (! (buffer1[0] == 'P' && buffer1[1] == 'K' && buffer1[2] == 0x01 && buffer1[3] == 0x02) )
00541 break;
00542
00543 if ( (ec = parseCentralDirectoryRecord()) != UnZip::Ok )
00544 break;
00545 }
00546
00547 if (ec != UnZip::Ok)
00548 closeArchive();
00549
00550 return ec;
00551 }
00552
00553
00554
00555
00556
00557
00558
00559
00560
00561
00562
00563
00564
00565
00566
00567
00568
00569
00570
00571
00572
00573
00574 UnZip::ErrorCode UnzipPrivate::parseLocalHeaderRecord(const QString& path, ZipEntryP& entry)
00575 {
00576 if (!device->seek(entry.lhOffset))
00577 return UnZip::SeekFailed;
00578
00579
00580 if (device->read(buffer1, 4) != 4)
00581 return UnZip::ReadFailed;
00582
00583 if ((buffer1[0] != 'P') || (buffer1[1] != 'K') || (buffer1[2] != 0x03) || (buffer1[3] != 0x04))
00584 return UnZip::InvalidArchive;
00585
00586 if (device->read(buffer1, UNZIP_LOCAL_HEADER_SIZE) != UNZIP_LOCAL_HEADER_SIZE)
00587 return UnZip::ReadFailed;
00588
00589
00590
00591
00592
00593
00594
00595
00596
00597 bool hasDataDescriptor = entry.hasDataDescriptor();
00598
00599 bool checkFailed = false;
00600
00601 if (!checkFailed)
00602 checkFailed = entry.compMethod != getUShort(uBuffer, UNZIP_LH_OFF_CMETHOD);
00603 if (!checkFailed)
00604 checkFailed = entry.gpFlag[0] != uBuffer[UNZIP_LH_OFF_GPFLAG];
00605 if (!checkFailed)
00606 checkFailed = entry.gpFlag[1] != uBuffer[UNZIP_LH_OFF_GPFLAG + 1];
00607 if (!checkFailed)
00608 checkFailed = entry.modTime[0] != uBuffer[UNZIP_LH_OFF_MODT];
00609 if (!checkFailed)
00610 checkFailed = entry.modTime[1] != uBuffer[UNZIP_LH_OFF_MODT + 1];
00611 if (!checkFailed)
00612 checkFailed = entry.modDate[0] != uBuffer[UNZIP_LH_OFF_MODD];
00613 if (!checkFailed)
00614 checkFailed = entry.modDate[1] != uBuffer[UNZIP_LH_OFF_MODD + 1];
00615 if (!hasDataDescriptor)
00616 {
00617 if (!checkFailed)
00618 checkFailed = entry.crc != getULong(uBuffer, UNZIP_LH_OFF_CRC32);
00619 if (!checkFailed)
00620 checkFailed = entry.szComp != getULong(uBuffer, UNZIP_LH_OFF_CSIZE);
00621 if (!checkFailed)
00622 checkFailed = entry.szUncomp != getULong(uBuffer, UNZIP_LH_OFF_USIZE);
00623 }
00624
00625 if (checkFailed)
00626 return UnZip::HeaderConsistencyError;
00627
00628
00629 quint16 szName = getUShort(uBuffer, UNZIP_LH_OFF_NAMELEN);
00630 if (szName == 0)
00631 return UnZip::HeaderConsistencyError;
00632
00633 if (device->read(buffer2, szName) != szName)
00634 return UnZip::ReadFailed;
00635
00636 QString filename = QString::fromAscii(buffer2, szName);
00637 if (filename != path)
00638 {
00639 qDebug() << "Filename in local header mismatches.";
00640 return UnZip::HeaderConsistencyError;
00641 }
00642
00643
00644 quint16 szExtra = getUShort(uBuffer, UNZIP_LH_OFF_XLEN);
00645 if (szExtra != 0)
00646 {
00647 if (!device->seek(device->pos() + szExtra))
00648 return UnZip::SeekFailed;
00649 }
00650
00651 entry.dataOffset = device->pos();
00652
00653 if (hasDataDescriptor)
00654 {
00655
00656
00657
00658
00659
00660 if (!device->seek(device->pos() + entry.szComp))
00661 return UnZip::SeekFailed;
00662
00663
00664 if (device->read(buffer2, 4) != 4)
00665 return UnZip::ReadFailed;
00666
00667 bool hasSignature = buffer2[0] == 'P' && buffer2[1] == 'K' && buffer2[2] == 0x07 && buffer2[3] == 0x08;
00668 if (hasSignature)
00669 {
00670 if (device->read(buffer2, UNZIP_DD_SIZE) != UNZIP_DD_SIZE)
00671 return UnZip::ReadFailed;
00672 }
00673 else
00674 {
00675 if (device->read(buffer2 + 4, UNZIP_DD_SIZE - 4) != UNZIP_DD_SIZE - 4)
00676 return UnZip::ReadFailed;
00677 }
00678
00679
00680 if (
00681 entry.crc != getULong((unsigned char*)buffer2, UNZIP_DD_OFF_CRC32) ||
00682 entry.szComp != getULong((unsigned char*)buffer2, UNZIP_DD_OFF_CSIZE) ||
00683 entry.szUncomp != getULong((unsigned char*)buffer2, UNZIP_DD_OFF_USIZE)
00684 )
00685 return UnZip::HeaderConsistencyError;
00686 }
00687
00688 return UnZip::Ok;
00689 }
00690
00712 UnZip::ErrorCode UnzipPrivate::seekToCentralDirectory()
00713 {
00714 qint64 length = device->size();
00715 qint64 offset = length - UNZIP_EOCD_SIZE;
00716
00717 if (length < UNZIP_EOCD_SIZE)
00718 return UnZip::InvalidArchive;
00719
00720 if (!device->seek( offset ))
00721 return UnZip::SeekFailed;
00722
00723 if (device->read(buffer1, UNZIP_EOCD_SIZE) != UNZIP_EOCD_SIZE)
00724 return UnZip::ReadFailed;
00725
00726 bool eocdFound = (buffer1[0] == 'P' && buffer1[1] == 'K' && buffer1[2] == 0x05 && buffer1[3] == 0x06);
00727
00728 if (eocdFound)
00729 {
00730
00731 eocdOffset = offset;
00732 }
00733 else
00734 {
00735 qint64 read;
00736 char* p = 0;
00737
00738 offset -= UNZIP_EOCD_SIZE;
00739
00740 if (offset <= 0)
00741 return UnZip::InvalidArchive;
00742
00743 if (!device->seek( offset ))
00744 return UnZip::SeekFailed;
00745
00746 while ((read = device->read(buffer1, UNZIP_EOCD_SIZE)) >= 0)
00747 {
00748 if ( (p = strstr(buffer1, "PK\5\6")) != 0)
00749 {
00750
00751
00752
00753 device->seek( offset + (p - buffer1) );
00754 eocdFound = true;
00755 eocdOffset = offset + (p - buffer1);
00756
00757
00758 if (device->read(buffer1, UNZIP_EOCD_SIZE) != UNZIP_EOCD_SIZE)
00759 return UnZip::ReadFailed;
00760
00761 break;
00762 }
00763
00764 offset -= UNZIP_EOCD_SIZE;
00765 if (offset <= 0)
00766 return UnZip::InvalidArchive;
00767
00768 if (!device->seek( offset ))
00769 return UnZip::SeekFailed;
00770 }
00771 }
00772
00773 if (!eocdFound)
00774 return UnZip::InvalidArchive;
00775
00776
00777 offset = getULong((const unsigned char*)buffer1, UNZIP_EOCD_OFF_CDOFF + 4);
00778
00779 cdOffset = offset;
00780
00781 cdEntryCount = getUShort((const unsigned char*)buffer1, UNZIP_EOCD_OFF_ENTRIES + 4);
00782
00783 quint16 commentLength = getUShort((const unsigned char*)buffer1, UNZIP_EOCD_OFF_COMMLEN + 4);
00784 if (commentLength != 0)
00785 {
00786 QByteArray c = device->read(commentLength);
00787 if (c.count() != commentLength)
00788 return UnZip::ReadFailed;
00789
00790 comment = c;
00791 }
00792
00793
00794 if (!device->seek( cdOffset ))
00795 return UnZip::SeekFailed;
00796
00797 return UnZip::Ok;
00798 }
00799
00836 UnZip::ErrorCode UnzipPrivate::parseCentralDirectoryRecord()
00837 {
00838
00839 if (device->read(buffer1, UNZIP_CD_ENTRY_SIZE_NS) != UNZIP_CD_ENTRY_SIZE_NS)
00840 return UnZip::ReadFailed;
00841
00842 bool skipEntry = false;
00843
00844
00845 quint16 compMethod = getUShort(uBuffer, UNZIP_CD_OFF_CMETHOD);
00846
00847
00848
00849 quint16 szName = getUShort(uBuffer, UNZIP_CD_OFF_NAMELEN);
00850 quint16 szExtra = getUShort(uBuffer, UNZIP_CD_OFF_XLEN);
00851 quint16 szComment = getUShort(uBuffer, UNZIP_CD_OFF_COMMLEN);
00852
00853 quint32 skipLength = szName + szExtra + szComment;
00854
00855 UnZip::ErrorCode ec = UnZip::Ok;
00856
00857 if ((compMethod != 0) && (compMethod != 8))
00858 {
00859 qDebug() << "Unsupported compression method. Skipping file.";
00860 skipEntry = true;
00861 }
00862
00863
00864 if (!skipEntry && buffer1[UNZIP_CD_OFF_VERSION] > UNZIP_VERSION)
00865 {
00866 qDebug() << "Unsupported PKZip version. Skipping file.";
00867 skipEntry = true;
00868 }
00869
00870 if (!skipEntry && szName == 0)
00871 {
00872 qDebug() << "Skipping file with no name.";
00873 skipEntry = true;
00874 }
00875
00876 if (!skipEntry && device->read(buffer2, szName) != szName)
00877 {
00878 ec = UnZip::ReadFailed;
00879 skipEntry = true;
00880 }
00881
00882 if (skipEntry)
00883 {
00884 if (ec == UnZip::Ok)
00885 {
00886 if (!device->seek( device->pos() + skipLength ))
00887 ec = UnZip::SeekFailed;
00888
00889 unsupportedEntryCount++;
00890 }
00891
00892 return ec;
00893 }
00894
00895 QString filename = QString::fromAscii(buffer2, szName);
00896
00897 ZipEntryP* h = new ZipEntryP;
00898 h->compMethod = compMethod;
00899
00900 h->gpFlag[0] = buffer1[UNZIP_CD_OFF_GPFLAG];
00901 h->gpFlag[1] = buffer1[UNZIP_CD_OFF_GPFLAG + 1];
00902
00903 h->modTime[0] = buffer1[UNZIP_CD_OFF_MODT];
00904 h->modTime[1] = buffer1[UNZIP_CD_OFF_MODT + 1];
00905
00906 h->modDate[0] = buffer1[UNZIP_CD_OFF_MODD];
00907 h->modDate[1] = buffer1[UNZIP_CD_OFF_MODD + 1];
00908
00909 h->crc = getULong(uBuffer, UNZIP_CD_OFF_CRC32);
00910 h->szComp = getULong(uBuffer, UNZIP_CD_OFF_CSIZE);
00911 h->szUncomp = getULong(uBuffer, UNZIP_CD_OFF_USIZE);
00912
00913
00914 if (szExtra != 0)
00915 {
00916 if (!device->seek( device->pos() + szExtra ))
00917 {
00918 delete h;
00919 return UnZip::SeekFailed;
00920 }
00921 }
00922
00923
00924 if (szComment != 0)
00925 {
00926 if (device->read(buffer2, szComment) != szComment)
00927 {
00928 delete h;
00929 return UnZip::ReadFailed;
00930 }
00931
00932 h->comment = QString::fromAscii(buffer2, szComment);
00933 }
00934
00935 h->lhOffset = getULong(uBuffer, UNZIP_CD_OFF_LHOFFSET);
00936
00937 if (headers == 0)
00938 headers = new QMap<QString, ZipEntryP*>();
00939 headers->insert(filename, h);
00940
00941 return UnZip::Ok;
00942 }
00943
00945 void UnzipPrivate::closeArchive()
00946 {
00947 if (device == 0)
00948 return;
00949
00950 skipAllEncrypted = false;
00951
00952 if (headers != 0)
00953 {
00954 qDeleteAll(*headers);
00955 delete headers;
00956 headers = 0;
00957 }
00958
00959 delete device; device = 0;
00960
00961 cdOffset = eocdOffset = 0;
00962 cdEntryCount = 0;
00963 unsupportedEntryCount = 0;
00964
00965 comment.clear();
00966 }
00967
00969 UnZip::ErrorCode UnzipPrivate::extractFile(const QString& path, ZipEntryP& entry, const QDir& dir, UnZip::ExtractionOptions options)
00970 {
00971 QString name(path);
00972 QString dirname;
00973 QString directory;
00974
00975 int pos = name.lastIndexOf('/');
00976
00977
00978 if (pos == name.length() - 1)
00979 {
00980 if (options.testFlag(UnZip::SkipPaths))
00981 return UnZip::Ok;
00982
00983 directory = QString("%1/%2").arg(dir.absolutePath()).arg(QDir::cleanPath(name));
00984 if (!createDirectory(directory))
00985 {
00986 qDebug() << QString("Unable to create directory: %1").arg(directory);
00987 return UnZip::CreateDirFailed;
00988 }
00989
00990 return UnZip::Ok;
00991 }
00992
00993
00994 if (pos > 0)
00995 {
00996
00997 dirname = name.left(pos);
00998 if (options.testFlag(UnZip::SkipPaths))
00999 {
01000 directory = dir.absolutePath();
01001 }
01002 else
01003 {
01004 directory = QString("%1/%2").arg(dir.absolutePath()).arg(QDir::cleanPath(dirname));
01005 if (!createDirectory(directory))
01006 {
01007 qDebug() << QString("Unable to create directory: %1").arg(directory);
01008 return UnZip::CreateDirFailed;
01009 }
01010 }
01011 name = name.right(name.length() - pos - 1);
01012 } else directory = dir.absolutePath();
01013
01014 name = QString("%1/%2").arg(directory).arg(name);
01015
01016 QFile outFile(name);
01017
01018 if (!outFile.open(QIODevice::WriteOnly))
01019 {
01020 qDebug() << QString("Unable to open %1 for writing").arg(name);
01021 return UnZip::OpenFailed;
01022 }
01023
01025
01026 UnZip::ErrorCode ec = extractFile(path, entry, &outFile, options);
01027
01028 outFile.close();
01029
01030 if (ec != UnZip::Ok)
01031 {
01032 if (!outFile.remove())
01033 qDebug() << QString("Unable to remove corrupted file: %1").arg(name);
01034 }
01035
01036 return ec;
01037 }
01038
01040 UnZip::ErrorCode UnzipPrivate::extractFile(const QString& path, ZipEntryP& entry, QIODevice* dev, UnZip::ExtractionOptions options)
01041 {
01042 Q_UNUSED(options);
01043 Q_ASSERT(dev != 0);
01044
01045 if (!entry.lhEntryChecked)
01046 {
01047 UnZip::ErrorCode ec = parseLocalHeaderRecord(path, entry);
01048 entry.lhEntryChecked = true;
01049
01050 if (ec != UnZip::Ok)
01051 return ec;
01052 }
01053
01054 if (!device->seek(entry.dataOffset))
01055 return UnZip::SeekFailed;
01056
01057
01058 quint32 keys[3];
01059
01060 if (entry.isEncrypted())
01061 {
01062 UnZip::ErrorCode e = testPassword(keys, path, entry);
01063 if (e != UnZip::Ok)
01064 {
01065 qDebug() << QString("Unable to decrypt %1").arg(path);
01066 return e;
01067 }
01068 entry.szComp -= UNZIP_LOCAL_ENC_HEADER_SIZE;
01069 }
01070
01071 if (entry.szComp == 0)
01072 {
01073 if (entry.crc != 0)
01074 return UnZip::Corrupted;
01075
01076 return UnZip::Ok;
01077 }
01078
01079 uInt rep = entry.szComp / UNZIP_READ_BUFFER;
01080 uInt rem = entry.szComp % UNZIP_READ_BUFFER;
01081 uInt cur = 0;
01082
01083
01084 qint64 read;
01085 quint64 tot = 0;
01086
01087 quint32 myCRC = crc32(0L, Z_NULL, 0);
01088
01089 if (entry.compMethod == 0)
01090 {
01091 while ( (read = device->read(buffer1, cur < rep ? UNZIP_READ_BUFFER : rem)) > 0 )
01092 {
01093 if (entry.isEncrypted())
01094 decryptBytes(keys, buffer1, read);
01095
01096 myCRC = crc32(myCRC, uBuffer, read);
01097
01098 if (dev->write(buffer1, read) != read)
01099 return UnZip::WriteFailed;
01100
01101 cur++;
01102 tot += read;
01103
01104 if (tot == entry.szComp)
01105 break;
01106 }
01107
01108 if (read < 0)
01109 return UnZip::ReadFailed;
01110 }
01111 else if (entry.compMethod == 8)
01112 {
01113
01114 z_stream zstr;
01115 zstr.zalloc = Z_NULL;
01116 zstr.zfree = Z_NULL;
01117 zstr.opaque = Z_NULL;
01118 zstr.next_in = Z_NULL;
01119 zstr.avail_in = 0;
01120
01121 int zret;
01122
01123
01124 if ( (zret = inflateInit2_(&zstr, -MAX_WBITS, ZLIB_VERSION, sizeof(z_stream))) != Z_OK )
01125 return UnZip::ZlibError;
01126
01127 int szDecomp;
01128
01129
01130 do
01131 {
01132 read = device->read(buffer1, cur < rep ? UNZIP_READ_BUFFER : rem);
01133 if (read == 0)
01134 break;
01135 if (read < 0)
01136 {
01137 (void)inflateEnd(&zstr);
01138 return UnZip::ReadFailed;
01139 }
01140
01141 if (entry.isEncrypted())
01142 decryptBytes(keys, buffer1, read);
01143
01144 cur++;
01145 tot += read;
01146
01147 zstr.avail_in = (uInt) read;
01148 zstr.next_in = (Bytef*) buffer1;
01149
01150
01151
01152 do {
01153 zstr.avail_out = UNZIP_READ_BUFFER;
01154 zstr.next_out = (Bytef*) buffer2;;
01155
01156 zret = inflate(&zstr, Z_NO_FLUSH);
01157
01158 switch (zret) {
01159 case Z_NEED_DICT:
01160 case Z_DATA_ERROR:
01161 case Z_MEM_ERROR:
01162 inflateEnd(&zstr);
01163 return UnZip::WriteFailed;
01164 default:
01165 ;
01166 }
01167
01168 szDecomp = UNZIP_READ_BUFFER - zstr.avail_out;
01169 if (dev->write(buffer2, szDecomp) != szDecomp)
01170 {
01171 inflateEnd(&zstr);
01172 return UnZip::ZlibError;
01173 }
01174
01175 myCRC = crc32(myCRC, (const Bytef*) buffer2, szDecomp);
01176
01177 } while (zstr.avail_out == 0);
01178
01179 }
01180 while (zret != Z_STREAM_END);
01181
01182 inflateEnd(&zstr);
01183 }
01184
01185 if (myCRC != entry.crc)
01186 return UnZip::Corrupted;
01187
01188 return UnZip::Ok;
01189 }
01190
01192 bool UnzipPrivate::createDirectory(const QString& path)
01193 {
01194 QDir d(path);
01195 if (!d.exists())
01196 {
01197 int sep = path.lastIndexOf("/");
01198 if (sep <= 0) return true;
01199
01200 if (!createDirectory(path.left(sep)))
01201 return false;
01202
01203 if (!d.mkdir(path))
01204 {
01205 qDebug() << QString("Unable to create directory: %1").arg(path);
01206 return false;
01207 }
01208 }
01209
01210 return true;
01211 }
01212
01216 quint32 UnzipPrivate::getULong(const unsigned char* data, quint32 offset) const
01217 {
01218 quint32 res = (quint32) data[offset];
01219 res |= (((quint32)data[offset+1]) << 8);
01220 res |= (((quint32)data[offset+2]) << 16);
01221 res |= (((quint32)data[offset+3]) << 24);
01222
01223 return res;
01224 }
01225
01229 quint64 UnzipPrivate::getULLong(const unsigned char* data, quint32 offset) const
01230 {
01231 quint64 res = (quint64) data[offset];
01232 res |= (((quint64)data[offset+1]) << 8);
01233 res |= (((quint64)data[offset+2]) << 16);
01234 res |= (((quint64)data[offset+3]) << 24);
01235 res |= (((quint64)data[offset+1]) << 32);
01236 res |= (((quint64)data[offset+2]) << 40);
01237 res |= (((quint64)data[offset+3]) << 48);
01238 res |= (((quint64)data[offset+3]) << 56);
01239
01240 return res;
01241 }
01242
01246 quint16 UnzipPrivate::getUShort(const unsigned char* data, quint32 offset) const
01247 {
01248 return (quint16) data[offset] | (((quint16)data[offset+1]) << 8);
01249 }
01250
01254 int UnzipPrivate::decryptByte(quint32 key2) const
01255 {
01256 quint16 temp = ((quint16)(key2) & 0xffff) | 2;
01257 return (int)(((temp * (temp ^ 1)) >> 8) & 0xff);
01258 }
01259
01263 void UnzipPrivate::updateKeys(quint32* keys, int c) const
01264 {
01265 keys[0] = CRC32(keys[0], c);
01266 keys[1] += keys[0] & 0xff;
01267 keys[1] = keys[1] * 134775813L + 1;
01268 keys[2] = CRC32(keys[2], ((int)keys[1]) >> 24);
01269 }
01270
01275 void UnzipPrivate::initKeys(const QString& pwd, quint32* keys) const
01276 {
01277 keys[0] = 305419896L;
01278 keys[1] = 591751049L;
01279 keys[2] = 878082192L;
01280
01281 QByteArray pwdBytes = pwd.toAscii();
01282 int sz = pwdBytes.size();
01283 const char* ascii = pwdBytes.data();
01284
01285 for (int i=0; i<sz; ++i)
01286 updateKeys(keys, (int)ascii[i]);
01287 }
01288
01294 UnZip::ErrorCode UnzipPrivate::testPassword(quint32* keys, const QString& file, const ZipEntryP& header)
01295 {
01296 Q_UNUSED(file);
01297
01298
01299 if (device->read(buffer1, 12) != 12)
01300 return UnZip::Corrupted;
01301
01302
01303 initKeys(password, keys);
01304 if (testKeys(header, keys))
01305 return UnZip::Ok;
01306
01307 return UnZip::Skip;
01308 }
01309
01313 bool UnzipPrivate::testKeys(const ZipEntryP& header, quint32* keys)
01314 {
01315 char lastByte;
01316
01317
01318 for (int i=0; i<11; ++i)
01319 updateKeys(keys, lastByte = buffer1[i] ^ decryptByte(keys[2]));
01320 updateKeys(keys, lastByte = buffer1[11] ^ decryptByte(keys[2]));
01321
01322
01323
01324 char c = ((header.gpFlag[0] & 0x08) == 8) ? header.modTime[1] : header.crc >> 24;
01325
01326 return (lastByte == c);
01327 }
01328
01332 void UnzipPrivate::decryptBytes(quint32* keys, char* buffer, qint64 read)
01333 {
01334 for (int i=0; i<(int)read; ++i)
01335 updateKeys(keys, buffer[i] ^= decryptByte(keys[2]));
01336 }
01337
01341 QDateTime UnzipPrivate::convertDateTime(const unsigned char date[2], const unsigned char time[2]) const
01342 {
01343 QDateTime dt;
01344
01345
01346
01347
01348 quint16 year = (date[1] >> 1) & 127;
01349 quint16 month = ((date[1] << 3) & 14) | ((date[0] >> 5) & 7);
01350 quint16 day = date[0] & 31;
01351
01352
01353 quint16 hour = (time[1] >> 3) & 31;
01354 quint16 minutes = ((time[1] << 3) & 56) | ((time[0] >> 5) & 7);
01355 quint16 seconds = (time[0] & 31) * 2;
01356
01357 dt.setDate(QDate(1980 + year, month, day));
01358 dt.setTime(QTime(hour, minutes, seconds));
01359 return dt;
01360 }