93static int inflate(Bytef *dest, ulong *destLen,
const Bytef *source, ulong sourceLen)
98 stream.next_in =
const_cast<Bytef*>(source);
99 stream.avail_in = (uInt)sourceLen;
100 if ((uLong)stream.avail_in != sourceLen)
103 stream.next_out = dest;
104 stream.avail_out = (uInt)*destLen;
105 if ((uLong)stream.avail_out != *destLen)
108 err = inflateInit2(&stream, -MAX_WBITS);
112 err = inflate(&stream, Z_FINISH);
113 if (err != Z_STREAM_END) {
115 if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0))
119 *destLen = stream.total_out;
121 err = inflateEnd(&stream);
125static int deflate (Bytef *dest, ulong *destLen,
const Bytef *source, ulong sourceLen)
130 stream.next_in =
const_cast<Bytef*>(source);
131 stream.avail_in = (uInt)sourceLen;
132 stream.next_out = dest;
133 stream.avail_out = (uInt)*destLen;
134 if ((uLong)stream.avail_out != *destLen)
return Z_BUF_ERROR;
136 stream.zalloc = (alloc_func)
nullptr;
137 stream.zfree = (free_func)
nullptr;
138 stream.opaque = (voidpf)
nullptr;
140 err = deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY);
141 if (err != Z_OK)
return err;
143 err = deflate(&stream, Z_FINISH);
144 if (err != Z_STREAM_END) {
146 return err == Z_OK ? Z_BUF_ERROR : err;
148 *destLen = stream.total_out;
150 err = deflateEnd(&stream);
188 QFile::Permissions ret;
189 if (mode & UnixFileAttributes::ReadUser)
190 ret |= QFile::ReadOwner | QFile::ReadUser;
191 if (mode & UnixFileAttributes::WriteUser)
192 ret |= QFile::WriteOwner | QFile::WriteUser;
193 if (mode & UnixFileAttributes::ExeUser)
194 ret |= QFile::ExeOwner | QFile::ExeUser;
195 if (mode & UnixFileAttributes::ReadGroup)
196 ret |= QFile::ReadGroup;
197 if (mode & UnixFileAttributes::WriteGroup)
198 ret |= QFile::WriteGroup;
199 if (mode & UnixFileAttributes::ExeGroup)
200 ret |= QFile::ExeGroup;
201 if (mode & UnixFileAttributes::ReadOther)
202 ret |= QFile::ReadOther;
203 if (mode & UnixFileAttributes::WriteOther)
204 ret |= QFile::WriteOther;
205 if (mode & UnixFileAttributes::ExeOther)
206 ret |= QFile::ExeOther;
236 uint dosDate = readUInt(src);
238 uDate = (quint64)(dosDate >> 16);
239 uint tm_mday = (uDate & 0x1f);
240 uint tm_mon = ((uDate & 0x1E0) >> 5);
241 uint tm_year = (((uDate & 0x0FE00) >> 9) + 1980);
242 uint tm_hour = ((dosDate & 0xF800) >> 11);
243 uint tm_min = ((dosDate & 0x7E0) >> 5);
244 uint tm_sec = ((dosDate & 0x1f) << 1);
246 return QDateTime(QDate(tm_year, tm_mon, tm_mday), QTime(tm_hour, tm_min, tm_sec));
406 QZipReader::FileInfo fileInfo;
408 quint32 mode = readUInt(header.h.external_file_attributes);
409 const HostOS hostOS = HostOS(readUShort(header.h.version_made) >> 8);
412 mode = (mode >> 16) & 0xffff;
415 fileInfo.isSymLink =
true;
418 fileInfo.isDir =
true;
422 fileInfo.isFile =
true;
425 fileInfo.permissions = modeToPermissions(mode);
433 fileInfo.isDir =
true;
437 fileInfo.isFile =
true;
440 fileInfo.permissions |= QFile::ReadOwner | QFile::ReadUser | QFile::ReadGroup | QFile::ReadOther;
441 if ((mode & WindowsFileAttributes::ReadOnly) == 0)
442 fileInfo.permissions |= QFile::WriteOwner | QFile::WriteUser | QFile::WriteGroup | QFile::WriteOther;
444 fileInfo.permissions |= QFile::ExeOwner | QFile::ExeUser | QFile::ExeGroup | QFile::ExeOther;
447 qWarning(
"QZip: Zip entry format at %d is not supported.", index);
451 ushort general_purpose_bits = readUShort(header.h.general_purpose_bits);
453 const bool inUtf8 = (general_purpose_bits &
Utf8Names) != 0;
454 fileInfo.filePath = inUtf8 ? QString::fromUtf8(header.file_name) : QString::fromLocal8Bit(header.file_name);
455 fileInfo.crc = readUInt(header.h.crc_32);
456 fileInfo.size = readUInt(header.h.uncompressed_size);
457 fileInfo.lastModified = readMSDosDate(header.h.last_mod_file);
460 fileInfo.filePath = QDir::fromNativeSeparators(fileInfo.filePath);
461 QStringView filePathRef(fileInfo.filePath);
462 while (filePathRef.startsWith(u'.') || filePathRef.startsWith(u'/'))
463 filePathRef = filePathRef.mid(1);
464 while (filePathRef.endsWith(u'/'))
467 fileInfo.filePath = filePathRef.toString();
507 writeUInt(h.signature, 0x04034b50);
508 copyUShort(h.version_needed, ch.version_needed);
509 copyUShort(h.general_purpose_bits, ch.general_purpose_bits);
510 copyUShort(h.compression_method, ch.compression_method);
511 copyUInt(h.last_mod_file, ch.last_mod_file);
512 copyUInt(h.crc_32, ch.crc_32);
513 copyUInt(h.compressed_size, ch.compressed_size);
514 copyUInt(h.uncompressed_size, ch.uncompressed_size);
515 copyUShort(h.file_name_length, ch.file_name_length);
516 copyUShort(h.extra_field_length, ch.extra_field_length);
525 if (! (device->isOpen() || device->open(QIODevice::ReadOnly))) {
526 status = QZipReader::FileOpenError;
530 if ((device->openMode() & QIODevice::ReadOnly) == 0) {
531 status = QZipReader::FileReadError;
537 device->read((
char *)tmp, 4);
538 if (readUInt(tmp) != 0x04034b50) {
539 qWarning(
"QZip: not a zip file!");
545 int start_of_directory = -1;
546 int num_dir_entries = 0;
548 while (start_of_directory == -1) {
549 const int pos = device->size() -
int(
sizeof(EndOfDirectory)) - i;
550 if (pos < 0 || i > 65535) {
551 qWarning(
"QZip: EndOfDirectory not found");
556 device->read((
char *)&eod,
sizeof(EndOfDirectory));
557 if (readUInt(eod.signature) == 0x06054b50)
563 start_of_directory = readUInt(eod.dir_start_offset);
564 num_dir_entries = readUShort(eod.num_dir_entries);
565 ZDEBUG(
"start_of_directory at %d, num_dir_entries=%d", start_of_directory, num_dir_entries);
566 int comment_length = readUShort(eod.comment_length);
567 if (comment_length != i)
568 qWarning(
"QZip: failed to parse zip file.");
569 comment = device->read(qMin(comment_length, i));
572 device->seek(start_of_directory);
573 for (i = 0; i < num_dir_entries; ++i) {
575 int read = device->read((
char *) &header.h,
sizeof(CentralFileHeader));
577 qWarning(
"QZip: Failed to read complete header, index may be incomplete");
580 if (readUInt(header.h.signature) != 0x02014b50) {
581 qWarning(
"QZip: invalid header signature, index may be incomplete");
585 int l = readUShort(header.h.file_name_length);
586 header.file_name = device->read(l);
587 if (header.file_name.size() != l) {
588 qWarning(
"QZip: Failed to read filename from zip index, index may be incomplete");
591 l = readUShort(header.h.extra_field_length);
592 header.extra_field = device->read(l);
593 if (header.extra_field.size() != l) {
594 qWarning(
"QZip: Failed to read extra field in zip file, skipping file, index may be incomplete");
597 l = readUShort(header.h.file_comment_length);
598 header.file_comment = device->read(l);
599 if (header.file_comment.size() != l) {
600 qWarning(
"QZip: Failed to read read file comment, index may be incomplete");
604 ZDEBUG(
"found file '%s'", header.file_name.data());
605 fileHeaders.append(header);
612 static const char *
const entryTypes[] = {
616 ZDEBUG() <<
"adding" << entryTypes[type] <<
":" << fileName.toUtf8().data() << (type == 2 ? QByteArray(
" -> " + contents).constData() :
"");
619 if (! (device->isOpen() || device->open(QIODevice::WriteOnly))) {
620 status = QZipWriter::FileOpenError;
623 device->seek(start_of_directory);
626 QZipWriter::CompressionPolicy compression = compressionPolicy;
627 if (compressionPolicy == QZipWriter::AutoCompress) {
628 if (contents.size() < 64)
629 compression = QZipWriter::NeverCompress;
631 compression = QZipWriter::AlwaysCompress;
636 writeUInt(header.h.signature, 0x02014b50);
639 writeUInt(header.h.uncompressed_size, contents.size());
640 writeMSDosDate(header.h.last_mod_file, QDateTime::currentDateTime());
641 QByteArray data = contents;
642 if (compression == QZipWriter::AlwaysCompress) {
645 ulong len = contents.size();
647 len += (len >> 12) + (len >> 14) + 11;
651 res = deflate((uchar*)data.data(), &len, (
const uchar*)contents.constData(), contents.size());
658 qWarning(
"QZip: Z_MEM_ERROR: Not enough memory to compress file, skipping");
665 }
while (res == Z_BUF_ERROR);
668 writeUInt(header.h.compressed_size, data.size());
669 uint crc_32 = ::crc32(0,
nullptr, 0);
670 crc_32 = ::crc32(crc_32, (
const uchar *)contents.constData(), contents.size());
671 writeUInt(header.h.crc_32, crc_32);
675 writeUShort(header.h.general_purpose_bits, general_purpose_bits);
677 const bool inUtf8 = (general_purpose_bits &
Utf8Names) != 0;
678 header.file_name = inUtf8 ? fileName.toUtf8() : fileName.toLocal8Bit();
679 if (header.file_name.size() > 0xffff) {
680 qWarning(
"QZip: Filename is too long, chopping it to 65535 bytes");
681 header.file_name = header.file_name.left(0xffff);
683 if (header.file_comment.size() + header.file_name.size() > 0xffff) {
684 qWarning(
"QZip: File comment is too long, chopping it to 65535 bytes");
685 header.file_comment.truncate(0xffff - header.file_name.size());
687 writeUShort(header.h.file_name_length, header.file_name.size());
690 writeUShort(header.h.version_made,
HostUnix << 8);
693 quint32 mode = permissionsToMode(permissions);
708 writeUInt(header.h.external_file_attributes, mode << 16);
709 writeUInt(header.h.offset_local_header, start_of_directory);
712 fileHeaders.append(header);
715 device->write((
const char *)&h,
sizeof(LocalFileHeader));
716 device->write(header.file_name);
718 start_of_directory = device->pos();
784QZipReader::QZipReader(
const QString &archive, QIODevice::OpenMode mode)
786 auto f = std::make_unique<QFile>(archive);
787 const bool result = f->open(mode);
788 QZipReader::Status status;
789 const QFileDevice::FileError error = f->error();
790 if (result && error == QFile::NoError) {
793 if (error == QFile::ReadError)
794 status = FileReadError;
795 else if (error == QFile::OpenError)
796 status = FileOpenError;
797 else if (error == QFile::PermissionsError)
798 status = FilePermissionsError;
803 d =
new QZipReaderPrivate(f.get(),
true);
804 Q_UNUSED(f.release());
897QByteArray QZipReader::fileData(
const QString &fileName)
const
901 for (i = 0; i < d->fileHeaders.size(); ++i) {
902 if (QString::fromLocal8Bit(d->fileHeaders.at(i).file_name) == fileName)
905 if (i == d->fileHeaders.size())
908 FileHeader header = d->fileHeaders.at(i);
910 ushort version_needed = readUShort(header.h.version_needed);
912 qWarning(
"QZip: .ZIP specification version %d implementationis needed to extract the data.", version_needed);
916 ushort general_purpose_bits = readUShort(header.h.general_purpose_bits);
917 int compressed_size = readUInt(header.h.compressed_size);
918 int uncompressed_size = readUInt(header.h.uncompressed_size);
919 int start = readUInt(header.h.offset_local_header);
922 d->device->seek(start);
924 d->device->read((
char *)&lh,
sizeof(LocalFileHeader));
925 uint skip = readUShort(lh.file_name_length) + readUShort(lh.extra_field_length);
926 d->device->seek(d->device->pos() + skip);
928 int compression_method = readUShort(lh.compression_method);
931 if ((general_purpose_bits & Encrypted) != 0) {
932 qWarning(
"QZip: Unsupported encryption method is needed to extract the data.");
937 QByteArray compressed = d->device->read(compressed_size);
938 if (compression_method == CompressionMethodStored) {
940 compressed.truncate(uncompressed_size);
942 }
else if (compression_method == CompressionMethodDeflated) {
945 compressed.truncate(compressed_size);
947 ulong len = qMax(uncompressed_size, 1);
951 res = inflate((uchar*)baunzip.data(), &len,
952 (
const uchar*)compressed.constData(), compressed_size);
956 if ((
int)len != baunzip.size())
960 qWarning(
"QZip: Z_MEM_ERROR: Not enough memory");
966 qWarning(
"QZip: Z_DATA_ERROR: Input data is corrupted");
969 }
while (res == Z_BUF_ERROR);
973 qWarning(
"QZip: Unsupported compression method %d is needed to extract the data.", compression_method);
982bool QZipReader::extractAll(
const QString &destinationDir)
const
984 QDir baseDir(destinationDir);
987 const QList<FileInfo> allFiles = fileInfoList();
988 bool foundDirs =
false;
989 bool hasDirs =
false;
990 for (
const FileInfo &fi : allFiles) {
991 const QString absPath = destinationDir + QDir::separator() + fi.filePath;
994 if (!baseDir.mkpath(fi.filePath))
996 if (!QFile::setPermissions(absPath, fi.permissions))
998 }
else if (!hasDirs && fi.filePath.contains(u"/")) {
1008 if (hasDirs && !foundDirs) {
1009 for (
const FileInfo &fi : allFiles) {
1010 if (!fi.filePath.contains(u"/"))
1012 const auto dirPath = fi.filePath.left(fi.filePath.lastIndexOf(u"/"));
1013 if (!baseDir.mkpath(dirPath))
1021 for (
const FileInfo &fi : allFiles) {
1022 const QString absPath = destinationDir + QDir::separator() + fi.filePath;
1024 QString destination = QFile::decodeName(fileData(fi.filePath));
1025 if (destination.isEmpty())
1027 QFileInfo linkFi(absPath);
1028 if (!QFile::exists(linkFi.absolutePath()))
1029 QDir::root().mkpath(linkFi.absolutePath());
1030 if (!QFile::link(destination, absPath))
1033
1034
1035
1039 for (
const FileInfo &fi : allFiles) {
1040 const QString absPath = destinationDir + QDir::separator() + fi.filePath;
1043 if (!f.open(QIODevice::WriteOnly))
1045 f.write(fileData(fi.filePath));
1046 f.setPermissions(fi.permissions);
1103QZipWriter::QZipWriter(
const QString &fileName, QIODevice::OpenMode mode)
1105 auto f = std::make_unique<QFile>(fileName);
1106 QZipWriter::Status status;
1107 if (f->open(mode) && f->error() == QFile::NoError)
1108 status = QZipWriter::NoError;
1110 if (f->error() == QFile::WriteError)
1111 status = QZipWriter::FileWriteError;
1112 else if (f->error() == QFile::OpenError)
1113 status = QZipWriter::FileOpenError;
1114 else if (f->error() == QFile::PermissionsError)
1115 status = QZipWriter::FilePermissionsError;
1117 status = QZipWriter::FileError;
1120 d =
new QZipWriterPrivate(f.get(),
true);
1121 Q_UNUSED(f.release());
1269void QZipWriter::addFile(
const QString &fileName, QIODevice *device)
1272 QIODevice::OpenMode mode = device->openMode();
1273 bool opened =
false;
1274 if ((mode & QIODevice::ReadOnly) == 0) {
1276 if (! device->open(QIODevice::ReadOnly)) {
1277 d->status = FileOpenError;
1281 d->addEntry(QZipWriterPrivate::File, QDir::fromNativeSeparators(fileName), device->readAll());
1312void QZipWriter::close()
1314 if (!(d->device->openMode() & QIODevice::WriteOnly)) {
1320 d->device->seek(d->start_of_directory);
1322 for (
int i = 0; i < d->fileHeaders.size(); ++i) {
1323 const FileHeader &header = d->fileHeaders.at(i);
1324 d->device->write((
const char *)&header.h,
sizeof(CentralFileHeader));
1325 d->device->write(header.file_name);
1326 d->device->write(header.extra_field);
1327 d->device->write(header.file_comment);
1329 int dir_size = d->device->pos() - d->start_of_directory;
1332 memset(&eod, 0,
sizeof(EndOfDirectory));
1333 writeUInt(eod.signature, 0x06054b50);
1336 writeUShort(eod.num_dir_entries_this_disk, d->fileHeaders.size());
1337 writeUShort(eod.num_dir_entries, d->fileHeaders.size());
1338 writeUInt(eod.directory_size, dir_size);
1339 writeUInt(eod.dir_start_offset, d->start_of_directory);
1340 writeUShort(eod.comment_length, d->comment.size());
1342 d->device->write((
const char *)&eod,
sizeof(EndOfDirectory));
1343 d->device->write(d->comment);