KIMAP Library
sessionthread.cpp
00001 /* 00002 Copyright (c) 2009 Kevin Ottens <ervin@kde.org> 00003 00004 This library is free software; you can redistribute it and/or modify it 00005 under the terms of the GNU Library General Public License as published by 00006 the Free Software Foundation; either version 2 of the License, or (at your 00007 option) any later version. 00008 00009 This library is distributed in the hope that it will be useful, but WITHOUT 00010 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 00011 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public 00012 License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to the 00016 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 00017 02110-1301, USA. 00018 */ 00019 00020 #include "sessionthread_p.h" 00021 00022 #include <QtCore/QDebug> 00023 #include <QtCore/QTimer> 00024 00025 #include <KDE/KDebug> 00026 00027 #include "imapstreamparser.h" 00028 #include "message_p.h" 00029 #include "session.h" 00030 00031 using namespace KIMAP; 00032 00033 Q_DECLARE_METATYPE(KTcpSocket::Error) 00034 Q_DECLARE_METATYPE(KSslErrorUiData) 00035 static const int _kimap_socketErrorTypeId = qRegisterMetaType<KTcpSocket::Error>(); 00036 static const int _kimap_sslErrorUiData = qRegisterMetaType<KSslErrorUiData>(); 00037 00038 SessionThread::SessionThread( const QString &hostName, quint16 port, Session *parent ) 00039 : QThread(), m_hostName(hostName), m_port(port), 00040 m_session(parent), m_socket(0), m_stream(0), m_encryptedMode(false) 00041 { 00042 // Yeah, sounds weird, but QThread object is linked to the parent 00043 // thread not to itself, and I'm too lazy to introduce yet another 00044 // internal QObject 00045 moveToThread(this); 00046 } 00047 00048 SessionThread::~SessionThread() 00049 { 00050 // don't call quit() directly, this will deadlock in wait() if exec() hasn't run yet 00051 QMetaObject::invokeMethod( this, "quit" ); 00052 if ( !wait( 10 * 1000 ) ) { 00053 kWarning() << "Session thread refuses to die, killing harder..."; 00054 terminate(); 00055 } 00056 } 00057 00058 void SessionThread::sendData( const QByteArray &payload ) 00059 { 00060 QMutexLocker locker(&m_mutex); 00061 00062 m_dataQueue.enqueue( payload ); 00063 QTimer::singleShot( 0, this, SLOT( writeDataQueue() ) ); 00064 } 00065 00066 void SessionThread::writeDataQueue() 00067 { 00068 QMutexLocker locker(&m_mutex); 00069 00070 while ( !m_dataQueue.isEmpty() ) { 00071 m_socket->write( m_dataQueue.dequeue() ); 00072 } 00073 } 00074 00075 void SessionThread::readMessage() 00076 { 00077 QMutexLocker locker(&m_mutex); 00078 00079 if ( m_stream->availableDataSize()==0 ) { 00080 return; 00081 } 00082 00083 Message message; 00084 QList<Message::Part> *payload = &message.content; 00085 00086 try { 00087 while ( !m_stream->atCommandEnd() ) { 00088 if ( m_stream->hasString() ) { 00089 QByteArray string = m_stream->readString(); 00090 if ( string == "NIL" ) { 00091 *payload << Message::Part( QList<QByteArray>() ); 00092 } else { 00093 *payload << Message::Part(string); 00094 } 00095 } else if ( m_stream->hasList() ) { 00096 *payload << Message::Part(m_stream->readParenthesizedList()); 00097 } else if ( m_stream->hasResponseCode() ) { 00098 payload = &message.responseCode; 00099 } else if ( m_stream->atResponseCodeEnd() ) { 00100 payload = &message.content; 00101 } else if ( m_stream->hasLiteral() ) { 00102 QByteArray literal; 00103 while ( !m_stream->atLiteralEnd() ) { 00104 literal+= m_stream->readLiteralPart(); 00105 } 00106 *payload << Message::Part(literal); 00107 } 00108 } 00109 00110 emit responseReceived(message); 00111 00112 } catch (KIMAP::ImapParserException e) { 00113 qWarning() << "The stream parser raised an exception:" << e.what(); 00114 } 00115 00116 if ( m_stream->availableDataSize()>1 ) { 00117 QTimer::singleShot( 0, this, SLOT( readMessage() ) ); 00118 } 00119 00120 } 00121 00122 void SessionThread::closeSocket() 00123 { 00124 QTimer::singleShot( 0, this, SLOT( doCloseSocket() ) ); 00125 } 00126 00127 void SessionThread::doCloseSocket() 00128 { 00129 m_encryptedMode = false; 00130 m_socket->close(); 00131 } 00132 00133 void SessionThread::reconnect() 00134 { 00135 QMutexLocker locker(&m_mutex); 00136 00137 if ( m_socket->state() != SessionSocket::ConnectedState && 00138 m_socket->state() != SessionSocket::ConnectingState ) { 00139 if (m_encryptedMode) { 00140 m_socket->connectToHostEncrypted(m_hostName, m_port); 00141 } else { 00142 m_socket->connectToHost(m_hostName, m_port); 00143 } 00144 } 00145 } 00146 00147 void SessionThread::run() 00148 { 00149 m_socket = new SessionSocket; 00150 m_stream = new ImapStreamParser( m_socket ); 00151 connect( m_socket, SIGNAL(readyRead()), 00152 this, SLOT(readMessage()), Qt::QueuedConnection ); 00153 00154 connect( m_socket, SIGNAL(disconnected()), 00155 m_session, SLOT(socketDisconnected()) ); 00156 connect( m_socket, SIGNAL(connected()), 00157 m_session, SLOT(socketConnected()) ); 00158 connect( m_socket, SIGNAL(error(KTcpSocket::Error)), 00159 m_session, SLOT(socketError()) ); 00160 00161 00162 connect( this, SIGNAL(responseReceived(KIMAP::Message)), 00163 m_session, SLOT(responseReceived(KIMAP::Message)) ); 00164 00165 QTimer::singleShot( 0, this, SLOT( reconnect() ) ); 00166 exec(); 00167 00168 delete m_stream; 00169 delete m_socket; 00170 } 00171 00172 void SessionThread::startSsl(const KTcpSocket::SslVersion &version) 00173 { 00174 QMutexLocker locker(&m_mutex); 00175 00176 m_socket->setAdvertisedSslVersion(version); 00177 m_socket->ignoreSslErrors(); 00178 connect(m_socket, SIGNAL(encrypted()), this, SLOT(sslConnected())); 00179 m_socket->startClientEncryption(); 00180 } 00181 00182 void SessionThread::sslConnected() 00183 { 00184 QMutexLocker locker(&m_mutex); 00185 KSslCipher cipher = m_socket->sessionCipher(); 00186 00187 if ( m_socket->sslErrors().count() > 0 || m_socket->encryptionMode() != KTcpSocket::SslClientMode 00188 || cipher.isNull() || cipher.usedBits() == 0) { 00189 kDebug() << "Initial SSL handshake failed. cipher.isNull() is" << cipher.isNull() 00190 << ", cipher.usedBits() is" << cipher.usedBits() 00191 << ", the socket says:" << m_socket->errorString() 00192 << "and the list of SSL errors contains" 00193 << m_socket->sslErrors().count() << "items."; 00194 KSslErrorUiData errorData(m_socket); 00195 emit sslError(errorData); 00196 } else { 00197 kDebug() << "TLS negotiation done."; 00198 m_encryptedMode = true; 00199 emit encryptionNegotiationResult(true, m_socket->negotiatedSslVersion()); 00200 } 00201 } 00202 00203 void SessionThread::sslErrorHandlerResponse(bool response) 00204 { 00205 QMutexLocker locker(&m_mutex); 00206 if (response) { 00207 m_encryptedMode = true; 00208 emit encryptionNegotiationResult(true, m_socket->negotiatedSslVersion()); 00209 } else { 00210 m_encryptedMode = false; 00211 //reconnect in unencrypted mode, so new commands can be issued 00212 m_socket->disconnectFromHost(); 00213 m_socket->waitForDisconnected(); 00214 m_socket->connectToHost(m_hostName, m_port); 00215 emit encryptionNegotiationResult(false, KTcpSocket::UnknownSslVersion); 00216 } 00217 } 00218 00219 #include "sessionthread_p.moc" 00220