1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtGui module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
44 #ifndef QT_NO_TEXTODFWRITER
46 #include "qzipreader_p.h"
47 #include "qzipwriter_p.h"
48 #include <qdatetime.h>
49 #include <qplatformdefs.h>
56 // Zip standard version for archives handled by this API
57 // (actually, the only basic support of this version is implemented but it is enough for now)
58 #define ZIP_VERSION 20
62 # define S_IFREG 0100000
64 # define S_IFDIR 0040000
67 # define S_ISDIR(x) ((x) & S_IFDIR) > 0
70 # define S_ISREG(x) ((x) & 0170000) == S_IFREG
72 # define S_IFLNK 020000
73 # define S_ISLNK(x) ((x) & S_IFLNK) > 0
91 #ifndef FILE_ATTRIBUTE_READONLY
92 # define FILE_ATTRIBUTE_READONLY 0x1
94 #ifndef FILE_ATTRIBUTE_DIRECTORY
95 # define FILE_ATTRIBUTE_DIRECTORY 0x10
101 #define ZDEBUG if (0) qDebug
106 static inline uint readUInt(const uchar *data)
108 return (data[0]) + (data[1]<<8) + (data[2]<<16) + (data[3]<<24);
111 static inline ushort readUShort(const uchar *data)
113 return (data[0]) + (data[1]<<8);
116 static inline void writeUInt(uchar *data, uint i)
119 data[1] = (i>>8) & 0xff;
120 data[2] = (i>>16) & 0xff;
121 data[3] = (i>>24) & 0xff;
124 static inline void writeUShort(uchar *data, ushort i)
127 data[1] = (i>>8) & 0xff;
130 static inline void copyUInt(uchar *dest, const uchar *src)
138 static inline void copyUShort(uchar *dest, const uchar *src)
144 static void writeMSDosDate(uchar *dest, const QDateTime& dt)
148 (dt.time().hour() << 11) // 5 bit hour
149 | (dt.time().minute() << 5) // 6 bit minute
150 | (dt.time().second() >> 1); // 5 bit double seconds
152 dest[0] = time & 0xff;
156 ((dt.date().year() - 1980) << 9) // 7 bit year 1980-based
157 | (dt.date().month() << 5) // 4 bit month
158 | (dt.date().day()); // 5 bit day
160 dest[2] = char(date);
161 dest[3] = char(date >> 8);
170 static quint32 permissionsToMode(QFile::Permissions perms)
173 if (perms & QFile::ReadOwner)
175 if (perms & QFile::WriteOwner)
177 if (perms & QFile::ExeOwner)
179 if (perms & QFile::ReadUser)
181 if (perms & QFile::WriteUser)
183 if (perms & QFile::ExeUser)
185 if (perms & QFile::ReadGroup)
187 if (perms & QFile::WriteGroup)
189 if (perms & QFile::ExeGroup)
191 if (perms & QFile::ReadOther)
193 if (perms & QFile::WriteOther)
195 if (perms & QFile::ExeOther)
200 static int inflate(Bytef *dest, ulong *destLen, const Bytef *source, ulong sourceLen)
205 stream.next_in = (Bytef*)source;
206 stream.avail_in = (uInt)sourceLen;
207 if ((uLong)stream.avail_in != sourceLen)
210 stream.next_out = dest;
211 stream.avail_out = (uInt)*destLen;
212 if ((uLong)stream.avail_out != *destLen)
215 stream.zalloc = (alloc_func)0;
216 stream.zfree = (free_func)0;
218 err = inflateInit2(&stream, -MAX_WBITS);
222 err = inflate(&stream, Z_FINISH);
223 if (err != Z_STREAM_END) {
225 if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0))
229 *destLen = stream.total_out;
231 err = inflateEnd(&stream);
235 static int deflate (Bytef *dest, ulong *destLen, const Bytef *source, ulong sourceLen)
240 stream.next_in = (Bytef*)source;
241 stream.avail_in = (uInt)sourceLen;
242 stream.next_out = dest;
243 stream.avail_out = (uInt)*destLen;
244 if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
246 stream.zalloc = (alloc_func)0;
247 stream.zfree = (free_func)0;
248 stream.opaque = (voidpf)0;
250 err = deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY);
251 if (err != Z_OK) return err;
253 err = deflate(&stream, Z_FINISH);
254 if (err != Z_STREAM_END) {
256 return err == Z_OK ? Z_BUF_ERROR : err;
258 *destLen = stream.total_out;
260 err = deflateEnd(&stream);
264 static QFile::Permissions modeToPermissions(quint32 mode)
266 QFile::Permissions ret;
268 ret |= QFile::ReadOwner;
270 ret |= QFile::WriteOwner;
272 ret |= QFile::ExeOwner;
274 ret |= QFile::ReadUser;
276 ret |= QFile::WriteUser;
278 ret |= QFile::ExeUser;
280 ret |= QFile::ReadGroup;
282 ret |= QFile::WriteGroup;
284 ret |= QFile::ExeGroup;
286 ret |= QFile::ReadOther;
288 ret |= QFile::WriteOther;
290 ret |= QFile::ExeOther;
294 static QDateTime readMSDosDate(const uchar *src)
296 uint dosDate = readUInt(src);
298 uDate = (quint64)(dosDate >> 16);
299 uint tm_mday = (uDate & 0x1f);
300 uint tm_mon = ((uDate & 0x1E0) >> 5);
301 uint tm_year = (((uDate & 0x0FE00) >> 9) + 1980);
302 uint tm_hour = ((dosDate & 0xF800) >> 11);
303 uint tm_min = ((dosDate & 0x7E0) >> 5);
304 uint tm_sec = ((dosDate & 0x1f) << 1);
306 return QDateTime(QDate(tm_year, tm_mon, tm_mday), QTime(tm_hour, tm_min, tm_sec));
309 // for details, see http://www.pkware.com/documents/casestudies/APPNOTE.TXT
314 HostVMS = 2, // VAX/VMS
317 HostAtari = 5, // what if it's a minix filesystem? [cjh]
318 HostHPFS = 6, // filesystem used by OS/2 (and NT 3.x)
322 HostTOPS20 = 10, // pkzip 2.50 NTFS
323 HostNTFS = 11, // filesystem used by Windows NT
324 HostQDOS = 12, // SMS/QDOS
325 HostAcorn = 13, // Archimedes Acorn RISC OS
326 HostVFAT = 14, // filesystem used by Windows 95, NT
328 HostBeOS = 16, // hybrid POSIX/database filesystem
334 enum GeneralPurposeFlag {
338 HasDataDescriptor = 0x08,
340 StrongEncrypted = 0x40,
342 CentralDirectoryEncrypted = 0x2000
345 enum CompressionMethod {
346 CompressionMethodStored = 0,
347 CompressionMethodShrunk = 1,
348 CompressionMethodReduced1 = 2,
349 CompressionMethodReduced2 = 3,
350 CompressionMethodReduced3 = 4,
351 CompressionMethodReduced4 = 5,
352 CompressionMethodImploded = 6,
353 CompressionMethodReservedTokenizing = 7, // reserved for tokenizing
354 CompressionMethodDeflated = 8,
355 CompressionMethodDeflated64 = 9,
356 CompressionMethodPKImploding = 10,
358 CompressionMethodBZip2 = 12,
360 CompressionMethodLZMA = 14,
362 CompressionMethodTerse = 18,
363 CompressionMethodLz77 = 19,
365 CompressionMethodJpeg = 96,
366 CompressionMethodWavPack = 97,
367 CompressionMethodPPMd = 98,
368 CompressionMethodWzAES = 99
371 struct LocalFileHeader
373 uchar signature[4]; // 0x04034b50
374 uchar version_needed[2];
375 uchar general_purpose_bits[2];
376 uchar compression_method[2];
377 uchar last_mod_file[4];
379 uchar compressed_size[4];
380 uchar uncompressed_size[4];
381 uchar file_name_length[2];
382 uchar extra_field_length[2];
385 struct DataDescriptor
388 uchar compressed_size[4];
389 uchar uncompressed_size[4];
392 struct CentralFileHeader
394 uchar signature[4]; // 0x02014b50
395 uchar version_made[2];
396 uchar version_needed[2];
397 uchar general_purpose_bits[2];
398 uchar compression_method[2];
399 uchar last_mod_file[4];
401 uchar compressed_size[4];
402 uchar uncompressed_size[4];
403 uchar file_name_length[2];
404 uchar extra_field_length[2];
405 uchar file_comment_length[2];
407 uchar internal_file_attributes[2];
408 uchar external_file_attributes[4];
409 uchar offset_local_header[4];
410 LocalFileHeader toLocalHeader() const;
413 struct EndOfDirectory
415 uchar signature[4]; // 0x06054b50
417 uchar start_of_directory_disk[2];
418 uchar num_dir_entries_this_disk[2];
419 uchar num_dir_entries[2];
420 uchar directory_size[4];
421 uchar dir_start_offset[4];
422 uchar comment_length[2];
428 QByteArray file_name;
429 QByteArray extra_field;
430 QByteArray file_comment;
433 QZipReader::FileInfo::FileInfo()
434 : isDir(false), isFile(false), isSymLink(false), crc(0), size(0)
438 QZipReader::FileInfo::~FileInfo()
442 QZipReader::FileInfo::FileInfo(const FileInfo &other)
447 QZipReader::FileInfo& QZipReader::FileInfo::operator=(const FileInfo &other)
449 filePath = other.filePath;
451 isFile = other.isFile;
452 isSymLink = other.isSymLink;
453 permissions = other.permissions;
456 lastModified = other.lastModified;
460 bool QZipReader::FileInfo::isValid() const
462 return isDir || isFile || isSymLink;
468 QZipPrivate(QIODevice *device, bool ownDev)
469 : device(device), ownDevice(ownDev), dirtyFileTree(true), start_of_directory(0)
479 void fillFileInfo(int index, QZipReader::FileInfo &fileInfo) const;
484 QList<FileHeader> fileHeaders;
486 uint start_of_directory;
489 void QZipPrivate::fillFileInfo(int index, QZipReader::FileInfo &fileInfo) const
491 FileHeader header = fileHeaders.at(index);
492 quint32 mode = readUInt(header.h.external_file_attributes);
493 const HostOS hostOS = HostOS(readUShort(header.h.version_made) >> 8);
496 mode = (mode >> 16) & 0xffff;
498 fileInfo.isDir = true;
499 else if (S_ISREG(mode))
500 fileInfo.isFile = true;
501 else if (S_ISLNK(mode))
502 fileInfo.isSymLink = true;
503 fileInfo.permissions = modeToPermissions(mode);
509 fileInfo.permissions |= QFile::ReadOwner | QFile::ReadUser | QFile::ReadGroup | QFile::ReadOther;
510 if ((mode & FILE_ATTRIBUTE_READONLY) == 0)
511 fileInfo.permissions |= QFile::WriteOwner | QFile::WriteUser | QFile::WriteGroup | QFile::WriteOther;
512 if ((mode & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) {
513 fileInfo.isDir = true;
514 fileInfo.permissions |= QFile::ExeOwner | QFile::ExeUser | QFile::ExeGroup | QFile::ExeOther;
516 fileInfo.isFile = true;
520 qWarning("QZip: Zip entry format at %d is not supported.", index);
521 return; // we don't support anything else
524 ushort general_purpose_bits = readUShort(header.h.general_purpose_bits);
525 // if bit 11 is set, the filename and comment fields must be encoded using UTF-8
526 const bool inUtf8 = (general_purpose_bits & Utf8Names) != 0;
527 fileInfo.filePath = inUtf8 ? QString::fromUtf8(header.file_name) : QString::fromLocal8Bit(header.file_name);
528 fileInfo.crc = readUInt(header.h.crc_32);
529 fileInfo.size = readUInt(header.h.uncompressed_size);
530 fileInfo.lastModified = readMSDosDate(header.h.last_mod_file);
532 // fix the file path, if broken (convert separators, eat leading and trailing ones)
533 fileInfo.filePath = QDir::fromNativeSeparators(fileInfo.filePath);
534 while (!fileInfo.filePath.isEmpty() && (fileInfo.filePath.at(0) == QLatin1Char('.') || fileInfo.filePath.at(0) == QLatin1Char('/')))
535 fileInfo.filePath = fileInfo.filePath.mid(1);
536 while (!fileInfo.filePath.isEmpty() && fileInfo.filePath.at(fileInfo.filePath.size() - 1) == QLatin1Char('/'))
537 fileInfo.filePath.chop(1);
540 class QZipReaderPrivate : public QZipPrivate
543 QZipReaderPrivate(QIODevice *device, bool ownDev)
544 : QZipPrivate(device, ownDev), status(QZipReader::NoError)
550 QZipReader::Status status;
553 class QZipWriterPrivate : public QZipPrivate
556 QZipWriterPrivate(QIODevice *device, bool ownDev)
557 : QZipPrivate(device, ownDev),
558 status(QZipWriter::NoError),
559 permissions(QFile::ReadOwner | QFile::WriteOwner),
560 compressionPolicy(QZipWriter::AlwaysCompress)
564 QZipWriter::Status status;
565 QFile::Permissions permissions;
566 QZipWriter::CompressionPolicy compressionPolicy;
568 enum EntryType { Directory, File, Symlink };
570 void addEntry(EntryType type, const QString &fileName, const QByteArray &contents);
573 LocalFileHeader CentralFileHeader::toLocalHeader() const
576 writeUInt(h.signature, 0x04034b50);
577 copyUShort(h.version_needed, version_needed);
578 copyUShort(h.general_purpose_bits, general_purpose_bits);
579 copyUShort(h.compression_method, compression_method);
580 copyUInt(h.last_mod_file, last_mod_file);
581 copyUInt(h.crc_32, crc_32);
582 copyUInt(h.compressed_size, compressed_size);
583 copyUInt(h.uncompressed_size, uncompressed_size);
584 copyUShort(h.file_name_length, file_name_length);
585 copyUShort(h.extra_field_length, extra_field_length);
589 void QZipReaderPrivate::scanFiles()
594 if (! (device->isOpen() || device->open(QIODevice::ReadOnly))) {
595 status = QZipReader::FileOpenError;
599 if ((device->openMode() & QIODevice::ReadOnly) == 0) { // only read the index from readable files.
600 status = QZipReader::FileReadError;
604 dirtyFileTree = false;
606 device->read((char *)tmp, 4);
607 if (readUInt(tmp) != 0x04034b50) {
608 qWarning() << "QZip: not a zip file!";
612 // find EndOfDirectory header
614 int start_of_directory = -1;
615 int num_dir_entries = 0;
617 while (start_of_directory == -1) {
618 const int pos = device->size() - int(sizeof(EndOfDirectory)) - i;
619 if (pos < 0 || i > 65535) {
620 qWarning() << "QZip: EndOfDirectory not found";
625 device->read((char *)&eod, sizeof(EndOfDirectory));
626 if (readUInt(eod.signature) == 0x06054b50)
632 start_of_directory = readUInt(eod.dir_start_offset);
633 num_dir_entries = readUShort(eod.num_dir_entries);
634 ZDEBUG("start_of_directory at %d, num_dir_entries=%d", start_of_directory, num_dir_entries);
635 int comment_length = readUShort(eod.comment_length);
636 if (comment_length != i)
637 qWarning() << "QZip: failed to parse zip file.";
638 comment = device->read(qMin(comment_length, i));
641 device->seek(start_of_directory);
642 for (i = 0; i < num_dir_entries; ++i) {
644 int read = device->read((char *) &header.h, sizeof(CentralFileHeader));
645 if (read < (int)sizeof(CentralFileHeader)) {
646 qWarning() << "QZip: Failed to read complete header, index may be incomplete";
649 if (readUInt(header.h.signature) != 0x02014b50) {
650 qWarning() << "QZip: invalid header signature, index may be incomplete";
654 int l = readUShort(header.h.file_name_length);
655 header.file_name = device->read(l);
656 if (header.file_name.length() != l) {
657 qWarning() << "QZip: Failed to read filename from zip index, index may be incomplete";
660 l = readUShort(header.h.extra_field_length);
661 header.extra_field = device->read(l);
662 if (header.extra_field.length() != l) {
663 qWarning() << "QZip: Failed to read extra field in zip file, skipping file, index may be incomplete";
666 l = readUShort(header.h.file_comment_length);
667 header.file_comment = device->read(l);
668 if (header.file_comment.length() != l) {
669 qWarning() << "QZip: Failed to read read file comment, index may be incomplete";
673 ZDEBUG("found file '%s'", header.file_name.data());
674 fileHeaders.append(header);
678 void QZipWriterPrivate::addEntry(EntryType type, const QString &fileName, const QByteArray &contents/*, QFile::Permissions permissions, QZip::Method m*/)
681 static const char *entryTypes[] = {
685 ZDEBUG() << "adding" << entryTypes[type] <<":" << fileName.toUtf8().data() << (type == 2 ? QByteArray(" -> " + contents).constData() : "");
688 if (! (device->isOpen() || device->open(QIODevice::WriteOnly))) {
689 status = QZipWriter::FileOpenError;
692 device->seek(start_of_directory);
694 // don't compress small files
695 QZipWriter::CompressionPolicy compression = compressionPolicy;
696 if (compressionPolicy == QZipWriter::AutoCompress) {
697 if (contents.length() < 64)
698 compression = QZipWriter::NeverCompress;
700 compression = QZipWriter::AlwaysCompress;
704 memset(&header.h, 0, sizeof(CentralFileHeader));
705 writeUInt(header.h.signature, 0x02014b50);
707 writeUShort(header.h.version_needed, ZIP_VERSION);
708 writeUInt(header.h.uncompressed_size, contents.length());
709 writeMSDosDate(header.h.last_mod_file, QDateTime::currentDateTime());
710 QByteArray data = contents;
711 if (compression == QZipWriter::AlwaysCompress) {
712 writeUShort(header.h.compression_method, CompressionMethodDeflated);
714 ulong len = contents.length();
715 // shamelessly copied form zlib
716 len += (len >> 12) + (len >> 14) + 11;
720 res = deflate((uchar*)data.data(), &len, (const uchar*)contents.constData(), contents.length());
727 qWarning("QZip: Z_MEM_ERROR: Not enough memory to compress file, skipping");
734 } while (res == Z_BUF_ERROR);
736 // TODO add a check if data.length() > contents.length(). Then try to store the original and revert the compression method to be uncompressed
737 writeUInt(header.h.compressed_size, data.length());
738 uint crc_32 = ::crc32(0, 0, 0);
739 crc_32 = ::crc32(crc_32, (const uchar *)contents.constData(), contents.length());
740 writeUInt(header.h.crc_32, crc_32);
742 // if bit 11 is set, the filename and comment fields must be encoded using UTF-8
743 ushort general_purpose_bits = Utf8Names; // always use utf-8
744 writeUShort(header.h.general_purpose_bits, general_purpose_bits);
746 const bool inUtf8 = (general_purpose_bits & Utf8Names) != 0;
747 header.file_name = inUtf8 ? fileName.toUtf8() : fileName.toLocal8Bit();
748 if (header.file_name.size() > 0xffff) {
749 qWarning("QZip: Filename is too long, chopping it to 65535 bytes");
750 header.file_name = header.file_name.left(0xffff); // ### don't break the utf-8 sequence, if any
752 if (header.file_comment.size() + header.file_name.size() > 0xffff) {
753 qWarning("QZip: File comment is too long, chopping it to 65535 bytes");
754 header.file_comment.truncate(0xffff - header.file_name.size()); // ### don't break the utf-8 sequence, if any
756 writeUShort(header.h.file_name_length, header.file_name.length());
757 //h.extra_field_length[2];
759 writeUShort(header.h.version_made, HostUnix << 8);
760 //uchar internal_file_attributes[2];
761 //uchar external_file_attributes[4];
762 quint32 mode = permissionsToMode(permissions);
764 case File: mode |= S_IFREG; break;
765 case Directory: mode |= S_IFDIR; break;
766 case Symlink: mode |= S_IFLNK; break;
768 writeUInt(header.h.external_file_attributes, mode << 16);
769 writeUInt(header.h.offset_local_header, start_of_directory);
772 fileHeaders.append(header);
774 LocalFileHeader h = header.h.toLocalHeader();
775 device->write((const char *)&h, sizeof(LocalFileHeader));
776 device->write(header.file_name);
778 start_of_directory = device->pos();
779 dirtyFileTree = true;
782 ////////////////////////////// Reader
785 \class QZipReader::FileInfo
787 Represents one entry in the zip table of contents.
791 \variable FileInfo::filePath
792 The full filepath inside the archive.
796 \variable FileInfo::isDir
797 A boolean type indicating if the entry is a directory.
801 \variable FileInfo::isFile
802 A boolean type, if it is one this entry is a file.
806 \variable FileInfo::isSymLink
807 A boolean type, if it is one this entry is symbolic link.
811 \variable FileInfo::permissions
812 A list of flags for the permissions of this entry.
816 \variable FileInfo::crc
817 The calculated checksum as a crc type.
821 \variable FileInfo::size
822 The total size of the unpacked content.
826 \variable FileInfo::d
836 \brief the QZipReader class provides a way to inspect the contents of a zip
837 archive and extract individual files from it.
839 QZipReader can be used to read a zip archive either from a file or from any
840 device. An in-memory QBuffer for instance. The reader can be used to read
841 which files are in the archive using fileInfoList() and entryInfoAt() but
842 also to extract individual files using fileData() or even to extract all
843 files in the archive using extractAll()
847 Create a new zip archive that operates on the \a fileName. The file will be
848 opened with the \a mode.
850 QZipReader::QZipReader(const QString &archive, QIODevice::OpenMode mode)
852 QScopedPointer<QFile> f(new QFile(archive));
854 QZipReader::Status status;
855 if (f->error() == QFile::NoError)
858 if (f->error() == QFile::ReadError)
859 status = FileReadError;
860 else if (f->error() == QFile::OpenError)
861 status = FileOpenError;
862 else if (f->error() == QFile::PermissionsError)
863 status = FilePermissionsError;
868 d = new QZipReaderPrivate(f.data(), /*ownDevice=*/true);
874 Create a new zip archive that operates on the archive found in \a device.
875 You have to open the device previous to calling the constructor and only a
876 device that is readable will be scanned for zip filecontent.
878 QZipReader::QZipReader(QIODevice *device)
879 : d(new QZipReaderPrivate(device, /*ownDevice=*/false))
887 QZipReader::~QZipReader()
894 Returns device used for reading zip archive.
896 QIODevice* QZipReader::device() const
902 Returns true if the user can read the file; otherwise returns false.
904 bool QZipReader::isReadable() const
906 return d->device->isReadable();
910 Returns true if the file exists; otherwise returns false.
912 bool QZipReader::exists() const
914 QFile *f = qobject_cast<QFile*> (d->device);
921 Returns the list of files the archive contains.
923 QList<QZipReader::FileInfo> QZipReader::fileInfoList() const
926 QList<QZipReader::FileInfo> files;
927 for (int i = 0; i < d->fileHeaders.size(); ++i) {
928 QZipReader::FileInfo fi;
929 d->fillFileInfo(i, fi);
937 Return the number of items in the zip archive.
939 int QZipReader::count() const
942 return d->fileHeaders.count();
946 Returns a FileInfo of an entry in the zipfile.
947 The \a index is the index into the directory listing of the zipfile.
948 Returns an invalid FileInfo if \a index is out of boundaries.
952 QZipReader::FileInfo QZipReader::entryInfoAt(int index) const
955 QZipReader::FileInfo fi;
956 if (index >= 0 && index < d->fileHeaders.count())
957 d->fillFileInfo(index, fi);
962 Fetch the file contents from the zip archive and return the uncompressed bytes.
964 QByteArray QZipReader::fileData(const QString &fileName) const
968 for (i = 0; i < d->fileHeaders.size(); ++i) {
969 if (QString::fromLocal8Bit(d->fileHeaders.at(i).file_name) == fileName)
972 if (i == d->fileHeaders.size())
975 FileHeader header = d->fileHeaders.at(i);
977 ushort version_needed = readUShort(header.h.version_needed);
978 if (version_needed > ZIP_VERSION) {
979 qWarning("QZip: .ZIP specification version %d implementationis needed to extract the data.", version_needed);
983 ushort general_purpose_bits = readUShort(header.h.general_purpose_bits);
984 int compressed_size = readUInt(header.h.compressed_size);
985 int uncompressed_size = readUInt(header.h.uncompressed_size);
986 int start = readUInt(header.h.offset_local_header);
987 //qDebug("uncompressing file %d: local header at %d", i, start);
989 d->device->seek(start);
991 d->device->read((char *)&lh, sizeof(LocalFileHeader));
992 uint skip = readUShort(lh.file_name_length) + readUShort(lh.extra_field_length);
993 d->device->seek(d->device->pos() + skip);
995 int compression_method = readUShort(lh.compression_method);
996 //qDebug("file=%s: compressed_size=%d, uncompressed_size=%d", fileName.toLocal8Bit().data(), compressed_size, uncompressed_size);
998 if ((general_purpose_bits & Encrypted) != 0) {
999 qWarning("QZip: Unsupported encryption method is needed to extract the data.");
1000 return QByteArray();
1003 //qDebug("file at %lld", d->device->pos());
1004 QByteArray compressed = d->device->read(compressed_size);
1005 if (compression_method == CompressionMethodStored) {
1007 compressed.truncate(uncompressed_size);
1009 } else if (compression_method == CompressionMethodDeflated) {
1011 //qDebug("compressed=%d", compressed.size());
1012 compressed.truncate(compressed_size);
1014 ulong len = qMax(uncompressed_size, 1);
1017 baunzip.resize(len);
1018 res = inflate((uchar*)baunzip.data(), &len,
1019 (uchar*)compressed.constData(), compressed_size);
1023 if ((int)len != baunzip.size())
1024 baunzip.resize(len);
1027 qWarning("QZip: Z_MEM_ERROR: Not enough memory");
1033 qWarning("QZip: Z_DATA_ERROR: Input data is corrupted");
1036 } while (res == Z_BUF_ERROR);
1040 qWarning("QZip: Unsupported compression method %d is needed to extract the data.", compression_method);
1041 return QByteArray();
1045 Extracts the full contents of the zip file into \a destinationDir on
1046 the local filesystem.
1047 In case writing or linking a file fails, the extraction will be aborted.
1049 bool QZipReader::extractAll(const QString &destinationDir) const
1051 QDir baseDir(destinationDir);
1053 // create directories first
1054 QList<FileInfo> allFiles = fileInfoList();
1055 foreach (FileInfo fi, allFiles) {
1056 const QString absPath = destinationDir + QDir::separator() + fi.filePath;
1058 if (!baseDir.mkpath(fi.filePath))
1060 if (!QFile::setPermissions(absPath, fi.permissions))
1066 foreach (FileInfo fi, allFiles) {
1067 const QString absPath = destinationDir + QDir::separator() + fi.filePath;
1069 QString destination = QFile::decodeName(fileData(fi.filePath));
1070 if (destination.isEmpty())
1072 QFileInfo linkFi(absPath);
1073 if (!QFile::exists(linkFi.absolutePath()))
1074 QDir::root().mkpath(linkFi.absolutePath());
1075 if (!QFile::link(destination, absPath))
1077 /* cannot change permission of links
1078 if (!QFile::setPermissions(absPath, fi.permissions))
1084 foreach (FileInfo fi, allFiles) {
1085 const QString absPath = destinationDir + QDir::separator() + fi.filePath;
1088 if (!f.open(QIODevice::WriteOnly))
1090 f.write(fileData(fi.filePath));
1091 f.setPermissions(fi.permissions);
1100 \enum QZipReader::Status
1102 The following status values are possible:
1104 \value NoError No error occurred.
1105 \value FileReadError An error occurred when reading from the file.
1106 \value FileOpenError The file could not be opened.
1107 \value FilePermissionsError The file could not be accessed.
1108 \value FileError Another file error occurred.
1112 Returns a status code indicating the first error that was met by QZipReader,
1113 or QZipReader::NoError if no error occurred.
1115 QZipReader::Status QZipReader::status() const
1123 void QZipReader::close()
1128 ////////////////////////////// Writer
1135 \brief the QZipWriter class provides a way to create a new zip archive.
1137 QZipWriter can be used to create a zip archive containing any number of files
1138 and directories. The files in the archive will be compressed in a way that is
1139 compatible with common zip reader applications.
1144 Create a new zip archive that operates on the \a archive filename. The file will
1145 be opened with the \a mode.
1148 QZipWriter::QZipWriter(const QString &fileName, QIODevice::OpenMode mode)
1150 QScopedPointer<QFile> f(new QFile(fileName));
1152 QZipWriter::Status status;
1153 if (f->error() == QFile::NoError)
1154 status = QZipWriter::NoError;
1156 if (f->error() == QFile::WriteError)
1157 status = QZipWriter::FileWriteError;
1158 else if (f->error() == QFile::OpenError)
1159 status = QZipWriter::FileOpenError;
1160 else if (f->error() == QFile::PermissionsError)
1161 status = QZipWriter::FilePermissionsError;
1163 status = QZipWriter::FileError;
1166 d = new QZipWriterPrivate(f.data(), /*ownDevice=*/true);
1172 Create a new zip archive that operates on the archive found in \a device.
1173 You have to open the device previous to calling the constructor and
1174 only a device that is readable will be scanned for zip filecontent.
1176 QZipWriter::QZipWriter(QIODevice *device)
1177 : d(new QZipWriterPrivate(device, /*ownDevice=*/false))
1182 QZipWriter::~QZipWriter()
1189 Returns device used for writing zip archive.
1191 QIODevice* QZipWriter::device() const
1197 Returns true if the user can write to the archive; otherwise returns false.
1199 bool QZipWriter::isWritable() const
1201 return d->device->isWritable();
1205 Returns true if the file exists; otherwise returns false.
1207 bool QZipWriter::exists() const
1209 QFile *f = qobject_cast<QFile*> (d->device);
1216 \enum QZipWriter::Status
1218 The following status values are possible:
1220 \value NoError No error occurred.
1221 \value FileWriteError An error occurred when writing to the device.
1222 \value FileOpenError The file could not be opened.
1223 \value FilePermissionsError The file could not be accessed.
1224 \value FileError Another file error occurred.
1228 Returns a status code indicating the first error that was met by QZipWriter,
1229 or QZipWriter::NoError if no error occurred.
1231 QZipWriter::Status QZipWriter::status() const
1237 \enum QZipWriter::CompressionPolicy
1239 \value AlwaysCompress A file that is added is compressed.
1240 \value NeverCompress A file that is added will be stored without changes.
1241 \value AutoCompress A file that is added will be compressed only if that will give a smaller file.
1245 Sets the policy for compressing newly added files to the new \a policy.
1247 \note the default policy is AlwaysCompress
1249 \sa compressionPolicy()
1252 void QZipWriter::setCompressionPolicy(CompressionPolicy policy)
1254 d->compressionPolicy = policy;
1258 Returns the currently set compression policy.
1259 \sa setCompressionPolicy()
1262 QZipWriter::CompressionPolicy QZipWriter::compressionPolicy() const
1264 return d->compressionPolicy;
1268 Sets the permissions that will be used for newly added files.
1270 \note the default permissions are QFile::ReadOwner | QFile::WriteOwner.
1272 \sa creationPermissions()
1275 void QZipWriter::setCreationPermissions(QFile::Permissions permissions)
1277 d->permissions = permissions;
1281 Returns the currently set creation permissions.
1283 \sa setCreationPermissions()
1286 QFile::Permissions QZipWriter::creationPermissions() const
1288 return d->permissions;
1292 Add a file to the archive with \a data as the file contents.
1293 The file will be stored in the archive using the \a fileName which
1294 includes the full path in the archive.
1296 The new file will get the file permissions based on the current
1297 creationPermissions and it will be compressed using the zip compression
1298 based on the current compression policy.
1300 \sa setCreationPermissions()
1301 \sa setCompressionPolicy()
1303 void QZipWriter::addFile(const QString &fileName, const QByteArray &data)
1305 d->addEntry(QZipWriterPrivate::File, fileName, data);
1309 Add a file to the archive with \a device as the source of the contents.
1310 The contents returned from QIODevice::readAll() will be used as the
1312 The file will be stored in the archive using the \a fileName which
1313 includes the full path in the archive.
1315 void QZipWriter::addFile(const QString &fileName, QIODevice *device)
1318 QIODevice::OpenMode mode = device->openMode();
1319 bool opened = false;
1320 if ((mode & QIODevice::ReadOnly) == 0) {
1322 if (! device->open(QIODevice::ReadOnly)) {
1323 d->status = FileOpenError;
1327 d->addEntry(QZipWriterPrivate::File, fileName, device->readAll());
1333 Create a new directory in the archive with the specified \a dirName and
1336 void QZipWriter::addDirectory(const QString &dirName)
1338 QString name = dirName;
1339 // separator is mandatory
1340 if (!name.endsWith(QDir::separator()))
1341 name.append(QDir::separator());
1342 d->addEntry(QZipWriterPrivate::Directory, name, QByteArray());
1346 Create a new symbolic link in the archive with the specified \a dirName
1347 and the \a permissions;
1348 A symbolic link contains the destination (relative) path and name.
1350 void QZipWriter::addSymLink(const QString &fileName, const QString &destination)
1352 d->addEntry(QZipWriterPrivate::Symlink, fileName, QFile::encodeName(destination));
1356 Closes the zip file.
1358 void QZipWriter::close()
1360 if (!(d->device->openMode() & QIODevice::WriteOnly)) {
1365 //qDebug("QZip::close writing directory, %d entries", d->fileHeaders.size());
1366 d->device->seek(d->start_of_directory);
1367 // write new directory
1368 for (int i = 0; i < d->fileHeaders.size(); ++i) {
1369 const FileHeader &header = d->fileHeaders.at(i);
1370 d->device->write((const char *)&header.h, sizeof(CentralFileHeader));
1371 d->device->write(header.file_name);
1372 d->device->write(header.extra_field);
1373 d->device->write(header.file_comment);
1375 int dir_size = d->device->pos() - d->start_of_directory;
1376 // write end of directory
1378 memset(&eod, 0, sizeof(EndOfDirectory));
1379 writeUInt(eod.signature, 0x06054b50);
1380 //uchar this_disk[2];
1381 //uchar start_of_directory_disk[2];
1382 writeUShort(eod.num_dir_entries_this_disk, d->fileHeaders.size());
1383 writeUShort(eod.num_dir_entries, d->fileHeaders.size());
1384 writeUInt(eod.directory_size, dir_size);
1385 writeUInt(eod.dir_start_offset, d->start_of_directory);
1386 writeUShort(eod.comment_length, d->comment.length());
1388 d->device->write((const char *)&eod, sizeof(EndOfDirectory));
1389 d->device->write(d->comment);
1395 #endif // QT_NO_TEXTODFWRITER