summaryrefslogtreecommitdiffstats
path: root/src/plugins/messageservices/imap/imaptransport.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/messageservices/imap/imaptransport.cpp')
-rw-r--r--src/plugins/messageservices/imap/imaptransport.cpp341
1 files changed, 341 insertions, 0 deletions
diff --git a/src/plugins/messageservices/imap/imaptransport.cpp b/src/plugins/messageservices/imap/imaptransport.cpp
new file mode 100644
index 00000000..cb7bf6c5
--- /dev/null
+++ b/src/plugins/messageservices/imap/imaptransport.cpp
@@ -0,0 +1,341 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Messaging Framework.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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 LGPL_EXCEPTION.txt 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 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "imaptransport.h"
+#include <qmaillog.h>
+#include <qbuffer.h>
+
+#ifdef QT_QMF_HAVE_ZLIB
+#include <zlib.h>
+
+/* From RFC4978 The IMAP COMPRESS:
+ "When using the zlib library (see [RFC1951]), the functions
+ deflateInit2(), deflate(), inflateInit2(), and inflate() suffice to
+ implement this extension. The windowBits value must be in the range
+ -8 to -15, or else deflateInit2() uses the wrong format.
+ deflateParams() can be used to improve compression rate and resource
+ use. The Z_FULL_FLUSH argument to deflate() can be used to clear the
+ dictionary (the receiving peer does not need to do anything)."
+
+ Total zlib mem use is 464KB plus a 'few kilobytes' per connection that uses COMPRESS:
+ 384K for deflate, 48KB for 3x16KB buffers, 32KB plus a 'few' kilobytes for inflate.
+*/
+
+class Rfc1951Compressor
+{
+public:
+ Rfc1951Compressor(int chunkSize = 16384);
+ ~Rfc1951Compressor();
+
+ bool write(QDataStream *out, QByteArray *in);
+
+private:
+ int _chunkSize;
+ z_stream _zStream;
+ char *_buffer;
+};
+
+Rfc1951Compressor::Rfc1951Compressor(int chunkSize)
+{
+ _chunkSize = chunkSize;
+ _buffer = new char[chunkSize];
+
+ /* allocate deflate state */
+ _zStream.zalloc = Z_NULL;
+ _zStream.zfree = Z_NULL;
+ _zStream.opaque = Z_NULL;
+
+ Q_ASSERT(deflateInit2(&_zStream,
+ Z_DEFAULT_COMPRESSION,
+ Z_DEFLATED,
+ -MAX_WBITS, // MAX_WBITS == 15 (zconf.h) MEM128K
+ MAX_MEM_LEVEL, // MAX_MEM_LEVEL = 9 (zconf.h) MEM256K
+ Z_DEFAULT_STRATEGY) == Z_OK);
+}
+
+Rfc1951Compressor::~Rfc1951Compressor()
+{
+ delete _buffer;
+ deflateEnd(&_zStream);
+}
+
+bool Rfc1951Compressor::write(QDataStream *out, QByteArray *in)
+{
+ _zStream.next_in = reinterpret_cast<Bytef*>(in->data());
+ _zStream.avail_in = in->size();
+
+ do {
+ _zStream.next_out = reinterpret_cast<Bytef*>(_buffer);
+ _zStream.avail_out = _chunkSize;
+ int result = deflate(&_zStream, Z_SYNC_FLUSH);
+ if (result != Z_OK &&
+ result != Z_STREAM_END &&
+ result != Z_BUF_ERROR) {
+ return false;
+ }
+ out->writeRawData(_buffer, _chunkSize - _zStream.avail_out);
+ } while (!_zStream.avail_out);
+ return true;
+}
+
+class Rfc1951Decompressor
+{
+public:
+ Rfc1951Decompressor(int chunkSize = 16384);
+ ~Rfc1951Decompressor();
+
+ bool consume(QIODevice *in);
+ bool canReadLine() const;
+ QByteArray readLine();
+
+private:
+ int _chunkSize;
+ z_stream _zStream;
+ QByteArray _inBuffer;
+ char *_stagingBuffer;
+ QByteArray _output;
+};
+
+Rfc1951Decompressor::Rfc1951Decompressor(int chunkSize)
+{
+ _chunkSize = chunkSize;
+ _stagingBuffer = new char[_chunkSize];
+
+ /* allocate inflate state */
+ _zStream.zalloc = Z_NULL;
+ _zStream.zfree = Z_NULL;
+ _zStream.opaque = Z_NULL;
+ _zStream.avail_in = 0;
+ _zStream.next_in = Z_NULL;
+ Q_ASSERT(inflateInit2(&_zStream, -MAX_WBITS) == Z_OK);
+}
+
+Rfc1951Decompressor::~Rfc1951Decompressor()
+{
+ inflateEnd(&_zStream);
+ delete _stagingBuffer;
+}
+
+bool Rfc1951Decompressor::consume(QIODevice *in)
+{
+ while (in->bytesAvailable()) {
+ _inBuffer = in->read(_chunkSize);
+ _zStream.next_in = reinterpret_cast<Bytef*>(_inBuffer.data());
+ _zStream.avail_in = _inBuffer.size();
+ do {
+ _zStream.next_out = reinterpret_cast<Bytef *>(_stagingBuffer);
+ _zStream.avail_out = _chunkSize;
+ int result = inflate(&_zStream, Z_SYNC_FLUSH);
+ if (result != Z_OK &&
+ result != Z_STREAM_END &&
+ result != Z_BUF_ERROR) {
+ return false;
+ }
+ _output.append(_stagingBuffer, _chunkSize - _zStream.avail_out);
+ } while (_zStream.avail_out == 0);
+ }
+ return true;
+}
+
+bool Rfc1951Decompressor::canReadLine() const
+{
+ return _output.contains('\n');
+}
+
+QByteArray Rfc1951Decompressor::readLine()
+{
+ int eolPos = _output.indexOf('\n');
+ if (eolPos == -1) {
+ return QByteArray();
+ }
+
+ QByteArray result = _output.left(eolPos + 1);
+ _output = _output.mid(eolPos + 1);
+ return result;
+}
+#else
+class Rfc1951Compressor
+{
+public:
+ Rfc1951Compressor() {}
+ ~Rfc1951Compressor() {}
+
+ bool write(QDataStream *, QByteArray *) { return true;}
+};
+
+class Rfc1951Decompressor
+{
+public:
+ Rfc1951Decompressor() {}
+ ~Rfc1951Decompressor() {}
+
+ bool consume(QIODevice *) { return true; }
+ bool canReadLine() const { return true; }
+ QByteArray readLine() {}
+};
+#endif
+
+
+ImapTransport::ImapTransport(const char* name)
+ :QMailTransport(name),
+ _compress(false),
+ _decompressor(0),
+ _compressor(0)
+{
+ test();
+}
+
+ImapTransport::~ImapTransport()
+{
+ delete _decompressor;
+ delete _compressor;
+}
+
+bool ImapTransport::imapCanReadLine()
+{
+ if (!compress()) {
+ return canReadLine();
+ } else {
+ _decompressor->consume(&socket());
+ return _decompressor->canReadLine();
+ }
+}
+
+QByteArray ImapTransport::imapReadLine()
+{
+ if (!compress()) {
+ return readLine();
+ } else {
+ return _decompressor->readLine();
+ }
+}
+
+bool ImapTransport::imapWrite(QByteArray *in)
+{
+ if (!compress()) {
+ stream().writeRawData(in->constData(), in->length());
+ return true;
+ } else {
+ return _compressor->write(&stream(), in);
+ }
+}
+
+void ImapTransport::setCompress(bool comp)
+{
+ _compress = comp;
+ if (comp) {
+ if (!_compressor) {
+ _compressor = new Rfc1951Compressor();
+ }
+ if (!_decompressor) {
+ _decompressor = new Rfc1951Decompressor();
+ }
+ }
+}
+
+bool ImapTransport::compress()
+{
+ return _compress;
+}
+
+void ImapTransport::imapClose()
+{
+ close();
+ _compress = false;
+ delete _decompressor;
+ _decompressor = 0;
+ delete _compressor;
+ _compressor = 0;
+}
+
+#ifndef QT_NO_OPENSSL
+bool ImapTransport::ignoreCertificateErrors(const QList<QSslError>& errors)
+{
+ QMailTransport::ignoreCertificateErrors(errors);
+
+ // Because we can't ask the user (due to string freeze), let's default
+ // to ignoring these errors...
+ foreach (const QSslError& error, errors)
+ if (error.error() == QSslError::NoSslSupport)
+ return false;
+
+ return true;
+}
+#endif
+
+void ImapTransport::test()
+{
+#if 0
+ qMailLog(IMAP) << "Rfc1951Compressor and Rfc1951Decompressor functional testing running...";
+ // Mainly aiming to test for bounday conditions
+ // So make the compression/decompression buffers about the same size as the input/output
+ QByteArray data("This\n is some test data.\r\n The quick brown fox jumps over the lazy dog. 0123456789.\r\n");
+ for(int i = 10; i <= 100; ++ i) {
+ for(int j = 10; i <= 100; ++ i) {
+ for (int k = 10; k <= 100; ++k) {
+ Rfc1951Compressor compressor(i);
+ Rfc1951Decompressor decompressor(j);
+ QByteArray input(data.left(k));
+ input += "\r\n";
+ QByteArray compressed;
+ {
+ QDataStream stream(&compressed, QIODevice::WriteOnly);
+ compressor.write(&stream, &input);
+ }
+ {
+ QByteArray output;
+ QBuffer buffer(&compressed);
+ buffer.open(QIODevice::ReadOnly);
+ decompressor.consume(&buffer);
+ while (decompressor.canReadLine()) {
+ output += decompressor.readLine();
+ }
+ if (input != output) {
+ qMailLog(IMAP) << "Test failure: input" << input.toHex() << "output" << output.toHex();
+ Q_ASSERT(input == output);
+ }
+ }
+ }
+ }
+ }
+ qMailLog(IMAP) << "Rfc1951Compressor and Rfc1951Decompressor functional testing completed OK";
+#endif
+}