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 // Make sure to wait until it's done, otherwise it can crash when the pthread callback is called 00056 wait(); 00057 } 00058 } 00059 00060 void SessionThread::sendData( const QByteArray &payload ) 00061 { 00062 QMutexLocker locker(&m_mutex); 00063 00064 m_dataQueue.enqueue( payload ); 00065 QTimer::singleShot( 0, this, SLOT(writeDataQueue()) ); 00066 } 00067 00068 void SessionThread::writeDataQueue() 00069 { 00070 QMutexLocker locker(&m_mutex); 00071 00072 while ( !m_dataQueue.isEmpty() ) { 00073 m_socket->write( m_dataQueue.dequeue() ); 00074 } 00075 } 00076 00077 void SessionThread::readMessage() 00078 { 00079 QMutexLocker locker(&m_mutex); 00080 00081 if ( m_stream->availableDataSize()==0 ) { 00082 return; 00083 } 00084 00085 Message message; 00086 QList<Message::Part> *payload = &message.content; 00087 00088 try { 00089 while ( !m_stream->atCommandEnd() ) { 00090 if ( m_stream->hasString() ) { 00091 QByteArray string = m_stream->readString(); 00092 if ( string == "NIL" ) { 00093 *payload << Message::Part( QList<QByteArray>() ); 00094 } else { 00095 *payload << Message::Part(string); 00096 } 00097 } else if ( m_stream->hasList() ) { 00098 *payload << Message::Part(m_stream->readParenthesizedList()); 00099 } else if ( m_stream->hasResponseCode() ) { 00100 payload = &message.responseCode; 00101 } else if ( m_stream->atResponseCodeEnd() ) { 00102 payload = &message.content; 00103 } else if ( m_stream->hasLiteral() ) { 00104 QByteArray literal; 00105 while ( !m_stream->atLiteralEnd() ) { 00106 literal+= m_stream->readLiteralPart(); 00107 } 00108 *payload << Message::Part(literal); 00109 } else { 00110 // Oops! Something really bad happened, we won't be able to recover 00111 // so close the socket immediately 00112 qWarning( "Inconsistent state, probably due to some packet loss" ); 00113 doCloseSocket(); 00114 return; 00115 } 00116 } 00117 00118 emit responseReceived(message); 00119 00120 } catch (KIMAP::ImapParserException e) { 00121 qWarning() << "The stream parser raised an exception:" << e.what(); 00122 } 00123 00124 if ( m_stream->availableDataSize()>1 ) { 00125 QTimer::singleShot( 0, this, SLOT(readMessage()) ); 00126 } 00127 00128 } 00129 00130 void SessionThread::closeSocket() 00131 { 00132 QTimer::singleShot( 0, this, SLOT(doCloseSocket()) ); 00133 } 00134 00135 void SessionThread::doCloseSocket() 00136 { 00137 m_encryptedMode = false; 00138 m_socket->close(); 00139 } 00140 00141 void SessionThread::reconnect() 00142 { 00143 QMutexLocker locker(&m_mutex); 00144 00145 if ( m_socket->state() != SessionSocket::ConnectedState && 00146 m_socket->state() != SessionSocket::ConnectingState ) { 00147 if (m_encryptedMode) { 00148 m_socket->connectToHostEncrypted(m_hostName, m_port); 00149 } else { 00150 m_socket->connectToHost(m_hostName, m_port); 00151 } 00152 } 00153 } 00154 00155 void SessionThread::run() 00156 { 00157 m_socket = new SessionSocket; 00158 m_stream = new ImapStreamParser( m_socket ); 00159 connect( m_socket, SIGNAL(readyRead()), 00160 this, SLOT(readMessage()), Qt::QueuedConnection ); 00161 00162 connect( m_socket, SIGNAL(disconnected()), 00163 m_session, SLOT(socketDisconnected()) ); 00164 connect( m_socket, SIGNAL(connected()), 00165 m_session, SLOT(socketConnected()) ); 00166 connect( m_socket, SIGNAL(error(KTcpSocket::Error)), 00167 m_session, SLOT(socketError()) ); 00168 connect( m_socket, SIGNAL(bytesWritten(qint64)), 00169 m_session, SLOT(socketActivity()) ); 00170 if ( m_socket->metaObject()->indexOfSignal("encryptedBytesWritten(qint64)" ) > -1 ) { 00171 connect( m_socket, SIGNAL(encryptedBytesWritten(qint64)), // needs kdelibs > 4.8 00172 m_session, SLOT(socketActivity()) ); 00173 } 00174 connect( m_socket, SIGNAL(readyRead()), 00175 m_session, SLOT(socketActivity()) ); 00176 00177 connect( this, SIGNAL(responseReceived(KIMAP::Message)), 00178 m_session, SLOT(responseReceived(KIMAP::Message)) ); 00179 00180 QTimer::singleShot( 0, this, SLOT(reconnect()) ); 00181 exec(); 00182 00183 delete m_stream; 00184 delete m_socket; 00185 } 00186 00187 void SessionThread::startSsl(const KTcpSocket::SslVersion &version) 00188 { 00189 QMutexLocker locker(&m_mutex); 00190 00191 m_socket->setAdvertisedSslVersion(version); 00192 m_socket->ignoreSslErrors(); 00193 connect(m_socket, SIGNAL(encrypted()), this, SLOT(sslConnected())); 00194 m_socket->startClientEncryption(); 00195 } 00196 00197 void SessionThread::sslConnected() 00198 { 00199 QMutexLocker locker(&m_mutex); 00200 KSslCipher cipher = m_socket->sessionCipher(); 00201 00202 if ( m_socket->sslErrors().count() > 0 || m_socket->encryptionMode() != KTcpSocket::SslClientMode 00203 || cipher.isNull() || cipher.usedBits() == 0) { 00204 kDebug() << "Initial SSL handshake failed. cipher.isNull() is" << cipher.isNull() 00205 << ", cipher.usedBits() is" << cipher.usedBits() 00206 << ", the socket says:" << m_socket->errorString() 00207 << "and the list of SSL errors contains" 00208 << m_socket->sslErrors().count() << "items."; 00209 KSslErrorUiData errorData(m_socket); 00210 emit sslError(errorData); 00211 } else { 00212 kDebug() << "TLS negotiation done."; 00213 m_encryptedMode = true; 00214 emit encryptionNegotiationResult(true, m_socket->negotiatedSslVersion()); 00215 } 00216 } 00217 00218 void SessionThread::sslErrorHandlerResponse(bool response) 00219 { 00220 QMutexLocker locker(&m_mutex); 00221 if (response) { 00222 m_encryptedMode = true; 00223 emit encryptionNegotiationResult(true, m_socket->negotiatedSslVersion()); 00224 } else { 00225 m_encryptedMode = false; 00226 //reconnect in unencrypted mode, so new commands can be issued 00227 m_socket->disconnectFromHost(); 00228 m_socket->waitForDisconnected(); 00229 m_socket->connectToHost(m_hostName, m_port); 00230 emit encryptionNegotiationResult(false, KTcpSocket::UnknownSslVersion); 00231 } 00232 } 00233 00234 #include "sessionthread_p.moc" 00235
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Mon Aug 27 2012 22:08:14 by doxygen 1.7.5 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2012 The KDE developers.
Generated on Mon Aug 27 2012 22:08:14 by doxygen 1.7.5 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.