20 #include "sessionthread_p.h"
22 #include <QtCore/QDebug>
23 #include <QtCore/QTimer>
27 #include "imapstreamparser.h"
28 #include "message_p.h"
31 using namespace KIMAP;
33 Q_DECLARE_METATYPE(KTcpSocket::Error)
34 Q_DECLARE_METATYPE(KSslErrorUiData)
35 static const
int _kimap_socketErrorTypeId = qRegisterMetaType<KTcpSocket::Error>();
36 static const
int _kimap_sslErrorUiData = qRegisterMetaType<KSslErrorUiData>();
38 SessionThread::SessionThread( const QString &hostName, quint16 port, Session *parent )
39 : QThread(), m_hostName( hostName ), m_port( port ),
40 m_session( parent ), m_socket( 0 ), m_stream( 0 ), m_encryptedMode( false ),
41 triedSslVersions( 0 ), doSslFallback( false )
49 SessionThread::~SessionThread()
52 QMetaObject::invokeMethod(
this,
"quit" );
53 if ( !wait( 10 * 1000 ) ) {
54 kWarning() <<
"Session thread refuses to die, killing harder...";
61 void SessionThread::sendData(
const QByteArray &payload )
63 QMutexLocker locker(&m_mutex);
65 m_dataQueue.enqueue( payload );
66 QTimer::singleShot( 0,
this, SLOT(writeDataQueue()) );
69 void SessionThread::writeDataQueue()
71 QMutexLocker locker(&m_mutex);
73 while ( !m_dataQueue.isEmpty() ) {
74 m_socket->write( m_dataQueue.dequeue() );
78 void SessionThread::readMessage()
80 QMutexLocker locker(&m_mutex);
82 if ( m_stream->availableDataSize()==0 ) {
87 QList<Message::Part> *payload = &message.content;
90 while ( !m_stream->atCommandEnd() ) {
91 if ( m_stream->hasString() ) {
92 QByteArray
string = m_stream->readString();
93 if (
string ==
"NIL" ) {
94 *payload << Message::Part( QList<QByteArray>() );
96 *payload << Message::Part(
string);
98 }
else if ( m_stream->hasList() ) {
99 *payload << Message::Part(m_stream->readParenthesizedList());
100 }
else if ( m_stream->hasResponseCode() ) {
101 payload = &message.responseCode;
102 }
else if ( m_stream->atResponseCodeEnd() ) {
103 payload = &message.content;
104 }
else if ( m_stream->hasLiteral() ) {
106 while ( !m_stream->atLiteralEnd() ) {
107 literal+= m_stream->readLiteralPart();
109 *payload << Message::Part(literal);
113 qWarning(
"Inconsistent state, probably due to some packet loss" );
119 emit responseReceived(message);
121 }
catch (KIMAP::ImapParserException e) {
122 qWarning() <<
"The stream parser raised an exception:" << e.what();
125 if ( m_stream->availableDataSize()>1 ) {
126 QTimer::singleShot( 0,
this, SLOT(readMessage()) );
131 void SessionThread::closeSocket()
133 QTimer::singleShot( 0,
this, SLOT(doCloseSocket()) );
136 void SessionThread::doCloseSocket()
138 m_encryptedMode =
false;
142 void SessionThread::reconnect()
144 QMutexLocker locker(&m_mutex);
146 if ( m_socket->state() != SessionSocket::ConnectedState &&
147 m_socket->state() != SessionSocket::ConnectingState ) {
148 if (m_encryptedMode) {
149 m_socket->connectToHostEncrypted(m_hostName, m_port);
151 m_socket->connectToHost(m_hostName, m_port);
156 void SessionThread::run()
158 m_socket =
new SessionSocket;
160 connect( m_socket, SIGNAL(readyRead()),
161 this, SLOT(readMessage()), Qt::QueuedConnection );
163 connect( m_socket, SIGNAL(disconnected()),
164 this, SLOT(socketDisconnected()) );
165 connect( m_socket, SIGNAL(connected()),
166 m_session, SLOT(socketConnected()) );
167 connect( m_socket, SIGNAL(error(KTcpSocket::Error)),
168 this, SLOT(socketError()) );
169 connect( m_socket, SIGNAL(bytesWritten(qint64)),
170 m_session, SLOT(socketActivity()) );
171 if ( m_socket->metaObject()->indexOfSignal(
"encryptedBytesWritten(qint64)" ) > -1 ) {
172 connect( m_socket, SIGNAL(encryptedBytesWritten(qint64)),
173 m_session, SLOT(socketActivity()) );
175 connect( m_socket, SIGNAL(readyRead()),
176 m_session, SLOT(socketActivity()) );
178 connect(
this, SIGNAL(responseReceived(KIMAP::Message)),
179 m_session, SLOT(responseReceived(KIMAP::Message)) );
181 QTimer::singleShot( 0,
this, SLOT(reconnect()) );
188 void SessionThread::startSsl(
const KTcpSocket::SslVersion &version)
190 QMutexLocker locker(&m_mutex);
192 if ( version == KTcpSocket::AnySslVersion ) {
193 doSslFallback =
true;
194 if ( m_socket->advertisedSslVersion() == KTcpSocket::UnknownSslVersion ) {
195 m_socket->setAdvertisedSslVersion( KTcpSocket::AnySslVersion );
196 }
else if ( !( triedSslVersions & KTcpSocket::TlsV1 ) ) {
197 triedSslVersions |= KTcpSocket::TlsV1;
198 m_socket->setAdvertisedSslVersion( KTcpSocket::TlsV1 );
199 }
else if ( !( triedSslVersions & KTcpSocket::SslV3 ) ) {
200 triedSslVersions |= KTcpSocket::SslV3;
201 m_socket->setAdvertisedSslVersion( KTcpSocket::SslV3 );
202 }
else if ( !( triedSslVersions & KTcpSocket::SslV2 ) ) {
203 triedSslVersions |= KTcpSocket::SslV2;
204 m_socket->setAdvertisedSslVersion( KTcpSocket::SslV2 );
205 doSslFallback =
false;
208 m_socket->setAdvertisedSslVersion( version );
211 m_socket->ignoreSslErrors();
212 connect(m_socket, SIGNAL(encrypted()),
this, SLOT(sslConnected()));
213 m_socket->startClientEncryption();
216 void SessionThread::socketDisconnected()
218 if ( doSslFallback ) {
221 QMetaObject::invokeMethod( m_session,
"socketDisconnected" );
225 void SessionThread::socketError()
227 QMutexLocker locker( &m_mutex );
228 if ( doSslFallback ) {
230 m_socket->disconnectFromHost();
232 QMetaObject::invokeMethod( m_session,
"socketError" );
236 void SessionThread::sslConnected()
238 QMutexLocker locker(&m_mutex);
239 KSslCipher cipher = m_socket->sessionCipher();
241 if ( m_socket->sslErrors().count() > 0 || m_socket->encryptionMode() != KTcpSocket::SslClientMode
242 || cipher.isNull() || cipher.usedBits() == 0) {
243 kDebug() <<
"Initial SSL handshake failed. cipher.isNull() is" << cipher.isNull()
244 <<
", cipher.usedBits() is" << cipher.usedBits()
245 <<
", the socket says:" << m_socket->errorString()
246 <<
"and the list of SSL errors contains"
247 << m_socket->sslErrors().count() <<
"items.";
248 KSslErrorUiData errorData(m_socket);
249 emit sslError(errorData);
251 doSslFallback =
false;
252 kDebug() <<
"TLS negotiation done.";
253 m_encryptedMode =
true;
254 emit encryptionNegotiationResult(
true, m_socket->negotiatedSslVersion());
258 void SessionThread::sslErrorHandlerResponse(
bool response)
260 QMutexLocker locker(&m_mutex);
262 m_encryptedMode =
true;
263 emit encryptionNegotiationResult(
true, m_socket->negotiatedSslVersion());
265 m_encryptedMode =
false;
267 m_socket->disconnectFromHost();
268 m_socket->waitForDisconnected();
269 m_socket->connectToHost(m_hostName, m_port);
270 emit encryptionNegotiationResult(
false, KTcpSocket::UnknownSslVersion);
274 #include "sessionthread_p.moc"