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
00080
00081 #define UNZIP_CD_ENTRY_SIZE_NS 42
00082
00083 #define UNZIP_DD_SIZE 12
00084
00085 #define UNZIP_EOCD_SIZE 22
00086
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
00132
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
00765 offset -= 1 ;
00766 if (offset <= 0)
00767 return UnZip::InvalidArchive;
00768
00769 if (!device->seek( offset ))
00770 return UnZip::SeekFailed;
00771 }
00772 }
00773
00774 if (!eocdFound)
00775 return UnZip::InvalidArchive;
00776
00777
00778 offset = getULong((const unsigned char*)buffer1, UNZIP_EOCD_OFF_CDOFF + 4);
00779
00780 cdOffset = offset;
00781
00782 cdEntryCount = getUShort((const unsigned char*)buffer1, UNZIP_EOCD_OFF_ENTRIES + 4);
00783
00784 quint16 commentLength = getUShort((const unsigned char*)buffer1, UNZIP_EOCD_OFF_COMMLEN + 4);
00785 if (commentLength != 0)
00786 {
00787 QByteArray c = device->read(commentLength);
00788 if (c.count() != commentLength)
00789 return UnZip::ReadFailed;
00790
00791 comment = c;
00792 }
00793
00794
00795 if (!device->seek( cdOffset ))
00796 return UnZip::SeekFailed;
00797
00798 return UnZip::Ok;
00799 }
00800
00837 UnZip::ErrorCode UnzipPrivate::parseCentralDirectoryRecord()
00838 {
00839
00840 if (device->read(buffer1, UNZIP_CD_ENTRY_SIZE_NS) != UNZIP_CD_ENTRY_SIZE_NS)
00841 return UnZip::ReadFailed;
00842
00843 bool skipEntry = false;
00844
00845
00846 quint16 compMethod = getUShort(uBuffer, UNZIP_CD_OFF_CMETHOD);
00847
00848
00849
00850 quint16 szName = getUShort(uBuffer, UNZIP_CD_OFF_NAMELEN);
00851 quint16 szExtra = getUShort(uBuffer, UNZIP_CD_OFF_XLEN);
00852 quint16 szComment = getUShort(uBuffer, UNZIP_CD_OFF_COMMLEN);
00853
00854 quint32 skipLength = szName + szExtra + szComment;
00855
00856 UnZip::ErrorCode ec = UnZip::Ok;
00857
00858 if ((compMethod != 0) && (compMethod != 8))
00859 {
00860 qDebug() << "Unsupported compression method. Skipping file.";
00861 skipEntry = true;
00862 }
00863
00864
00865 if (!skipEntry && buffer1[UNZIP_CD_OFF_VERSION] > UNZIP_VERSION)
00866 {
00867 qDebug() << "Unsupported PKZip version. Skipping file.";
00868 skipEntry = true;
00869 }
00870
00871 if (!skipEntry && szName == 0)
00872 {
00873 qDebug() << "Skipping file with no name.";
00874 skipEntry = true;
00875 }
00876
00877 if (!skipEntry && device->read(buffer2, szName) != szName)
00878 {
00879 ec = UnZip::ReadFailed;
00880 skipEntry = true;
00881 }
00882
00883 if (skipEntry)
00884 {
00885 if (ec == UnZip::Ok)
00886 {
00887 if (!device->seek( device->pos() + skipLength ))
00888 ec = UnZip::SeekFailed;
00889
00890 unsupportedEntryCount++;
00891 }
00892
00893 return ec;
00894 }
00895
00896 QString filename = QString::fromAscii(buffer2, szName);
00897
00898 ZipEntryP* h = new ZipEntryP;
00899 h->compMethod = compMethod;
00900
00901 h->gpFlag[0] = buffer1[UNZIP_CD_OFF_GPFLAG];
00902 h->gpFlag[1] = buffer1[UNZIP_CD_OFF_GPFLAG + 1];
00903
00904 h->modTime[0] = buffer1[UNZIP_CD_OFF_MODT];
00905 h->modTime[1] = buffer1[UNZIP_CD_OFF_MODT + 1];
00906
00907 h->modDate[0] = buffer1[UNZIP_CD_OFF_MODD];
00908 h->modDate[1] = buffer1[UNZIP_CD_OFF_MODD + 1];
00909
00910 h->crc = getULong(uBuffer, UNZIP_CD_OFF_CRC32);
00911 h->szComp = getULong(uBuffer, UNZIP_CD_OFF_CSIZE);
00912 h->szUncomp = getULong(uBuffer, UNZIP_CD_OFF_USIZE);
00913
00914
00915 if (szExtra != 0)
00916 {
00917 if (!device->seek( device->pos() + szExtra ))
00918 {
00919 delete h;
00920 return UnZip::SeekFailed;
00921 }
00922 }
00923
00924
00925 if (szComment != 0)
00926 {
00927 if (device->read(buffer2, szComment) != szComment)
00928 {
00929 delete h;
00930 return UnZip::ReadFailed;
00931 }
00932
00933 h->comment = QString::fromAscii(buffer2, szComment);
00934 }
00935
00936 h->lhOffset = getULong(uBuffer, UNZIP_CD_OFF_LHOFFSET);
00937
00938 if (headers == 0)
00939 headers = new QMap<QString, ZipEntryP*>();
00940 headers->insert(filename, h);
00941
00942 return UnZip::Ok;
00943 }
00944
00946 void UnzipPrivate::closeArchive()
00947 {
00948 if (device == 0)
00949 return;
00950
00951 skipAllEncrypted = false;
00952
00953 if (headers != 0)
00954 {
00955 qDeleteAll(*headers);
00956 delete headers;
00957 headers = 0;
00958 }
00959
00960 delete device; device = 0;
00961
00962 cdOffset = eocdOffset = 0;
00963 cdEntryCount = 0;
00964 unsupportedEntryCount = 0;
00965
00966 comment.clear();
00967 }
00968
00970 UnZip::ErrorCode UnzipPrivate::extractFile(const QString& path, ZipEntryP& entry, const QDir& dir, UnZip::ExtractionOptions options)
00971 {
00972 QString name(path);
00973 QString dirname;
00974 QString directory;
00975
00976 int pos = name.lastIndexOf('/');
00977
00978
00979 if (pos == name.length() - 1)
00980 {
00981 if (options.testFlag(UnZip::SkipPaths))
00982 return UnZip::Ok;
00983
00984 directory = QString("%1/%2").arg(dir.absolutePath()).arg(QDir::cleanPath(name));
00985 if (!createDirectory(directory))
00986 {
00987 qDebug() << QString("Unable to create directory: %1").arg(directory);
00988 return UnZip::CreateDirFailed;
00989 }
00990
00991 return UnZip::Ok;
00992 }
00993
00994
00995 if (pos > 0)
00996 {
00997
00998 dirname = name.left(pos);
00999 if (options.testFlag(UnZip::SkipPaths))
01000 {
01001 directory = dir.absolutePath();
01002 }
01003 else
01004 {
01005 directory = QString("%1/%2").arg(dir.absolutePath()).arg(QDir::cleanPath(dirname));
01006 if (!createDirectory(directory))
01007 {
01008 qDebug() << QString("Unable to create directory: %1").arg(directory);
01009 return UnZip::CreateDirFailed;
01010 }
01011 }
01012 name = name.right(name.length() - pos - 1);
01013 } else directory = dir.absolutePath();
01014
01015 name = QString("%1/%2").arg(directory).arg(name);
01016
01017 QFile outFile(name);
01018
01019 if (!outFile.open(QIODevice::WriteOnly))
01020 {
01021 qDebug() << QString("Unable to open %1 for writing").arg(name);
01022 return UnZip::OpenFailed;
01023 }
01024
01026
01027 UnZip::ErrorCode ec = extractFile(path, entry, &outFile, options);
01028
01029 outFile.close();
01030
01031 if (ec != UnZip::Ok)
01032 {
01033 if (!outFile.remove())
01034 qDebug() << QString("Unable to remove corrupted file: %1").arg(name);
01035 }
01036
01037 return ec;
01038 }
01039
01041 UnZip::ErrorCode UnzipPrivate::extractFile(const QString& path, ZipEntryP& entry, QIODevice* dev, UnZip::ExtractionOptions options)
01042 {
01043 Q_UNUSED(options);
01044 Q_ASSERT(dev != 0);
01045
01046 if (!entry.lhEntryChecked)
01047 {
01048 UnZip::ErrorCode ec = parseLocalHeaderRecord(path, entry);
01049 entry.lhEntryChecked = true;
01050
01051 if (ec != UnZip::Ok)
01052 return ec;
01053 }
01054
01055 if (!device->seek(entry.dataOffset))
01056 return UnZip::SeekFailed;
01057
01058
01059 quint32 keys[3];
01060
01061 if (entry.isEncrypted())
01062 {
01063 UnZip::ErrorCode e = testPassword(keys, path, entry);
01064 if (e != UnZip::Ok)
01065 {
01066 qDebug() << QString("Unable to decrypt %1").arg(path);
01067 return e;
01068 }
01069 entry.szComp -= UNZIP_LOCAL_ENC_HEADER_SIZE;
01070 }
01071
01072 if (entry.szComp == 0)
01073 {
01074 if (entry.crc != 0)
01075 return UnZip::Corrupted;
01076
01077 return UnZip::Ok;
01078 }
01079
01080 uInt rep = entry.szComp / UNZIP_READ_BUFFER;
01081 uInt rem = entry.szComp % UNZIP_READ_BUFFER;
01082 uInt cur = 0;
01083
01084
01085 qint64 read;
01086 quint64 tot = 0;
01087
01088 quint32 myCRC = crc32(0L, Z_NULL, 0);
01089
01090 if (entry.compMethod == 0)
01091 {
01092 while ( (read = device->read(buffer1, cur < rep ? UNZIP_READ_BUFFER : rem)) > 0 )
01093 {
01094 if (entry.isEncrypted())
01095 decryptBytes(keys, buffer1, read);
01096
01097 myCRC = crc32(myCRC, uBuffer, read);
01098
01099 if (dev->write(buffer1, read) != read)
01100 return UnZip::WriteFailed;
01101
01102 cur++;
01103 tot += read;
01104
01105 if (tot == entry.szComp)
01106 break;
01107 }
01108
01109 if (read < 0)
01110 return UnZip::ReadFailed;
01111 }
01112 else if (entry.compMethod == 8)
01113 {
01114
01115 z_stream zstr;
01116 zstr.zalloc = Z_NULL;
01117 zstr.zfree = Z_NULL;
01118 zstr.opaque = Z_NULL;
01119 zstr.next_in = Z_NULL;
01120 zstr.avail_in = 0;
01121
01122 int zret;
01123
01124
01125 if ( (zret = inflateInit2_(&zstr, -MAX_WBITS, ZLIB_VERSION, sizeof(z_stream))) != Z_OK )
01126 return UnZip::ZlibError;
01127
01128 int szDecomp;
01129
01130
01131 do
01132 {
01133 read = device->read(buffer1, cur < rep ? UNZIP_READ_BUFFER : rem);
01134 if (read == 0)
01135 break;
01136 if (read < 0)
01137 {
01138 (void)inflateEnd(&zstr);
01139 return UnZip::ReadFailed;
01140 }
01141
01142 if (entry.isEncrypted())
01143 decryptBytes(keys, buffer1, read);
01144
01145 cur++;
01146 tot += read;
01147
01148 zstr.avail_in = (uInt) read;
01149 zstr.next_in = (Bytef*) buffer1;
01150
01151
01152
01153 do {
01154 zstr.avail_out = UNZIP_READ_BUFFER;
01155 zstr.next_out = (Bytef*) buffer2;;
01156
01157 zret = inflate(&zstr, Z_NO_FLUSH);
01158
01159 switch (zret) {
01160 case Z_NEED_DICT:
01161 case Z_DATA_ERROR:
01162 case Z_MEM_ERROR:
01163 inflateEnd(&zstr);
01164 return UnZip::WriteFailed;
01165 default:
01166 ;
01167 }
01168
01169 szDecomp = UNZIP_READ_BUFFER - zstr.avail_out;
01170 if (dev->write(buffer2, szDecomp) != szDecomp)
01171 {
01172 inflateEnd(&zstr);
01173 return UnZip::ZlibError;
01174 }
01175
01176 myCRC = crc32(myCRC, (const Bytef*) buffer2, szDecomp);
01177
01178 } while (zstr.avail_out == 0);
01179
01180 }
01181 while (zret != Z_STREAM_END);
01182
01183 inflateEnd(&zstr);
01184 }
01185
01186 if (myCRC != entry.crc)
01187 return UnZip::Corrupted;
01188
01189 return UnZip::Ok;
01190 }
01191
01193 bool UnzipPrivate::createDirectory(const QString& path)
01194 {
01195 QDir d(path);
01196 if (!d.exists())
01197 {
01198 int sep = path.lastIndexOf("/");
01199 if (sep <= 0) return true;
01200
01201 if (!createDirectory(path.left(sep)))
01202 return false;
01203
01204 if (!d.mkdir(path))
01205 {
01206 qDebug() << QString("Unable to create directory: %1").arg(path);
01207 return false;
01208 }
01209 }
01210
01211 return true;
01212 }
01213
01217 quint32 UnzipPrivate::getULong(const unsigned char* data, quint32 offset) const
01218 {
01219 quint32 res = (quint32) data[offset];
01220 res |= (((quint32)data[offset+1]) << 8);
01221 res |= (((quint32)data[offset+2]) << 16);
01222 res |= (((quint32)data[offset+3]) << 24);
01223
01224 return res;
01225 }
01226
01230 quint64 UnzipPrivate::getULLong(const unsigned char* data, quint32 offset) const
01231 {
01232 quint64 res = (quint64) data[offset];
01233 res |= (((quint64)data[offset+1]) << 8);
01234 res |= (((quint64)data[offset+2]) << 16);
01235 res |= (((quint64)data[offset+3]) << 24);
01236 res |= (((quint64)data[offset+1]) << 32);
01237 res |= (((quint64)data[offset+2]) << 40);
01238 res |= (((quint64)data[offset+3]) << 48);
01239 res |= (((quint64)data[offset+3]) << 56);
01240
01241 return res;
01242 }
01243
01247 quint16 UnzipPrivate::getUShort(const unsigned char* data, quint32 offset) const
01248 {
01249 return (quint16) data[offset] | (((quint16)data[offset+1]) << 8);
01250 }
01251
01255 int UnzipPrivate::decryptByte(quint32 key2) const
01256 {
01257 quint16 temp = ((quint16)(key2) & 0xffff) | 2;
01258 return (int)(((temp * (temp ^ 1)) >> 8) & 0xff);
01259 }
01260
01264 void UnzipPrivate::updateKeys(quint32* keys, int c) const
01265 {
01266 keys[0] = CRC32(keys[0], c);
01267 keys[1] += keys[0] & 0xff;
01268 keys[1] = keys[1] * 134775813L + 1;
01269 keys[2] = CRC32(keys[2], ((int)keys[1]) >> 24);
01270 }
01271
01276 void UnzipPrivate::initKeys(const QString& pwd, quint32* keys) const
01277 {
01278 keys[0] = 305419896L;
01279 keys[1] = 591751049L;
01280 keys[2] = 878082192L;
01281
01282 QByteArray pwdBytes = pwd.toAscii();
01283 int sz = pwdBytes.size();
01284 const char* ascii = pwdBytes.data();
01285
01286 for (int i=0; i<sz; ++i)
01287 updateKeys(keys, (int)ascii[i]);
01288 }
01289
01295 UnZip::ErrorCode UnzipPrivate::testPassword(quint32* keys, const QString& file, const ZipEntryP& header)
01296 {
01297 Q_UNUSED(file);
01298
01299
01300 if (device->read(buffer1, 12) != 12)
01301 return UnZip::Corrupted;
01302
01303
01304 initKeys(password, keys);
01305 if (testKeys(header, keys))
01306 return UnZip::Ok;
01307
01308 return UnZip::Skip;
01309 }
01310
01314 bool UnzipPrivate::testKeys(const ZipEntryP& header, quint32* keys)
01315 {
01316 char lastByte;
01317
01318
01319 for (int i=0; i<11; ++i)
01320 updateKeys(keys, lastByte = buffer1[i] ^ decryptByte(keys[2]));
01321 updateKeys(keys, lastByte = buffer1[11] ^ decryptByte(keys[2]));
01322
01323
01324
01325 char c = ((header.gpFlag[0] & 0x08) == 8) ? header.modTime[1] : header.crc >> 24;
01326
01327 return (lastByte == c);
01328 }
01329
01333 void UnzipPrivate::decryptBytes(quint32* keys, char* buffer, qint64 read)
01334 {
01335 for (int i=0; i<(int)read; ++i)
01336 updateKeys(keys, buffer[i] ^= decryptByte(keys[2]));
01337 }
01338
01342 QDateTime UnzipPrivate::convertDateTime(const unsigned char date[2], const unsigned char time[2]) const
01343 {
01344 QDateTime dt;
01345
01346
01347
01348
01349 quint16 year = (date[1] >> 1) & 127;
01350 quint16 month = ((date[1] << 3) & 14) | ((date[0] >> 5) & 7);
01351 quint16 day = date[0] & 31;
01352
01353
01354 quint16 hour = (time[1] >> 3) & 31;
01355 quint16 minutes = ((time[1] << 3) & 56) | ((time[0] >> 5) & 7);
01356 quint16 seconds = (time[0] & 31) * 2;
01357
01358 dt.setDate(QDate(1980 + year, month, day));
01359 dt.setTime(QTime(hour, minutes, seconds));
01360 return dt;
01361 }