33 #include <QStringList> 36 #include <QCoreApplication> 79 #define UNZIP_LOCAL_HEADER_SIZE 26 81 #define UNZIP_CD_ENTRY_SIZE_NS 42 83 #define UNZIP_DD_SIZE 12 85 #define UNZIP_EOCD_SIZE 22 87 #define UNZIP_LOCAL_ENC_HEADER_SIZE 12 90 #define UNZIP_CD_OFF_VERSION 0 91 #define UNZIP_CD_OFF_GPFLAG 4 92 #define UNZIP_CD_OFF_CMETHOD 6 93 #define UNZIP_CD_OFF_MODT 8 94 #define UNZIP_CD_OFF_MODD 10 95 #define UNZIP_CD_OFF_CRC32 12 96 #define UNZIP_CD_OFF_CSIZE 16 97 #define UNZIP_CD_OFF_USIZE 20 98 #define UNZIP_CD_OFF_NAMELEN 24 99 #define UNZIP_CD_OFF_XLEN 26 100 #define UNZIP_CD_OFF_COMMLEN 28 101 #define UNZIP_CD_OFF_LHOFFSET 38 104 #define UNZIP_LH_OFF_VERSION 0 105 #define UNZIP_LH_OFF_GPFLAG 2 106 #define UNZIP_LH_OFF_CMETHOD 4 107 #define UNZIP_LH_OFF_MODT 6 108 #define UNZIP_LH_OFF_MODD 8 109 #define UNZIP_LH_OFF_CRC32 10 110 #define UNZIP_LH_OFF_CSIZE 14 111 #define UNZIP_LH_OFF_USIZE 18 112 #define UNZIP_LH_OFF_NAMELEN 22 113 #define UNZIP_LH_OFF_XLEN 24 116 #define UNZIP_DD_OFF_CRC32 0 117 #define UNZIP_DD_OFF_CSIZE 4 118 #define UNZIP_DD_OFF_USIZE 8 121 #define UNZIP_EOCD_OFF_ENTRIES 6 122 #define UNZIP_EOCD_OFF_CDOFF 12 123 #define UNZIP_EOCD_OFF_COMMLEN 16 131 #define UNZIP_VERSION 0x1B 133 #define UNZIP_VERSION_STRICT 0x14 136 #define CRC32(c, b) crcTable[((int)c^b) & 0xff] ^ (c >> 8) 139 #define UNZIP_CHECK_FOR_VALID_DATA \ 143 qDebug() << "Corrupted zip archive. Some files might be extracted.";\ 144 ec = headers->size() != 0 ? UnZip::PartiallyCorrupted : UnZip::Corrupted;\ 151 qDebug() << "Corrupted or invalid zip archive";\ 152 ec = UnZip::Corrupted;\ 192 QFile* file =
new QFile(filename);
194 if (!file->exists()) {
199 if (!file->open(QIODevice::ReadOnly)) {
216 qDebug() <<
"Invalid device.";
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;
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;
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;
251 case WrongPassword:
return QCoreApplication::translate(
"UnZip",
"Wrong password.");
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;
254 case ReadFailed:
return QCoreApplication::translate(
"UnZip",
"File read error.");
break;
255 case WriteFailed:
return QCoreApplication::translate(
"UnZip",
"File write 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;
258 case InvalidDevice:
return QCoreApplication::translate(
"UnZip",
"Invalid device.");
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;
264 return QCoreApplication::translate(
"UnZip",
"Unknown error.");
291 QList<UnZip::ZipEntry> list;
295 for (QMap<QString,ZipEntryP*>::ConstIterator it =
d->
headers->constBegin(); it !=
d->
headers->constEnd(); ++it)
298 Q_ASSERT(entry != 0);
343 for (QMap<QString,ZipEntryP*>::Iterator itr =
d->
headers->begin(); itr !=
d->
headers->end(); ++itr)
346 Q_ASSERT(entry != 0);
351 switch (
d->
extractFile(itr.key(), *entry, dir, options))
354 qDebug() <<
"Removing corrupted entry" << itr.key();
382 return extractFile(filename, QDir(dirname), options);
390 QMap<QString,ZipEntryP*>::Iterator itr =
d->
headers->find(filename);
394 Q_ASSERT(entry != 0);
409 QMap<QString,ZipEntryP*>::Iterator itr =
d->
headers->find(filename);
412 Q_ASSERT(entry != 0);
428 for (QStringList::ConstIterator itr = filenames.constBegin(); itr != filenames.constEnd(); ++itr)
448 for (QStringList::ConstIterator itr = filenames.constBegin(); itr != filenames.constEnd(); ++itr)
473 compressedSize = uncompressedSize = crc32 = 0;
487 skipAllEncrypted =
false;
491 uBuffer = (
unsigned char*) buffer1;
492 crcTable = (quint32*) get_crc_table();
494 cdOffset = eocdOffset = 0;
496 unsupportedEntryCount = 0;
509 if (!(device->isOpen() || device->open(QIODevice::ReadOnly)))
514 qDebug() <<
"Unable to open device for reading";
520 ec = seekToCentralDirectory();
528 if (cdEntryCount == 0)
533 bool continueParsing =
true;
535 while (continueParsing)
537 if (device->read(buffer1, 4) != 4)
540 if (! (buffer1[0] ==
'P' && buffer1[1] ==
'K' && buffer1[2] == 0x01 && buffer1[3] == 0x02) )
543 if ( (ec = parseCentralDirectoryRecord()) !=
UnZip::Ok )
580 if (device->read(buffer1, 4) != 4)
583 if ((buffer1[0] !=
'P') || (buffer1[1] !=
'K') || (buffer1[2] != 0x03) || (buffer1[3] != 0x04))
599 bool checkFailed =
false;
615 if (!hasDataDescriptor)
633 if (device->read(buffer2, szName) != szName)
637 QString filename = QString::fromLatin1(buffer2, szName);
638 if (filename != path)
640 qDebug() <<
"Filename in local header mismatches.";
648 if (!device->seek(device->pos() + szExtra))
654 if (hasDataDescriptor)
661 if (!device->seek(device->pos() + entry.
szComp))
665 if (device->read(buffer2, 4) != 4)
668 bool hasSignature = buffer2[0] ==
'P' && buffer2[1] ==
'K' && buffer2[2] == 0x07 && buffer2[3] == 0x08;
715 qint64 length = device->size();
718 if (length < UNZIP_EOCD_SIZE)
721 if (!device->seek( offset ))
727 bool eocdFound = (buffer1[0] ==
'P' && buffer1[1] ==
'K' && buffer1[2] == 0x05 && buffer1[3] == 0x06);
744 if (!device->seek( offset ))
747 while ((read = device->read(buffer1, UNZIP_EOCD_SIZE)) >= 0)
749 if ( (p = strstr(buffer1,
"PK\5\6")) != 0)
754 device->seek( offset + (p - buffer1) );
756 eocdOffset = offset + (p - buffer1);
769 if (!device->seek( offset ))
785 if (commentLength != 0)
787 QByteArray c = device->read(commentLength);
788 if (c.count() != commentLength)
795 if (!device->seek( cdOffset ))
843 bool skipEntry =
false;
854 quint32 skipLength = szName + szExtra + szComment;
858 if ((compMethod != 0) && (compMethod != 8))
860 qDebug() <<
"Unsupported compression method. Skipping file.";
867 qDebug() <<
"Unsupported PKZip version. Skipping file.";
871 if (!skipEntry && szName == 0)
873 qDebug() <<
"Skipping file with no name.";
877 if (!skipEntry && device->read(buffer2, szName) != szName)
887 if (!device->seek( device->pos() + skipLength ))
890 unsupportedEntryCount++;
897 QString filename = QString::fromLatin1(buffer2, szName);
918 if (!device->seek( device->pos() + szExtra ))
928 if (device->read(buffer2, szComment) != szComment)
935 h->
comment = QString::fromLatin1(buffer2, szComment);
941 headers =
new QMap<QString, ZipEntryP*>();
942 headers->insert(filename, h);
953 skipAllEncrypted =
false;
957 qDeleteAll(*headers);
962 delete device; device = 0;
964 cdOffset = eocdOffset = 0;
966 unsupportedEntryCount = 0;
978 int pos = name.lastIndexOf(
'/');
981 if (pos == name.length() - 1)
986 directory = QString(
"%1/%2").arg(dir.absolutePath()).arg(QDir::cleanPath(name));
987 if (!createDirectory(directory))
989 qDebug() << QString(
"Unable to create directory: %1").arg(directory);
1000 dirname = name.left(pos);
1003 directory = dir.absolutePath();
1007 directory = QString(
"%1/%2").arg(dir.absolutePath()).arg(QDir::cleanPath(dirname));
1008 if (!createDirectory(directory))
1010 qDebug() << QString(
"Unable to create directory: %1").arg(directory);
1014 name = name.right(name.length() - pos - 1);
1015 }
else directory = dir.absolutePath();
1017 name = QString(
"%1/%2").arg(directory).arg(name);
1019 QFile outFile(name);
1021 if (!outFile.open(QIODevice::WriteOnly))
1023 qDebug() << QString(
"Unable to open %1 for writing").arg(name);
1035 if (!outFile.remove())
1036 qDebug() << QString(
"Unable to remove corrupted file: %1").arg(name);
1068 qDebug() << QString(
"Unable to decrypt %1").arg(path);
1090 quint32 myCRC = crc32(0L, Z_NULL, 0);
1094 while ( (read = device->read(buffer1, cur < rep ?
UNZIP_READ_BUFFER : rem)) > 0 )
1097 decryptBytes(keys, buffer1, read);
1099 myCRC = crc32(myCRC, uBuffer, read);
1101 if (dev->write(buffer1, read) != read)
1118 zstr.zalloc = Z_NULL;
1119 zstr.zfree = Z_NULL;
1120 zstr.opaque = Z_NULL;
1121 zstr.next_in = Z_NULL;
1127 if ( (zret = inflateInit2_(&zstr, -MAX_WBITS, ZLIB_VERSION,
sizeof(z_stream))) != Z_OK )
1140 (void)inflateEnd(&zstr);
1145 decryptBytes(keys, buffer1, read);
1150 zstr.avail_in = (uInt) read;
1151 zstr.next_in = (Bytef*) buffer1;
1157 zstr.next_out = (Bytef*) buffer2;;
1159 zret = inflate(&zstr, Z_NO_FLUSH);
1172 if (dev->write(buffer2, szDecomp) != szDecomp)
1178 myCRC = crc32(myCRC, (
const Bytef*) buffer2, szDecomp);
1180 }
while (zstr.avail_out == 0);
1183 while (zret != Z_STREAM_END);
1188 if (myCRC != entry.
crc)
1200 int sep = path.lastIndexOf(
"/");
1201 if (sep <= 0)
return true;
1203 if (!createDirectory(path.left(sep)))
1208 qDebug() << QString(
"Unable to create directory: %1").arg(path);
1221 quint32 res = (quint32) data[offset];
1222 res |= (((quint32)data[offset+1]) << 8);
1223 res |= (((quint32)data[offset+2]) << 16);
1224 res |= (((quint32)data[offset+3]) << 24);
1234 quint64 res = (quint64) data[offset];
1235 res |= (((quint64)data[offset+1]) << 8);
1236 res |= (((quint64)data[offset+2]) << 16);
1237 res |= (((quint64)data[offset+3]) << 24);
1238 res |= (((quint64)data[offset+1]) << 32);
1239 res |= (((quint64)data[offset+2]) << 40);
1240 res |= (((quint64)data[offset+3]) << 48);
1241 res |= (((quint64)data[offset+3]) << 56);
1251 return (quint16) data[offset] | (((quint16)data[offset+1]) << 8);
1259 quint16 temp = ((quint16)(key2) & 0xffff) | 2;
1260 return (
int)(((temp * (temp ^ 1)) >> 8) & 0xff);
1268 keys[0] =
CRC32(keys[0], c);
1269 keys[1] += keys[0] & 0xff;
1270 keys[1] = keys[1] * 134775813L + 1;
1271 keys[2] =
CRC32(keys[2], ((
int)keys[1]) >> 24);
1280 keys[0] = 305419896L;
1281 keys[1] = 591751049L;
1282 keys[2] = 878082192L;
1285 QByteArray pwdBytes = pwd.toLatin1();
1286 int sz = pwdBytes.size();
1287 const char* ascii = pwdBytes.data();
1289 for (
int i=0; i<sz; ++i)
1290 updateKeys(keys, (
int)ascii[i]);
1303 if (device->read(buffer1, 12) != 12)
1307 initKeys(password, keys);
1308 if (testKeys(header, keys))
1322 for (
int i=0; i<11; ++i)
1323 updateKeys(keys, lastByte = buffer1[i] ^ decryptByte(keys[2]));
1324 updateKeys(keys, lastByte = buffer1[11] ^ decryptByte(keys[2]));
1328 char c = ((header.
gpFlag[0] & 0x08) == 8) ? header.
modTime[1] : header.
crc >> 24;
1330 return (lastByte == c);
1338 for (
int i=0; i<(int)read; ++i)
1339 updateKeys(keys, buffer[i] ^= decryptByte(keys[2]));
1352 quint16 year = (date[1] >> 1) & 127;
1353 quint16 month = ((date[1] << 3) & 14) | ((date[0] >> 5) & 7);
1354 quint16 day = date[0] & 31;
1357 quint16 hour = (time[1] >> 3) & 31;
1358 quint16 minutes = ((time[1] << 3) & 56) | ((time[0] >> 5) & 7);
1359 quint16 seconds = (time[0] & 31) * 2;
1361 dt.setDate(QDate(1980 + year, month, day));
1362 dt.setTime(QTime(hour, minutes, seconds));
quint16 getUShort(const unsigned char *data, quint32 offset) const
#define UNZIP_EOCD_OFF_CDOFF
UnZip::ErrorCode testPassword(quint32 *keys, const QString &file, const ZipEntryP &header)
bool hasDataDescriptor() const
#define UNZIP_CD_OFF_CSIZE
ErrorCode extractAll(const QString &dirname, ExtractionOptions options=ExtractPaths)
#define UNZIP_LH_OFF_GPFLAG
QDateTime convertDateTime(const unsigned char date[2], const unsigned char time[2]) const
bool testKeys(const ZipEntryP &header, quint32 *keys)
#define UNZIP_DD_OFF_CRC32
QStringList fileList() const
UnZip::ErrorCode parseLocalHeaderRecord(const QString &path, ZipEntryP &entry)
#define UNZIP_CD_OFF_CMETHOD
#define UNZIP_CD_OFF_LHOFFSET
QList< ZipEntry > entryList() const
bool createDirectory(const QString &path)
#define UNZIP_CD_OFF_MODT
#define UNZIP_READ_BUFFER
CompressionMethod compression
#define UNZIP_DD_SIZE
Data descriptor size (excluding signature)
#define UNZIP_LOCAL_ENC_HEADER_SIZE
Local header entry encryption header size.
QMap< QString, ZipEntryP * > * headers
void initKeys(const QString &pwd, quint32 *keys) const
#define UNZIP_CD_ENTRY_SIZE_NS
Central Directory file entry size (excluding signature, excluding variable length fields) ...
#define UNZIP_DD_OFF_USIZE
#define UNZIP_CHECK_FOR_VALID_DATA
Checks if some file has been already extracted.
#define UNZIP_LH_OFF_USIZE
void updateKeys(quint32 *keys, int c) const
void decryptBytes(quint32 *keys, char *buffer, qint64 read)
bool contains(const QString &file) const
#define UNZIP_EOCD_OFF_COMMLEN
UnZip::ErrorCode openArchive(QIODevice *device)
#define CRC32(c, b)
CRC32 routine.
QString archiveComment() const
Ignores paths and extracts all the files to the same directory.
QString formatError(UnZip::ErrorCode c) const
#define UNZIP_LOCAL_HEADER_SIZE
Local header size (excluding signature, excluding variable length fields)
UnZip::ErrorCode seekToCentralDirectory()
#define UNZIP_CD_OFF_COMMLEN
#define UNZIP_CD_OFF_XLEN
#define UNZIP_EOCD_OFF_ENTRIES
#define UNZIP_LH_OFF_XLEN
#define UNZIP_CD_OFF_CRC32
#define UNZIP_LH_OFF_CMETHOD
#define UNZIP_CD_OFF_GPFLAG
ErrorCode extractFile(const QString &filename, const QString &dirname, ExtractionOptions options=ExtractPaths)
int decryptByte(quint32 key2) const
UnZip::ErrorCode extractFile(const QString &path, ZipEntryP &entry, const QDir &dir, UnZip::ExtractionOptions options)
#define UNZIP_LH_OFF_MODD
#define UNZIP_CD_OFF_VERSION
ErrorCode extractFiles(const QStringList &filenames, const QString &dirname, ExtractionOptions options=ExtractPaths)
ErrorCode openArchive(const QString &filename)
quint64 getULLong(const unsigned char *data, quint32 offset) const
#define UNZIP_DD_OFF_CSIZE
#define UNZIP_LH_OFF_CRC32
#define UNZIP_CD_OFF_NAMELEN
#define UNZIP_CD_OFF_MODD
#define UNZIP_EOCD_SIZE
End Of Central Directory size (including signature, excluding variable length fields) ...
UnZip::ErrorCode parseCentralDirectoryRecord()
#define UNZIP_CD_OFF_USIZE
#define UNZIP_LH_OFF_MODT
void setPassword(const QString &pwd)
#define UNZIP_LH_OFF_NAMELEN
quint32 getULong(const unsigned char *data, quint32 offset) const
#define UNZIP_LH_OFF_CSIZE