Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/keepassxreboot/keepassxc.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFelix Geyer <debfx@fobos.de>2010-09-24 00:27:59 +0400
committerFelix Geyer <debfx@fobos.de>2010-09-24 00:27:59 +0400
commitb8dfb9cc4d5133e0f09cd7533d15a4f1c19a40f2 (patch)
treee97d0ab0c0dd06118dd8a739730e39bbb5a1bc88 /src
parentf0e711ac2330efce495155edc4ec163b85c99b4f (diff)
Add support for gzip compressed databases.
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt3
-rw-r--r--src/format/KeePass2Reader.cpp20
-rw-r--r--src/format/KeePass2Reader.h3
-rw-r--r--src/streams/QtIOCompressor1
-rw-r--r--src/streams/qtiocompressor.cpp624
-rw-r--r--src/streams/qtiocompressor.h94
6 files changed, 740 insertions, 5 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 771b8b5ce..367696c3e 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -43,6 +43,7 @@ set(keepassx_SOURCES
keys/PasswordKey.cpp
streams/HashedBlockStream.cpp
streams/LayeredStream.cpp
+ streams/qtiocompressor.cpp
streams/SymmetricCipherStream.cpp
)
@@ -55,4 +56,4 @@ qt4_wrap_ui(keepassx_SOURCES ${keepassx_FORMS})
automoc4_add_library( keepassx_core STATIC ${keepassx_SOURCES} )
automoc4_add_executable( ${PROGNAME} WIN32 MACOSX_BUNDLE main.cpp )
-target_link_libraries( ${PROGNAME} keepassx_core ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY} ${LIBGCRYPT_LIBS} )
+target_link_libraries( ${PROGNAME} keepassx_core ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY} ${LIBGCRYPT_LIBS} ${ZLIB_LIBRARIES} )
diff --git a/src/format/KeePass2Reader.cpp b/src/format/KeePass2Reader.cpp
index d38b433e7..13df6b97d 100644
--- a/src/format/KeePass2Reader.cpp
+++ b/src/format/KeePass2Reader.cpp
@@ -21,10 +21,10 @@
#include <QtCore/QFile>
#include <QtCore/QIODevice>
-#include "KeePass2.h"
#include "KeePass2XmlReader.h"
#include "crypto/CryptoHash.h"
#include "streams/HashedBlockStream.h"
+#include "streams/QtIOCompressor"
#include "streams/SymmetricCipherStream.h"
const QSysInfo::Endian KeePass2Reader::BYTEORDER = QSysInfo::LittleEndian;
@@ -80,8 +80,22 @@ Database* KeePass2Reader::readDatabase(QIODevice* device, const CompositeKey& ke
HashedBlockStream hashedStream(&cipherStream);
hashedStream.open(QIODevice::ReadOnly);
+ QIODevice* xmlDevice;
+ QScopedPointer<QtIOCompressor> ioCompressor;
+
+ if (m_compression == KeePass2::CompressionNone) {
+ xmlDevice = &hashedStream;
+ }
+ else {
+ ioCompressor.reset(new QtIOCompressor(&hashedStream));
+ ioCompressor->setStreamFormat(QtIOCompressor::GzipFormat);
+ ioCompressor->open(QIODevice::ReadOnly);
+ xmlDevice = ioCompressor.data();
+ }
+
KeePass2XmlReader xmlReader;
- Database* db = xmlReader.readDatabase(&hashedStream);
+ Database* db = xmlReader.readDatabase(xmlDevice);
+ // TODO forward error messages from xmlReader
return db;
}
@@ -208,7 +222,7 @@ void KeePass2Reader::setCompressionFlags(const QByteArray& data)
raiseError("");
}
else {
- m_compression = id;
+ m_compression = static_cast<KeePass2::CompressionAlgorithm>(id);
}
}
}
diff --git a/src/format/KeePass2Reader.h b/src/format/KeePass2Reader.h
index 396348036..e839fcaa8 100644
--- a/src/format/KeePass2Reader.h
+++ b/src/format/KeePass2Reader.h
@@ -23,6 +23,7 @@
#include "core/Endian.h"
#include "core/Uuid.h"
#include "keys/CompositeKey.h"
+#include "format/KeePass2.h"
class Database;
@@ -59,7 +60,7 @@ private:
bool m_headerEnd;
Uuid m_cipher;
- int m_compression;
+ KeePass2::CompressionAlgorithm m_compression;
QByteArray m_masterSeed;
QByteArray m_transformSeed;
quint64 m_transformRounds;
diff --git a/src/streams/QtIOCompressor b/src/streams/QtIOCompressor
new file mode 100644
index 000000000..350ed4b02
--- /dev/null
+++ b/src/streams/QtIOCompressor
@@ -0,0 +1 @@
+#include "qtiocompressor.h"
diff --git a/src/streams/qtiocompressor.cpp b/src/streams/qtiocompressor.cpp
new file mode 100644
index 000000000..0de5dff4d
--- /dev/null
+++ b/src/streams/qtiocompressor.cpp
@@ -0,0 +1,624 @@
+/****************************************************************************
+**
+** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of a Qt Solutions component.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Solutions Commercial License Agreement provided
+** with the Software or, alternatively, in accordance with the terms
+** contained in a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.1, included in the file LICENSE.NOKIA-LGPL-EXCEPTION
+** in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL-3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** Please note Third Party Software included with Qt Solutions may impose
+** additional restrictions and it is the user's responsibility to ensure
+** that they have met the licensing requirements of the GPL, LGPL, or Qt
+** Solutions Commercial license and the relevant license of the Third
+** Party Software they are using.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact Nokia at qt-info@nokia.com.
+**
+****************************************************************************/
+
+#include "qtiocompressor.h"
+#include "zlib.h"
+#include <QtCore/QDebug>
+
+typedef Bytef ZlibByte;
+typedef uInt ZlibSize;
+
+class QtIOCompressorPrivate {
+ QtIOCompressor *q_ptr;
+ Q_DECLARE_PUBLIC(QtIOCompressor)
+public:
+ enum State {
+ // Read state
+ NotReadFirstByte,
+ InStream,
+ EndOfStream,
+ // Write state
+ NoBytesWritten,
+ BytesWritten,
+ // Common
+ Closed,
+ Error
+ };
+
+ QtIOCompressorPrivate(QtIOCompressor *q_ptr, QIODevice *device, int compressionLevel, int bufferSize);
+ ~QtIOCompressorPrivate();
+ void flushZlib(int flushMode);
+ bool writeBytes(ZlibByte *buffer, ZlibSize outputSize);
+ void setZlibError(const QString &erroMessage, int zlibErrorCode);
+
+ QIODevice *device;
+ bool manageDevice;
+ z_stream zlibStream;
+ const int compressionLevel;
+ const ZlibSize bufferSize;
+ ZlibByte *buffer;
+ State state;
+ QtIOCompressor::StreamFormat streamFormat;
+};
+
+/*!
+ \internal
+*/
+QtIOCompressorPrivate::QtIOCompressorPrivate(QtIOCompressor *q_ptr, QIODevice *device, int compressionLevel, int bufferSize)
+:q_ptr(q_ptr)
+,device(device)
+,compressionLevel(compressionLevel)
+,bufferSize(bufferSize)
+,buffer(new ZlibByte[bufferSize])
+,state(Closed)
+,streamFormat(QtIOCompressor::ZlibFormat)
+{
+ // Use default zlib memory management.
+ zlibStream.zalloc = Z_NULL;
+ zlibStream.zfree = Z_NULL;
+ zlibStream.opaque = Z_NULL;
+}
+
+/*!
+ \internal
+*/
+QtIOCompressorPrivate::~QtIOCompressorPrivate()
+{
+ delete[] buffer;
+}
+
+/*!
+ \internal
+ Flushes the zlib stream.
+*/
+void QtIOCompressorPrivate::flushZlib(int flushMode)
+{
+ // No input.
+ zlibStream.next_in = 0;
+ zlibStream.avail_in = 0;
+ int status;
+ do {
+ zlibStream.next_out = buffer;
+ zlibStream.avail_out = bufferSize;
+ status = deflate(&zlibStream, flushMode);
+ if (status != Z_OK && status != Z_STREAM_END) {
+ state = QtIOCompressorPrivate::Error;
+ setZlibError(QT_TRANSLATE_NOOP("QtIOCompressor", "Internal zlib error when compressing: "), status);
+ return;
+ }
+
+ ZlibSize outputSize = bufferSize - zlibStream.avail_out;
+
+ // Try to write data from the buffer to to the underlying device, return on failure.
+ if (!writeBytes(buffer, outputSize))
+ return;
+
+ // If the mode is Z_FNISH we must loop until we get Z_STREAM_END,
+ // else we loop as long as zlib is able to fill the output buffer.
+ } while ((flushMode == Z_FINISH && status != Z_STREAM_END) || (flushMode != Z_FINISH && zlibStream.avail_out == 0));
+
+ if (flushMode == Z_FINISH)
+ Q_ASSERT(status == Z_STREAM_END);
+ else
+ Q_ASSERT(status == Z_OK);
+}
+
+/*!
+ \internal
+ Writes outputSize bytes from buffer to the inderlying device.
+*/
+bool QtIOCompressorPrivate::writeBytes(ZlibByte *buffer, ZlibSize outputSize)
+{
+ Q_Q(QtIOCompressor);
+ ZlibSize totalBytesWritten = 0;
+ // Loop until all bytes are written to the underlying device.
+ do {
+ const qint64 bytesWritten = device->write(reinterpret_cast<char *>(buffer), outputSize);
+ if (bytesWritten == -1) {
+ q->setErrorString(QT_TRANSLATE_NOOP("QtIOCompressor", "Error writing to underlying device: ") + device->errorString());
+ return false;
+ }
+ totalBytesWritten += bytesWritten;
+ } while (totalBytesWritten != outputSize);
+
+ // put up a flag so that the device will be flushed on close.
+ state = BytesWritten;
+ return true;
+}
+
+/*!
+ \internal
+ Sets the error string to errorMessage + zlib error string for zlibErrorCode
+*/
+void QtIOCompressorPrivate::setZlibError(const QString &errorMessage, int zlibErrorCode)
+{
+ Q_Q(QtIOCompressor);
+ // Watch out, zlibErrorString may be null.
+ const char * const zlibErrorString = zError(zlibErrorCode);
+ QString errorString;
+ if (zlibErrorString)
+ errorString = errorMessage + zlibErrorString;
+ else
+ errorString = errorMessage + " Unknown error, code " + QString::number(zlibErrorCode);
+
+ q->setErrorString(errorString);
+}
+
+/*! \class QtIOCompressor
+ \brief The QtIOCompressor class is a QIODevice that compresses data streams.
+
+ A QtIOCompressor object is constructed with a pointer to an
+ underlying QIODevice. Data written to the QtIOCompressor object
+ will be compressed before it is written to the underlying
+ QIODevice. Similary, if you read from the QtIOCompressor object,
+ the data will be read from the underlying device and then
+ decompressed.
+
+ QtIOCompressor is a sequential device, which means that it does
+ not support seeks or random access. Internally, QtIOCompressor
+ uses the zlib library to compress and uncompress data.
+
+ Usage examples:
+ Writing compressed data to a file:
+ \code
+ QFile file("foo");
+ QtIOCompressor compressor(&file);
+ compressor.open(QIODevice::WriteOnly);
+ compressor.write(QByteArray() << "The quick brown fox");
+ compressor.close();
+ \endcode
+
+ Reading compressed data from a file:
+ \code
+ QFile file("foo");
+ QtIOCompressor compressor(&file);
+ compressor.open(QIODevice::ReadOnly);
+ const QByteArray text = compressor.readAll();
+ compressor.close();
+ \endcode
+
+ QtIOCompressor can also read and write compressed data in
+ different compressed formats, ref. StreamFormat. Use
+ setStreamFormat() before open() to select format.
+*/
+
+/*!
+ \enum QtIOCompressor::StreamFormat
+ This enum specifies which stream format to use.
+
+ \value ZlibFormat: This is the default and has the smallest overhead.
+
+ \value GzipFormat: This format is compatible with the gzip file
+ format, but has more overhead than ZlibFormat. Note: requires zlib
+ version 1.2.x or higher at runtime.
+
+ \value RawZipFormat: This is compatible with the most common
+ compression method of the data blocks contained in ZIP
+ archives. Note: ZIP file headers are not read or generated, so
+ setting this format, by itself, does not let QtIOCompressor read
+ or write ZIP files. Ref. the ziplist example program.
+
+ \sa setStreamFormat()
+*/
+
+/*!
+ Constructs a QtIOCompressor using the given \a device as the underlying device.
+
+ The allowed value range for \a compressionLevel is 0 to 9, where 0 means no compression
+ and 9 means maximum compression. The default value is 6.
+
+ \a bufferSize specifies the size of the internal buffer used when reading from and writing to the
+ underlying device. The default value is 65KB. Using a larger value allows for faster compression and
+ deompression at the expense of memory usage.
+*/
+QtIOCompressor::QtIOCompressor(QIODevice *device, int compressionLevel, int bufferSize)
+:d_ptr(new QtIOCompressorPrivate(this, device, compressionLevel, bufferSize))
+{}
+
+/*!
+ Destroys the QtIOCompressor, closing it if neccesary.
+*/
+QtIOCompressor::~QtIOCompressor()
+{
+ Q_D(QtIOCompressor);
+ close();
+ delete d;
+}
+
+/*!
+ Sets the format on the compressed stream to \a format.
+
+ \sa QtIOCompressor::StreamFormat
+*/
+void QtIOCompressor::setStreamFormat(StreamFormat format)
+{
+ Q_D(QtIOCompressor);
+
+ // Print a waning if the compile-time version of zlib does not support gzip.
+ if (format == GzipFormat && checkGzipSupport(ZLIB_VERSION) == false)
+ qWarning("QtIOCompressor::setStreamFormat: zlib 1.2.x or higher is "
+ "required to use the gzip format. Current version is: %s",
+ ZLIB_VERSION);
+
+ d->streamFormat = format;
+}
+
+/*!
+ Returns the format set on the compressed stream.
+ \sa QtIOCompressor::StreamFormat
+*/
+QtIOCompressor::StreamFormat QtIOCompressor::streamFormat() const
+{
+ Q_D(const QtIOCompressor);
+ return d->streamFormat;
+}
+
+/*!
+ Returns true if the zlib library in use supports the gzip format, false otherwise.
+*/
+bool QtIOCompressor::isGzipSupported()
+{
+ return checkGzipSupport(zlibVersion());
+}
+
+/*!
+ \reimp
+*/
+bool QtIOCompressor::isSequential() const
+{
+ return true;
+}
+
+/*!
+ Opens the QtIOCompressor in \a mode. Only ReadOnly and WriteOnly is supported.
+ This functon will return false if you try to open in other modes.
+
+ If the underlying device is not opened, this function will open it in a suitable mode. If this happens
+ the device will also be closed when close() is called.
+
+ If the underlying device is already opened, its openmode must be compatable with \a mode.
+
+ Returns true on success, false on error.
+
+ \sa close()
+*/
+bool QtIOCompressor::open(OpenMode mode)
+{
+ Q_D(QtIOCompressor);
+ if (isOpen()) {
+ qWarning("QtIOCompressor::open: device already open");
+ return false;
+ }
+
+ // Check for correct mode: ReadOnly xor WriteOnly
+ const bool read = (mode & ReadOnly);
+ const bool write = (mode & WriteOnly);
+ const bool both = (read && write);
+ const bool neither = !(read || write);
+ if (both || neither) {
+ qWarning("QtIOCompressor::open: QtIOCompressor can only be opened in the ReadOnly or WriteOnly modes");
+ return false;
+ }
+
+ // If the underlying device is open, check that is it opened in a compatible mode.
+ if (d->device->isOpen()) {
+ d->manageDevice = false;
+ const OpenMode deviceMode = d->device->openMode();
+ if (read && !(deviceMode & ReadOnly)) {
+ qWarning("QtIOCompressor::open: underlying device must be open in one of the ReadOnly or WriteOnly modes");
+ return false;
+ } else if (write && !(deviceMode & WriteOnly)) {
+ qWarning("QtIOCompressor::open: underlying device must be open in one of the ReadOnly or WriteOnly modes");
+ return false;
+ }
+
+ // If the underlying device is closed, open it.
+ } else {
+ d->manageDevice = true;
+ if (d->device->open(mode) == false) {
+ setErrorString(QT_TRANSLATE_NOOP("QtIOCompressor", "Error opening underlying device: ") + d->device->errorString());
+ return false;
+ }
+ }
+
+ // Initialize zlib for deflating or inflating.
+
+ // The second argument to inflate/deflateInit2 is the windowBits parameter,
+ // which also controls what kind of compression stream headers to use.
+ // The default value for this is 15. Passing a value greater than 15
+ // enables gzip headers and then subtracts 16 form the windowBits value.
+ // (So passing 31 gives gzip headers and 15 windowBits). Passing a negative
+ // value selects no headers hand then negates the windowBits argument.
+ int windowBits;
+ switch (d->streamFormat) {
+ case QtIOCompressor::GzipFormat:
+ windowBits = 31;
+ break;
+ case QtIOCompressor::RawZipFormat:
+ windowBits = -15;
+ break;
+ default:
+ windowBits = 15;
+ }
+
+ int status;
+ if (read) {
+ d->state = QtIOCompressorPrivate::NotReadFirstByte;
+ d->zlibStream.avail_in = 0;
+ d->zlibStream.next_in = 0;
+ if (d->streamFormat == QtIOCompressor::ZlibFormat) {
+ status = inflateInit(&d->zlibStream);
+ } else {
+ if (checkGzipSupport(zlibVersion()) == false) {
+ setErrorString(QT_TRANSLATE_NOOP("QtIOCompressor::open", "The gzip format not supported in this version of zlib."));
+ return false;
+ }
+
+ status = inflateInit2(&d->zlibStream, windowBits);
+ }
+ } else {
+ d->state = QtIOCompressorPrivate::NoBytesWritten;
+ if (d->streamFormat == QtIOCompressor::ZlibFormat)
+ status = deflateInit(&d->zlibStream, d->compressionLevel);
+ else
+ status = deflateInit2(&d->zlibStream, d->compressionLevel, Z_DEFLATED, windowBits, 8, Z_DEFAULT_STRATEGY);
+ }
+
+ // Handle error.
+ if (status != Z_OK) {
+ d->setZlibError(QT_TRANSLATE_NOOP("QtIOCompressor::open", "Internal zlib error: "), status);
+ return false;
+ }
+ return QIODevice::open(mode);
+}
+
+/*!
+ Closes the QtIOCompressor, and also the underlying device if it was opened by QtIOCompressor.
+ \sa open()
+*/
+void QtIOCompressor::close()
+{
+ Q_D(QtIOCompressor);
+ if (isOpen() == false)
+ return;
+
+ // Flush and close the zlib stream.
+ if (openMode() & ReadOnly) {
+ d->state = QtIOCompressorPrivate::NotReadFirstByte;
+ inflateEnd(&d->zlibStream);
+ } else {
+ if (d->state == QtIOCompressorPrivate::BytesWritten) { // Only flush if we have written anything.
+ d->state = QtIOCompressorPrivate::NoBytesWritten;
+ d->flushZlib(Z_FINISH);
+ }
+ deflateEnd(&d->zlibStream);
+ }
+
+ // Close the underlying device if we are managing it.
+ if (d->manageDevice)
+ d->device->close();
+
+ QIODevice::close();
+}
+
+/*!
+ Flushes the internal buffer.
+
+ Each time you call flush, all data written to the QtIOCompressor is compressed and written to the
+ underlying device. Calling this function can reduce the compression ratio. The underlying device
+ is not flushed.
+
+ Calling this function when QtIOCompressor is in ReadOnly mode has no effect.
+*/
+void QtIOCompressor::flush()
+{
+ Q_D(QtIOCompressor);
+ if (isOpen() == false || openMode() & ReadOnly)
+ return;
+
+ d->flushZlib(Z_SYNC_FLUSH);
+}
+
+/*!
+ Returns 1 if there might be data available for reading, or 0 if there is no data available.
+
+ There is unfortunately no way of knowing how much data there is available when dealing with compressed streams.
+
+ Also, since the remaining compressed data might be a part of the meta-data that ends the compressed stream (and
+ therefore will yield no uncompressed data), you cannot assume that a read after getting a 1 from this function will return data.
+*/
+qint64 QtIOCompressor::bytesAvailable() const
+{
+ Q_D(const QtIOCompressor);
+ if ((openMode() & ReadOnly) == false)
+ return 0;
+
+ int numBytes = 0;
+
+ switch (d->state) {
+ case QtIOCompressorPrivate::NotReadFirstByte:
+ numBytes = d->device->bytesAvailable();
+ break;
+ case QtIOCompressorPrivate::InStream:
+ numBytes = 1;
+ break;
+ case QtIOCompressorPrivate::EndOfStream:
+ case QtIOCompressorPrivate::Error:
+ default:
+ numBytes = 0;
+ break;
+ };
+
+ numBytes += QIODevice::bytesAvailable();
+
+ if (numBytes > 0)
+ return 1;
+ else
+ return 0;
+}
+
+/*!
+ \internal
+ Reads and decompresses data from the underlying device.
+*/
+qint64 QtIOCompressor::readData(char *data, qint64 maxSize)
+{
+ Q_D(QtIOCompressor);
+
+ if (d->state == QtIOCompressorPrivate::EndOfStream)
+ return 0;
+
+ if (d->state == QtIOCompressorPrivate::Error)
+ return -1;
+
+ // We are ging to try to fill the data buffer
+ d->zlibStream.next_out = reinterpret_cast<ZlibByte *>(data);
+ d->zlibStream.avail_out = maxSize;
+
+ int status;
+ do {
+ // Read data if if the input buffer is empty. There could be data in the buffer
+ // from a previous readData call.
+ if (d->zlibStream.avail_in == 0) {
+ qint64 bytesAvalible = d->device->read(reinterpret_cast<char *>(d->buffer), d->bufferSize);
+ d->zlibStream.next_in = d->buffer;
+ d->zlibStream.avail_in = bytesAvalible;
+
+ if (bytesAvalible == -1) {
+ d->state = QtIOCompressorPrivate::Error;
+ setErrorString(QT_TRANSLATE_NOOP("QtIOCompressor", "Error reading data from underlying device: ") + d->device->errorString());
+ return -1;
+ }
+
+ if (d->state != QtIOCompressorPrivate::InStream) {
+ // If we are not in a stream and get 0 bytes, we are probably trying to read from an empty device.
+ if(bytesAvalible == 0)
+ return 0;
+ else if (bytesAvalible > 0)
+ d->state = QtIOCompressorPrivate::InStream;
+ }
+ }
+
+ // Decompress.
+ status = inflate(&d->zlibStream, Z_SYNC_FLUSH);
+ switch (status) {
+ case Z_NEED_DICT:
+ case Z_DATA_ERROR:
+ case Z_MEM_ERROR:
+ d->state = QtIOCompressorPrivate::Error;
+ d->setZlibError(QT_TRANSLATE_NOOP("QtIOCompressor", "Internal zlib error when decompressing: "), status);
+ return -1;
+ case Z_BUF_ERROR: // No more input and zlib can not privide more output - Not an error, we can try to read again when we have more input.
+ return 0;
+ break;
+ }
+ // Loop util data buffer is full or we reach the end of the input stream.
+ } while (d->zlibStream.avail_out != 0 && status != Z_STREAM_END);
+
+ if (status == Z_STREAM_END) {
+ d->state = QtIOCompressorPrivate::EndOfStream;
+
+ // Unget any data left in the read buffer.
+ for (int i = d->zlibStream.avail_in; i >= 0; --i)
+ d->device->ungetChar(*reinterpret_cast<char *>(d->zlibStream.next_in + i));
+ }
+
+ const ZlibSize outputSize = maxSize - d->zlibStream.avail_out;
+ return outputSize;
+}
+
+
+/*!
+ \internal
+ Compresses and writes data to the underlying device.
+*/
+qint64 QtIOCompressor::writeData(const char *data, qint64 maxSize)
+{
+ if (maxSize < 1)
+ return 0;
+ Q_D(QtIOCompressor);
+ d->zlibStream.next_in = reinterpret_cast<ZlibByte *>(const_cast<char *>(data));
+ d->zlibStream.avail_in = maxSize;
+
+ if (d->state == QtIOCompressorPrivate::Error)
+ return -1;
+
+ do {
+ d->zlibStream.next_out = d->buffer;
+ d->zlibStream.avail_out = d->bufferSize;
+ const int status = deflate(&d->zlibStream, Z_NO_FLUSH);
+ if (status != Z_OK) {
+ d->state = QtIOCompressorPrivate::Error;
+ d->setZlibError(QT_TRANSLATE_NOOP("QtIOCompressor", "Internal zlib error when compressing: "), status);
+ return -1;
+ }
+
+ ZlibSize outputSize = d->bufferSize - d->zlibStream.avail_out;
+
+ // Try to write data from the buffer to to the underlying device, return -1 on failure.
+ if (d->writeBytes(d->buffer, outputSize) == false)
+ return -1;
+
+ } while (d->zlibStream.avail_out == 0); // run until output is not full.
+ Q_ASSERT(d->zlibStream.avail_in == 0);
+
+ return maxSize;
+}
+
+/*
+ \internal
+ Checks if the run-time zlib version is 1.2.x or higher.
+*/
+bool QtIOCompressor::checkGzipSupport(const char * const versionString)
+{
+ if (strlen(versionString) < 3)
+ return false;
+
+ if (versionString[0] == '0' || (versionString[0] == '1' && (versionString[2] == '0' || versionString[2] == '1' )))
+ return false;
+
+ return true;
+}
diff --git a/src/streams/qtiocompressor.h b/src/streams/qtiocompressor.h
new file mode 100644
index 000000000..72b21cbc3
--- /dev/null
+++ b/src/streams/qtiocompressor.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of a Qt Solutions component.
+**
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Solutions Commercial License Agreement provided
+** with the Software or, alternatively, in accordance with the terms
+** contained in a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.1, included in the file LICENSE.NOKIA-LGPL-EXCEPTION
+** in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL-3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** Please note Third Party Software included with Qt Solutions may impose
+** additional restrictions and it is the user's responsibility to ensure
+** that they have met the licensing requirements of the GPL, LGPL, or Qt
+** Solutions Commercial license and the relevant license of the Third
+** Party Software they are using.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact Nokia at qt-info@nokia.com.
+**
+****************************************************************************/
+
+#ifndef QTIOCOMPRESSOR_H
+#define QTIOCOMPRESSOR_H
+
+#include <QtCore/QIODevice>
+
+#if defined(Q_WS_WIN)
+# if !defined(QT_QTIOCOMPRESSOR_EXPORT) && !defined(QT_QTIOCOMPRESSOR_IMPORT)
+# define QT_QTIOCOMPRESSOR_EXPORT
+# elif defined(QT_QTIOCOMPRESSOR_IMPORT)
+# if defined(QT_QTIOCOMPRESSOR_EXPORT)
+# undef QT_QTIOCOMPRESSOR_EXPORT
+# endif
+# define QT_QTIOCOMPRESSOR_EXPORT __declspec(dllimport)
+# elif defined(QT_QTIOCOMPRESSOR_EXPORT)
+# undef QT_QTIOCOMPRESSOR_EXPORT
+# define QT_QTIOCOMPRESSOR_EXPORT __declspec(dllexport)
+# endif
+#else
+# define QT_QTIOCOMPRESSOR_EXPORT
+#endif
+
+class QtIOCompressorPrivate;
+class QT_QTIOCOMPRESSOR_EXPORT QtIOCompressor : public QIODevice
+{
+Q_OBJECT
+public:
+enum StreamFormat { ZlibFormat, GzipFormat, RawZipFormat };
+ QtIOCompressor(QIODevice *device, int compressionLevel = 6, int bufferSize = 65500);
+ ~QtIOCompressor();
+ void setStreamFormat(StreamFormat format);
+ StreamFormat streamFormat() const;
+ static bool isGzipSupported();
+ bool isSequential() const;
+ bool open(OpenMode mode);
+ void close();
+ void flush();
+ qint64 bytesAvailable() const;
+protected:
+ qint64 readData(char * data, qint64 maxSize);
+ qint64 writeData(const char * data, qint64 maxSize);
+private:
+ static bool checkGzipSupport(const char * const versionString);
+ QtIOCompressorPrivate *d_ptr;
+ Q_DECLARE_PRIVATE(QtIOCompressor)
+ Q_DISABLE_COPY(QtIOCompressor)
+};
+
+#endif