24 #include "session_p.h"
25 #include "sessionuiproxy.h"
27 #include <QtCore/QDebug>
28 #include <QtCore/QTimer>
31 #include <KDE/KLocale>
35 #include "message_p.h"
36 #include "sessionlogger_p.h"
37 #include "sessionthread_p.h"
40 Q_DECLARE_METATYPE( KTcpSocket::SslVersion )
41 Q_DECLARE_METATYPE( QSslSocket::SslMode )
42 static const
int _kimap_sslVersionId = qRegisterMetaType<KTcpSocket::SslVersion>();
44 using namespace KIMAP;
46 Session::Session( const QString &hostName, quint16 port, QObject *parent)
47 : QObject( parent ), d( new SessionPrivate( this ) )
49 if ( !qgetenv(
"KIMAP_LOGFILE" ).isEmpty() ) {
50 d->logger =
new SessionLogger;
53 d->isSocketConnected =
false;
54 d->state = Disconnected;
55 d->jobRunning =
false;
57 d->thread =
new SessionThread( hostName, port,
this );
58 connect( d->thread, SIGNAL(encryptionNegotiationResult(
bool,KTcpSocket::SslVersion)),
59 d, SLOT(onEncryptionNegotiationResult(
bool,KTcpSocket::SslVersion)) );
60 connect( d->thread, SIGNAL(sslError(KSslErrorUiData)),
this, SLOT(handleSslError(KSslErrorUiData)) );
62 d->startSocketTimer();
71 void Session::setUiProxy(SessionUiProxy::Ptr proxy)
78 setUiProxy( SessionUiProxy::Ptr( proxy ) );
81 QString Session::hostName()
const
83 return d->thread->hostName();
86 quint16 Session::port()
const
88 return d->thread->port();
91 Session::State Session::state()
const
96 QString Session::userName()
const
101 QByteArray Session::serverGreeting()
const
106 int Session::jobQueueSize()
const
108 return d->queue.size() + ( d->jobRunning ? 1 : 0 );
111 void KIMAP::Session::close()
113 d->thread->closeSocket();
116 void SessionPrivate::handleSslError(
const KSslErrorUiData& errorData)
118 if ( uiProxy && uiProxy->ignoreSslError( errorData ) ) {
119 QMetaObject::invokeMethod( thread,
"sslErrorHandlerResponse", Q_ARG(
bool,
true ) );
121 QMetaObject::invokeMethod( thread,
"sslErrorHandlerResponse", Q_ARG(
bool,
false ) );
125 SessionPrivate::SessionPrivate( Session *session )
126 : QObject( session ),
128 state( Session::Disconnected ),
132 sslVersion( KTcpSocket::UnknownSslVersion ),
133 socketTimerInterval( 30000 )
137 SessionPrivate::~SessionPrivate()
142 void SessionPrivate::addJob(Job *job)
145 emit q->jobQueueSizeChanged( q->jobQueueSize() );
147 QObject::connect( job, SIGNAL(result(KJob*)), q, SLOT(jobDone(KJob*)) );
148 QObject::connect( job, SIGNAL(destroyed(QObject*)), q, SLOT(jobDestroyed(QObject*)) );
150 if ( state!=Session::Disconnected ) {
155 void SessionPrivate::startNext()
157 QTimer::singleShot( 0, q, SLOT(doStartNext()) );
160 void SessionPrivate::doStartNext()
162 if ( queue.isEmpty() || jobRunning || !isSocketConnected ) {
169 currentJob = queue.dequeue();
170 currentJob->doStart();
173 void SessionPrivate::jobDone( KJob *job )
176 Q_ASSERT( job == currentJob );
181 if ( state!=Session::Disconnected ) {
187 emit q->jobQueueSizeChanged( q->jobQueueSize() );
191 void SessionPrivate::jobDestroyed( QObject *job )
193 queue.removeAll( static_cast<KIMAP::Job*>( job ) );
194 if ( currentJob == job ) {
199 void SessionPrivate::responseReceived(
const Message &response )
201 if ( logger && ( state == Session::Authenticated || state == Session::Selected ) ) {
202 logger->dataReceived( response.toString() );
208 if ( response.content.size()>=1 ) {
209 tag = response.content[0].toString();
212 if ( response.content.size()>=2 ) {
213 code = response.content[1].toString();
217 case Session::Disconnected:
218 if ( socketTimer.isActive() ) {
221 if ( code ==
"OK" ) {
222 setState( Session::NotAuthenticated );
224 Message simplified = response;
225 simplified.content.removeFirst();
226 simplified.content.removeFirst();
227 greeting = simplified.toString().trimmed();
230 }
else if ( code ==
"PREAUTH" ) {
231 setState( Session::Authenticated );
233 Message simplified = response;
234 simplified.content.removeFirst();
235 simplified.content.removeFirst();
236 greeting = simplified.toString().trimmed();
240 thread->closeSocket();
243 case Session::NotAuthenticated:
244 if ( code ==
"OK" && tag == authTag ) {
245 setState( Session::Authenticated );
248 case Session::Authenticated:
249 if ( code ==
"OK" && tag == selectTag ) {
250 setState( Session::Selected );
251 currentMailBox = upcomingMailBox;
254 case Session::Selected:
255 if ( ( code ==
"OK" && tag == closeTag ) ||
256 ( code !=
"OK" && tag == selectTag ) ) {
257 setState( Session::Authenticated );
258 currentMailBox = QByteArray();
259 }
else if ( code ==
"OK" && tag == selectTag ) {
260 currentMailBox = upcomingMailBox;
265 if ( tag == authTag ) {
268 if ( tag == selectTag ) {
271 if ( tag == closeTag ) {
276 if ( currentJob != 0 ) {
277 restartSocketTimer();
278 currentJob->handleResponse( response );
280 qWarning() <<
"A message was received from the server with no job to handle it:"
281 << response.toString()
282 <<
'(' + response.toString().toHex() +
')';
286 void SessionPrivate::setState(Session::State s)
289 Session::State oldState = state;
291 emit q->stateChanged( state, oldState );
295 QByteArray SessionPrivate::sendCommand(
const QByteArray &command,
const QByteArray &args )
297 QByteArray tag =
'A' + QByteArray::number( ++tagCount ).rightJustified( 6,
'0' );
299 QByteArray payload = tag +
' ' + command;
300 if ( !args.isEmpty() ) {
301 payload +=
' ' + args;
306 if ( command ==
"LOGIN" || command ==
"AUTHENTICATE" ) {
308 }
else if ( command ==
"SELECT" || command ==
"EXAMINE" ) {
310 upcomingMailBox = args;
311 upcomingMailBox.remove( 0, 1 );
312 upcomingMailBox.chop( 1 );
313 upcomingMailBox = KIMAP::decodeImapFolderName( upcomingMailBox );
314 }
else if ( command ==
"CLOSE" ) {
320 void SessionPrivate::sendData(
const QByteArray &data )
322 restartSocketTimer();
324 if ( logger && ( state == Session::Authenticated || state == Session::Selected ) ) {
325 logger->dataSent( data );
328 thread->sendData( data +
"\r\n" );
331 void SessionPrivate::socketConnected()
334 isSocketConnected =
true;
336 bool willUseSsl =
false;
337 if ( !queue.isEmpty() ) {
338 KIMAP::LoginJob *login = qobject_cast<KIMAP::LoginJob*>( queue.first() );
340 willUseSsl = ( login->encryptionMode() == KIMAP::LoginJob::SslV2 ) ||
341 ( login->encryptionMode() == KIMAP::LoginJob::SslV3 ) ||
342 ( login->encryptionMode() == KIMAP::LoginJob::SslV3_1 ) ||
343 ( login->encryptionMode() == KIMAP::LoginJob::AnySslVersion );
345 userName = login->userName();
349 if ( state == Session::Disconnected && willUseSsl ) {
356 void SessionPrivate::socketDisconnected()
358 if ( socketTimer.isActive() ) {
362 if ( logger && ( state == Session::Authenticated || state == Session::Selected ) ) {
363 logger->disconnectionOccured();
366 if ( state != Session::Disconnected ) {
367 setState( Session::Disconnected );
368 emit q->connectionLost();
370 emit q->connectionFailed();
373 isSocketConnected =
false;
378 void SessionPrivate::socketActivity()
380 restartSocketTimer();
383 void SessionPrivate::socketError()
385 if ( socketTimer.isActive() ) {
389 if ( isSocketConnected ) {
390 thread->closeSocket();
392 emit q->connectionFailed();
393 emit q->connectionLost();
398 void SessionPrivate::clearJobQueue()
401 currentJob->connectionLost();
402 }
else if ( !queue.isEmpty() ) {
403 currentJob = queue.takeFirst();
404 currentJob->connectionLost();
407 QQueue<Job*> queueCopy = queue;
408 qDeleteAll(queueCopy);
410 emit q->jobQueueSizeChanged( 0 );
413 void SessionPrivate::startSsl(
const KTcpSocket::SslVersion &version)
415 QMetaObject::invokeMethod( thread,
"startSsl", Qt::QueuedConnection, Q_ARG( KTcpSocket::SslVersion, version ) );
418 QString Session::selectedMailBox()
const
420 return QString::fromUtf8( d->currentMailBox );
423 void SessionPrivate::onEncryptionNegotiationResult(
bool isEncrypted, KTcpSocket::SslVersion version)
426 sslVersion = version;
428 sslVersion = KTcpSocket::UnknownSslVersion;
430 emit encryptionNegotiationResult( isEncrypted );
433 KTcpSocket::SslVersion SessionPrivate::negotiatedEncryption()
const
438 void SessionPrivate::setSocketTimeout(
int ms )
440 bool timerActive = socketTimer.isActive();
446 socketTimerInterval = ms;
453 int SessionPrivate::socketTimeout()
const
455 return socketTimerInterval;
458 void SessionPrivate::startSocketTimer()
460 if ( socketTimerInterval < 0 ) {
463 Q_ASSERT( !socketTimer.isActive() );
465 connect( &socketTimer, SIGNAL(timeout()),
466 this, SLOT(onSocketTimeout()) );
468 socketTimer.setSingleShot(
true );
469 socketTimer.start( socketTimerInterval );
472 void SessionPrivate::stopSocketTimer()
474 if ( socketTimerInterval < 0 ) {
480 disconnect( &socketTimer, SIGNAL(timeout()),
481 this, SLOT(onSocketTimeout()) );
484 void SessionPrivate::restartSocketTimer()
486 if ( socketTimer.isActive() ) {
492 void SessionPrivate::onSocketTimeout()
494 kDebug() <<
"Socket timeout!";
495 thread->closeSocket();
498 void Session::setTimeout(
int timeout )
500 d->setSocketTimeout( timeout * 1000 );
503 #include "moc_session.cpp"
504 #include "moc_session_p.cpp"