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>
58 # define S_IFREG 0100000
60 # define S_IFDIR 0040000
63 # define S_ISDIR(x) ((x) & S_IFDIR) > 0
66 # define S_ISREG(x) ((x) & 0170000) == S_IFREG
68 # define S_IFLNK 020000
69 # define S_ISLNK(x) ((x) & S_IFLNK) > 0
90 #define ZDEBUG if (0) qDebug
95 static inline uint readUInt(const uchar *data)
97 return (data[0]) + (data[1]<<8) + (data[2]<<16) + (data[3]<<24);
100 static inline ushort readUShort(const uchar *data)
102 return (data[0]) + (data[1]<<8);
105 static inline void writeUInt(uchar *data, uint i)
108 data[1] = (i>>8) & 0xff;
109 data[2] = (i>>16) & 0xff;
110 data[3] = (i>>24) & 0xff;
113 static inline void writeUShort(uchar *data, ushort i)
116 data[1] = (i>>8) & 0xff;
119 static inline void copyUInt(uchar *dest, const uchar *src)
127 static inline void copyUShort(uchar *dest, const uchar *src)
133 static void writeMSDosDate(uchar *dest, const QDateTime& dt)
137 (dt.time().hour() << 11) // 5 bit hour
138 | (dt.time().minute() << 5) // 6 bit minute
139 | (dt.time().second() >> 1); // 5 bit double seconds
141 dest[0] = time & 0xff;
145 ((dt.date().year() - 1980) << 9) // 7 bit year 1980-based
146 | (dt.date().month() << 5) // 4 bit month
147 | (dt.date().day()); // 5 bit day
149 dest[2] = char(date);
150 dest[3] = char(date >> 8);
159 static quint32 permissionsToMode(QFile::Permissions perms)
162 if (perms & QFile::ReadOwner)
164 if (perms & QFile::WriteOwner)
166 if (perms & QFile::ExeOwner)
168 if (perms & QFile::ReadUser)
170 if (perms & QFile::WriteUser)
172 if (perms & QFile::ExeUser)
174 if (perms & QFile::ReadGroup)
176 if (perms & QFile::WriteGroup)
178 if (perms & QFile::ExeGroup)
180 if (perms & QFile::ReadOther)
182 if (perms & QFile::WriteOther)
184 if (perms & QFile::ExeOther)
189 static int inflate(Bytef *dest, ulong *destLen, const Bytef *source, ulong sourceLen)
194 stream.next_in = (Bytef*)source;
195 stream.avail_in = (uInt)sourceLen;
196 if ((uLong)stream.avail_in != sourceLen)
199 stream.next_out = dest;
200 stream.avail_out = (uInt)*destLen;
201 if ((uLong)stream.avail_out != *destLen)
204 stream.zalloc = (alloc_func)0;
205 stream.zfree = (free_func)0;
207 err = inflateInit2(&stream, -MAX_WBITS);
211 err = inflate(&stream, Z_FINISH);
212 if (err != Z_STREAM_END) {
214 if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0))
218 *destLen = stream.total_out;
220 err = inflateEnd(&stream);
224 static int deflate (Bytef *dest, ulong *destLen, const Bytef *source, ulong sourceLen)
229 stream.next_in = (Bytef*)source;
230 stream.avail_in = (uInt)sourceLen;
231 stream.next_out = dest;
232 stream.avail_out = (uInt)*destLen;
233 if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
235 stream.zalloc = (alloc_func)0;
236 stream.zfree = (free_func)0;
237 stream.opaque = (voidpf)0;
239 err = deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY);
240 if (err != Z_OK) return err;
242 err = deflate(&stream, Z_FINISH);
243 if (err != Z_STREAM_END) {
245 return err == Z_OK ? Z_BUF_ERROR : err;
247 *destLen = stream.total_out;
249 err = deflateEnd(&stream);
253 static QFile::Permissions modeToPermissions(quint32 mode)
255 QFile::Permissions ret;
257 ret |= QFile::ReadOwner;
259 ret |= QFile::WriteOwner;
261 ret |= QFile::ExeOwner;
263 ret |= QFile::ReadUser;
265 ret |= QFile::WriteUser;
267 ret |= QFile::ExeUser;
269 ret |= QFile::ReadGroup;
271 ret |= QFile::WriteGroup;
273 ret |= QFile::ExeGroup;
275 ret |= QFile::ReadOther;
277 ret |= QFile::WriteOther;
279 ret |= QFile::ExeOther;
283 static QDateTime readMSDosDate(const uchar *src)
285 uint dosDate = readUInt(src);
287 uDate = (quint64)(dosDate >> 16);
288 uint tm_mday = (uDate & 0x1f);
289 uint tm_mon = ((uDate & 0x1E0) >> 5);
290 uint tm_year = (((uDate & 0x0FE00) >> 9) + 1980);
291 uint tm_hour = ((dosDate & 0xF800) >> 11);
292 uint tm_min = ((dosDate & 0x7E0) >> 5);
293 uint tm_sec = ((dosDate & 0x1f) << 1);
295 return QDateTime(QDate(tm_year, tm_mon, tm_mday), QTime(tm_hour, tm_min, tm_sec));
298 struct LocalFileHeader
300 uchar signature[4]; // 0x04034b50
301 uchar version_needed[2];
302 uchar general_purpose_bits[2];
303 uchar compression_method[2];
304 uchar last_mod_file[4];
306 uchar compressed_size[4];
307 uchar uncompressed_size[4];
308 uchar file_name_length[2];
309 uchar extra_field_length[2];
312 struct DataDescriptor
315 uchar compressed_size[4];
316 uchar uncompressed_size[4];
319 struct CentralFileHeader
321 uchar signature[4]; // 0x02014b50
322 uchar version_made[2];
323 uchar version_needed[2];
324 uchar general_purpose_bits[2];
325 uchar compression_method[2];
326 uchar last_mod_file[4];
328 uchar compressed_size[4];
329 uchar uncompressed_size[4];
330 uchar file_name_length[2];
331 uchar extra_field_length[2];
332 uchar file_comment_length[2];
334 uchar internal_file_attributes[2];
335 uchar external_file_attributes[4];
336 uchar offset_local_header[4];
337 LocalFileHeader toLocalHeader() const;
340 struct EndOfDirectory
342 uchar signature[4]; // 0x06054b50
344 uchar start_of_directory_disk[2];
345 uchar num_dir_entries_this_disk[2];
346 uchar num_dir_entries[2];
347 uchar directory_size[4];
348 uchar dir_start_offset[4];
349 uchar comment_length[2];
355 QByteArray file_name;
356 QByteArray extra_field;
357 QByteArray file_comment;
360 QZipReader::FileInfo::FileInfo()
361 : isDir(false), isFile(false), isSymLink(false), crc32(0), size(0)
365 QZipReader::FileInfo::~FileInfo()
369 QZipReader::FileInfo::FileInfo(const FileInfo &other)
374 QZipReader::FileInfo& QZipReader::FileInfo::operator=(const FileInfo &other)
376 filePath = other.filePath;
378 isFile = other.isFile;
379 isSymLink = other.isSymLink;
380 permissions = other.permissions;
383 lastModified = other.lastModified;
387 bool QZipReader::FileInfo::isValid() const
389 return isDir || isFile || isSymLink;
395 QZipPrivate(QIODevice *device, bool ownDev)
396 : device(device), ownDevice(ownDev), dirtyFileTree(true), start_of_directory(0)
406 void fillFileInfo(int index, QZipReader::FileInfo &fileInfo) const;
411 QList<FileHeader> fileHeaders;
413 uint start_of_directory;
416 void QZipPrivate::fillFileInfo(int index, QZipReader::FileInfo &fileInfo) const
418 FileHeader header = fileHeaders.at(index);
419 // if bit 11 is set, the filename and comment fields must be encoded using UTF-8
420 const bool inUtf8 = (readUShort(header.h.general_purpose_bits) & 0x0800) != 0;
421 fileInfo.filePath = inUtf8 ? QString::fromUtf8(header.file_name) : QString::fromLocal8Bit(header.file_name);
422 const quint32 mode = (qFromLittleEndian<quint32>(&header.h.external_file_attributes[0]) >> 16) & 0xFFFF;
423 fileInfo.isDir = S_ISDIR(mode);
424 fileInfo.isFile = S_ISREG(mode);
425 fileInfo.isSymLink = S_ISLNK(mode);
426 fileInfo.permissions = modeToPermissions(mode);
427 fileInfo.crc32 = readUInt(header.h.crc_32);
428 fileInfo.size = readUInt(header.h.uncompressed_size);
429 fileInfo.lastModified = readMSDosDate(header.h.last_mod_file);
432 class QZipReaderPrivate : public QZipPrivate
435 QZipReaderPrivate(QIODevice *device, bool ownDev)
436 : QZipPrivate(device, ownDev), status(QZipReader::NoError)
442 QZipReader::Status status;
445 class QZipWriterPrivate : public QZipPrivate
448 QZipWriterPrivate(QIODevice *device, bool ownDev)
449 : QZipPrivate(device, ownDev),
450 status(QZipWriter::NoError),
451 permissions(QFile::ReadOwner | QFile::WriteOwner),
452 compressionPolicy(QZipWriter::AlwaysCompress)
456 QZipWriter::Status status;
457 QFile::Permissions permissions;
458 QZipWriter::CompressionPolicy compressionPolicy;
460 enum EntryType { Directory, File, Symlink };
462 void addEntry(EntryType type, const QString &fileName, const QByteArray &contents);
465 LocalFileHeader CentralFileHeader::toLocalHeader() const
468 writeUInt(h.signature, 0x04034b50);
469 copyUShort(h.version_needed, version_needed);
470 copyUShort(h.general_purpose_bits, general_purpose_bits);
471 copyUShort(h.compression_method, compression_method);
472 copyUInt(h.last_mod_file, last_mod_file);
473 copyUInt(h.crc_32, crc_32);
474 copyUInt(h.compressed_size, compressed_size);
475 copyUInt(h.uncompressed_size, uncompressed_size);
476 copyUShort(h.file_name_length, file_name_length);
477 copyUShort(h.extra_field_length, extra_field_length);
481 void QZipReaderPrivate::scanFiles()
486 if (! (device->isOpen() || device->open(QIODevice::ReadOnly))) {
487 status = QZipReader::FileOpenError;
491 if ((device->openMode() & QIODevice::ReadOnly) == 0) { // only read the index from readable files.
492 status = QZipReader::FileReadError;
496 dirtyFileTree = false;
498 device->read((char *)tmp, 4);
499 if (readUInt(tmp) != 0x04034b50) {
500 qWarning() << "QZip: not a zip file!";
504 // find EndOfDirectory header
506 int start_of_directory = -1;
507 int num_dir_entries = 0;
509 while (start_of_directory == -1) {
510 const int pos = device->size() - int(sizeof(EndOfDirectory)) - i;
511 if (pos < 0 || i > 65535) {
512 qWarning() << "QZip: EndOfDirectory not found";
517 device->read((char *)&eod, sizeof(EndOfDirectory));
518 if (readUInt(eod.signature) == 0x06054b50)
524 start_of_directory = readUInt(eod.dir_start_offset);
525 num_dir_entries = readUShort(eod.num_dir_entries);
526 ZDEBUG("start_of_directory at %d, num_dir_entries=%d", start_of_directory, num_dir_entries);
527 int comment_length = readUShort(eod.comment_length);
528 if (comment_length != i)
529 qWarning() << "QZip: failed to parse zip file.";
530 comment = device->read(qMin(comment_length, i));
533 device->seek(start_of_directory);
534 for (i = 0; i < num_dir_entries; ++i) {
536 int read = device->read((char *) &header.h, sizeof(CentralFileHeader));
537 if (read < (int)sizeof(CentralFileHeader)) {
538 qWarning() << "QZip: Failed to read complete header, index may be incomplete";
541 if (readUInt(header.h.signature) != 0x02014b50) {
542 qWarning() << "QZip: invalid header signature, index may be incomplete";
546 int l = readUShort(header.h.file_name_length);
547 header.file_name = device->read(l);
548 if (header.file_name.length() != l) {
549 qWarning() << "QZip: Failed to read filename from zip index, index may be incomplete";
552 l = readUShort(header.h.extra_field_length);
553 header.extra_field = device->read(l);
554 if (header.extra_field.length() != l) {
555 qWarning() << "QZip: Failed to read extra field in zip file, skipping file, index may be incomplete";
558 l = readUShort(header.h.file_comment_length);
559 header.file_comment = device->read(l);
560 if (header.file_comment.length() != l) {
561 qWarning() << "QZip: Failed to read read file comment, index may be incomplete";
565 ZDEBUG("found file '%s'", header.file_name.data());
566 fileHeaders.append(header);
570 void QZipWriterPrivate::addEntry(EntryType type, const QString &fileName, const QByteArray &contents/*, QFile::Permissions permissions, QZip::Method m*/)
573 static const char *entryTypes[] = {
577 ZDEBUG() << "adding" << entryTypes[type] <<":" << fileName.toUtf8().data() << (type == 2 ? QByteArray(" -> " + contents).constData() : "");
580 if (! (device->isOpen() || device->open(QIODevice::WriteOnly))) {
581 status = QZipWriter::FileOpenError;
584 device->seek(start_of_directory);
586 // don't compress small files
587 QZipWriter::CompressionPolicy compression = compressionPolicy;
588 if (compressionPolicy == QZipWriter::AutoCompress) {
589 if (contents.length() < 64)
590 compression = QZipWriter::NeverCompress;
592 compression = QZipWriter::AlwaysCompress;
596 memset(&header.h, 0, sizeof(CentralFileHeader));
597 writeUInt(header.h.signature, 0x02014b50);
599 writeUShort(header.h.version_needed, 0x14);
600 writeUInt(header.h.uncompressed_size, contents.length());
601 writeMSDosDate(header.h.last_mod_file, QDateTime::currentDateTime());
602 QByteArray data = contents;
603 if (compression == QZipWriter::AlwaysCompress) {
604 writeUShort(header.h.compression_method, 8);
606 ulong len = contents.length();
607 // shamelessly copied form zlib
608 len += (len >> 12) + (len >> 14) + 11;
612 res = deflate((uchar*)data.data(), &len, (const uchar*)contents.constData(), contents.length());
619 qWarning("QZip: Z_MEM_ERROR: Not enough memory to compress file, skipping");
626 } while (res == Z_BUF_ERROR);
628 // TODO add a check if data.length() > contents.length(). Then try to store the original and revert the compression method to be uncompressed
629 writeUInt(header.h.compressed_size, data.length());
630 uint crc_32 = ::crc32(0, 0, 0);
631 crc_32 = ::crc32(crc_32, (const uchar *)contents.constData(), contents.length());
632 writeUInt(header.h.crc_32, crc_32);
634 // if bit 11 is set, the filename and comment fields must be encoded using UTF-8
635 ushort general_purpose_bits = 0x0800; // always use utf-8
636 writeUShort(header.h.general_purpose_bits, general_purpose_bits);
638 const bool inUtf8 = (general_purpose_bits & 0x0800) != 0;
639 header.file_name = inUtf8 ? fileName.toUtf8() : fileName.toLocal8Bit();
640 if (header.file_name.size() > 0xffff) {
641 qWarning("QZip: Filename too long, chopping it to 65535 characters");
642 header.file_name = header.file_name.left(0xffff);
644 writeUShort(header.h.file_name_length, header.file_name.length());
645 //h.extra_field_length[2];
647 writeUShort(header.h.version_made, 3 << 8);
648 //uchar internal_file_attributes[2];
649 //uchar external_file_attributes[4];
650 quint32 mode = permissionsToMode(permissions);
652 case File: mode |= S_IFREG; break;
653 case Directory: mode |= S_IFDIR; break;
654 case Symlink: mode |= S_IFLNK; break;
656 writeUInt(header.h.external_file_attributes, mode << 16);
657 writeUInt(header.h.offset_local_header, start_of_directory);
660 fileHeaders.append(header);
662 LocalFileHeader h = header.h.toLocalHeader();
663 device->write((const char *)&h, sizeof(LocalFileHeader));
664 device->write(header.file_name);
666 start_of_directory = device->pos();
667 dirtyFileTree = true;
670 ////////////////////////////// Reader
673 \class QZipReader::FileInfo
675 Represents one entry in the zip table of contents.
679 \variable FileInfo::filePath
680 The full filepath inside the archive.
684 \variable FileInfo::isDir
685 A boolean type indicating if the entry is a directory.
689 \variable FileInfo::isFile
690 A boolean type, if it is one this entry is a file.
694 \variable FileInfo::isSymLink
695 A boolean type, if it is one this entry is symbolic link.
699 \variable FileInfo::permissions
700 A list of flags for the permissions of this entry.
704 \variable FileInfo::crc32
705 The calculated checksum as a crc32 type.
709 \variable FileInfo::size
710 The total size of the unpacked content.
714 \variable FileInfo::d
724 \brief the QZipReader class provides a way to inspect the contents of a zip
725 archive and extract individual files from it.
727 QZipReader can be used to read a zip archive either from a file or from any
728 device. An in-memory QBuffer for instance. The reader can be used to read
729 which files are in the archive using fileInfoList() and entryInfoAt() but
730 also to extract individual files using fileData() or even to extract all
731 files in the archive using extractAll()
735 Create a new zip archive that operates on the \a fileName. The file will be
736 opened with the \a mode.
738 QZipReader::QZipReader(const QString &archive, QIODevice::OpenMode mode)
740 QScopedPointer<QFile> f(new QFile(archive));
742 QZipReader::Status status;
743 if (f->error() == QFile::NoError)
746 if (f->error() == QFile::ReadError)
747 status = FileReadError;
748 else if (f->error() == QFile::OpenError)
749 status = FileOpenError;
750 else if (f->error() == QFile::PermissionsError)
751 status = FilePermissionsError;
756 d = new QZipReaderPrivate(f.data(), /*ownDevice=*/true);
762 Create a new zip archive that operates on the archive found in \a device.
763 You have to open the device previous to calling the constructor and only a
764 device that is readable will be scanned for zip filecontent.
766 QZipReader::QZipReader(QIODevice *device)
767 : d(new QZipReaderPrivate(device, /*ownDevice=*/false))
775 QZipReader::~QZipReader()
782 Returns device used for reading zip archive.
784 QIODevice* QZipReader::device() const
790 Returns true if the user can read the file; otherwise returns false.
792 bool QZipReader::isReadable() const
794 return d->device->isReadable();
798 Returns true if the file exists; otherwise returns false.
800 bool QZipReader::exists() const
802 QFile *f = qobject_cast<QFile*> (d->device);
809 Returns the list of files the archive contains.
811 QList<QZipReader::FileInfo> QZipReader::fileInfoList() const
814 QList<QZipReader::FileInfo> files;
815 for (int i = 0; i < d->fileHeaders.size(); ++i) {
816 QZipReader::FileInfo fi;
817 d->fillFileInfo(i, fi);
825 Return the number of items in the zip archive.
827 int QZipReader::count() const
830 return d->fileHeaders.count();
834 Returns a FileInfo of an entry in the zipfile.
835 The \a index is the index into the directory listing of the zipfile.
836 Returns an invalid FileInfo if \a index is out of boundaries.
840 QZipReader::FileInfo QZipReader::entryInfoAt(int index) const
843 QZipReader::FileInfo fi;
844 if (index >= 0 && index < d->fileHeaders.count())
845 d->fillFileInfo(index, fi);
850 Fetch the file contents from the zip archive and return the uncompressed bytes.
852 QByteArray QZipReader::fileData(const QString &fileName) const
856 for (i = 0; i < d->fileHeaders.size(); ++i) {
857 if (QString::fromLocal8Bit(d->fileHeaders.at(i).file_name) == fileName)
860 if (i == d->fileHeaders.size())
863 FileHeader header = d->fileHeaders.at(i);
865 int compressed_size = readUInt(header.h.compressed_size);
866 int uncompressed_size = readUInt(header.h.uncompressed_size);
867 int start = readUInt(header.h.offset_local_header);
868 //qDebug("uncompressing file %d: local header at %d", i, start);
870 d->device->seek(start);
872 d->device->read((char *)&lh, sizeof(LocalFileHeader));
873 uint skip = readUShort(lh.file_name_length) + readUShort(lh.extra_field_length);
874 d->device->seek(d->device->pos() + skip);
876 int compression_method = readUShort(lh.compression_method);
877 //qDebug("file=%s: compressed_size=%d, uncompressed_size=%d", fileName.toLocal8Bit().data(), compressed_size, uncompressed_size);
879 //qDebug("file at %lld", d->device->pos());
880 QByteArray compressed = d->device->read(compressed_size);
881 if (compression_method == 0) {
883 compressed.truncate(uncompressed_size);
885 } else if (compression_method == 8) {
887 //qDebug("compressed=%d", compressed.size());
888 compressed.truncate(compressed_size);
890 ulong len = qMax(uncompressed_size, 1);
894 res = inflate((uchar*)baunzip.data(), &len,
895 (uchar*)compressed.constData(), compressed_size);
899 if ((int)len != baunzip.size())
903 qWarning("QZip: Z_MEM_ERROR: Not enough memory");
909 qWarning("QZip: Z_DATA_ERROR: Input data is corrupted");
912 } while (res == Z_BUF_ERROR);
915 qWarning() << "QZip: Unknown compression method";
920 Extracts the full contents of the zip file into \a destinationDir on
921 the local filesystem.
922 In case writing or linking a file fails, the extraction will be aborted.
924 bool QZipReader::extractAll(const QString &destinationDir) const
926 QDir baseDir(destinationDir);
928 // create directories first
929 QList<FileInfo> allFiles = fileInfoList();
930 foreach (FileInfo fi, allFiles) {
931 const QString absPath = destinationDir + QDir::separator() + fi.filePath;
933 if (!baseDir.mkpath(fi.filePath))
935 if (!QFile::setPermissions(absPath, fi.permissions))
941 foreach (FileInfo fi, allFiles) {
942 const QString absPath = destinationDir + QDir::separator() + fi.filePath;
944 QString destination = QFile::decodeName(fileData(fi.filePath));
945 if (destination.isEmpty())
947 QFileInfo linkFi(absPath);
948 if (!QFile::exists(linkFi.absolutePath()))
949 QDir::root().mkpath(linkFi.absolutePath());
950 if (!QFile::link(destination, absPath))
952 /* cannot change permission of links
953 if (!QFile::setPermissions(absPath, fi.permissions))
959 foreach (FileInfo fi, allFiles) {
960 const QString absPath = destinationDir + QDir::separator() + fi.filePath;
963 if (!f.open(QIODevice::WriteOnly))
965 f.write(fileData(fi.filePath));
966 f.setPermissions(fi.permissions);
975 \enum QZipReader::Status
977 The following status values are possible:
979 \value NoError No error occurred.
980 \value FileReadError An error occurred when reading from the file.
981 \value FileOpenError The file could not be opened.
982 \value FilePermissionsError The file could not be accessed.
983 \value FileError Another file error occurred.
987 Returns a status code indicating the first error that was met by QZipReader,
988 or QZipReader::NoError if no error occurred.
990 QZipReader::Status QZipReader::status() const
998 void QZipReader::close()
1003 ////////////////////////////// Writer
1010 \brief the QZipWriter class provides a way to create a new zip archive.
1012 QZipWriter can be used to create a zip archive containing any number of files
1013 and directories. The files in the archive will be compressed in a way that is
1014 compatible with common zip reader applications.
1019 Create a new zip archive that operates on the \a archive filename. The file will
1020 be opened with the \a mode.
1023 QZipWriter::QZipWriter(const QString &fileName, QIODevice::OpenMode mode)
1025 QScopedPointer<QFile> f(new QFile(fileName));
1027 QZipWriter::Status status;
1028 if (f->error() == QFile::NoError)
1029 status = QZipWriter::NoError;
1031 if (f->error() == QFile::WriteError)
1032 status = QZipWriter::FileWriteError;
1033 else if (f->error() == QFile::OpenError)
1034 status = QZipWriter::FileOpenError;
1035 else if (f->error() == QFile::PermissionsError)
1036 status = QZipWriter::FilePermissionsError;
1038 status = QZipWriter::FileError;
1041 d = new QZipWriterPrivate(f.data(), /*ownDevice=*/true);
1047 Create a new zip archive that operates on the archive found in \a device.
1048 You have to open the device previous to calling the constructor and
1049 only a device that is readable will be scanned for zip filecontent.
1051 QZipWriter::QZipWriter(QIODevice *device)
1052 : d(new QZipWriterPrivate(device, /*ownDevice=*/false))
1057 QZipWriter::~QZipWriter()
1064 Returns device used for writing zip archive.
1066 QIODevice* QZipWriter::device() const
1072 Returns true if the user can write to the archive; otherwise returns false.
1074 bool QZipWriter::isWritable() const
1076 return d->device->isWritable();
1080 Returns true if the file exists; otherwise returns false.
1082 bool QZipWriter::exists() const
1084 QFile *f = qobject_cast<QFile*> (d->device);
1091 \enum QZipWriter::Status
1093 The following status values are possible:
1095 \value NoError No error occurred.
1096 \value FileWriteError An error occurred when writing to the device.
1097 \value FileOpenError The file could not be opened.
1098 \value FilePermissionsError The file could not be accessed.
1099 \value FileError Another file error occurred.
1103 Returns a status code indicating the first error that was met by QZipWriter,
1104 or QZipWriter::NoError if no error occurred.
1106 QZipWriter::Status QZipWriter::status() const
1112 \enum QZipWriter::CompressionPolicy
1114 \value AlwaysCompress A file that is added is compressed.
1115 \value NeverCompress A file that is added will be stored without changes.
1116 \value AutoCompress A file that is added will be compressed only if that will give a smaller file.
1120 Sets the policy for compressing newly added files to the new \a policy.
1122 \note the default policy is AlwaysCompress
1124 \sa compressionPolicy()
1127 void QZipWriter::setCompressionPolicy(CompressionPolicy policy)
1129 d->compressionPolicy = policy;
1133 Returns the currently set compression policy.
1134 \sa setCompressionPolicy()
1137 QZipWriter::CompressionPolicy QZipWriter::compressionPolicy() const
1139 return d->compressionPolicy;
1143 Sets the permissions that will be used for newly added files.
1145 \note the default permissions are QFile::ReadOwner | QFile::WriteOwner.
1147 \sa creationPermissions()
1150 void QZipWriter::setCreationPermissions(QFile::Permissions permissions)
1152 d->permissions = permissions;
1156 Returns the currently set creation permissions.
1158 \sa setCreationPermissions()
1161 QFile::Permissions QZipWriter::creationPermissions() const
1163 return d->permissions;
1167 Add a file to the archive with \a data as the file contents.
1168 The file will be stored in the archive using the \a fileName which
1169 includes the full path in the archive.
1171 The new file will get the file permissions based on the current
1172 creationPermissions and it will be compressed using the zip compression
1173 based on the current compression policy.
1175 \sa setCreationPermissions()
1176 \sa setCompressionPolicy()
1178 void QZipWriter::addFile(const QString &fileName, const QByteArray &data)
1180 d->addEntry(QZipWriterPrivate::File, fileName, data);
1184 Add a file to the archive with \a device as the source of the contents.
1185 The contents returned from QIODevice::readAll() will be used as the
1187 The file will be stored in the archive using the \a fileName which
1188 includes the full path in the archive.
1190 void QZipWriter::addFile(const QString &fileName, QIODevice *device)
1193 QIODevice::OpenMode mode = device->openMode();
1194 bool opened = false;
1195 if ((mode & QIODevice::ReadOnly) == 0) {
1197 if (! device->open(QIODevice::ReadOnly)) {
1198 d->status = FileOpenError;
1202 d->addEntry(QZipWriterPrivate::File, fileName, device->readAll());
1208 Create a new directory in the archive with the specified \a dirName and
1211 void QZipWriter::addDirectory(const QString &dirName)
1213 QString name = dirName;
1214 // separator is mandatory
1215 if (!name.endsWith(QDir::separator()))
1216 name.append(QDir::separator());
1217 d->addEntry(QZipWriterPrivate::Directory, name, QByteArray());
1221 Create a new symbolic link in the archive with the specified \a dirName
1222 and the \a permissions;
1223 A symbolic link contains the destination (relative) path and name.
1225 void QZipWriter::addSymLink(const QString &fileName, const QString &destination)
1227 d->addEntry(QZipWriterPrivate::Symlink, fileName, QFile::encodeName(destination));
1231 Closes the zip file.
1233 void QZipWriter::close()
1235 if (!(d->device->openMode() & QIODevice::WriteOnly)) {
1240 //qDebug("QZip::close writing directory, %d entries", d->fileHeaders.size());
1241 d->device->seek(d->start_of_directory);
1242 // write new directory
1243 for (int i = 0; i < d->fileHeaders.size(); ++i) {
1244 const FileHeader &header = d->fileHeaders.at(i);
1245 d->device->write((const char *)&header.h, sizeof(CentralFileHeader));
1246 d->device->write(header.file_name);
1247 d->device->write(header.extra_field);
1248 d->device->write(header.file_comment);
1250 int dir_size = d->device->pos() - d->start_of_directory;
1251 // write end of directory
1253 memset(&eod, 0, sizeof(EndOfDirectory));
1254 writeUInt(eod.signature, 0x06054b50);
1255 //uchar this_disk[2];
1256 //uchar start_of_directory_disk[2];
1257 writeUShort(eod.num_dir_entries_this_disk, d->fileHeaders.size());
1258 writeUShort(eod.num_dir_entries, d->fileHeaders.size());
1259 writeUInt(eod.directory_size, dir_size);
1260 writeUInt(eod.dir_start_offset, d->start_of_directory);
1261 writeUShort(eod.comment_length, d->comment.length());
1263 d->device->write((const char *)&eod, sizeof(EndOfDirectory));
1264 d->device->write(d->comment);
1270 #endif // QT_NO_TEXTODFWRITER