24 #include "session_p.h"
25 #include "sessionuiproxy.h"
27 #include <QtCore/QDebug>
28 #include <QtCore/QTimer>
31 #include <KDE/KLocalizedString>
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 );
58 connect( d->thread, SIGNAL(encryptionNegotiationResult(
bool,KTcpSocket::SslVersion)),
59 d, SLOT(onEncryptionNegotiationResult(
bool,KTcpSocket::SslVersion)) );
60 connect( d->thread, SIGNAL(sslError(KSslErrorUiData)),
61 d, SLOT(handleSslError(KSslErrorUiData)) );
62 connect( d->thread, SIGNAL(socketDisconnected()),
63 d, SLOT(socketDisconnected()) );
64 connect( d->thread, SIGNAL(responseReceived(KIMAP::Message)),
65 d, SLOT(responseReceived(KIMAP::Message)) );
66 connect( d->thread, SIGNAL(socketConnected()),
67 d, SLOT(socketConnected()) );
68 connect( d->thread, SIGNAL(socketActivity()),
69 d, SLOT(socketActivity()) );
70 connect( d->thread, SIGNAL(socketError()),
71 d, SLOT(socketError()) );
73 d->startSocketTimer();
81 void Session::setUiProxy(SessionUiProxy::Ptr proxy)
88 setUiProxy( SessionUiProxy::Ptr( proxy ) );
91 QString Session::hostName()
const
93 return d->thread->hostName();
96 quint16 Session::port()
const
98 return d->thread->port();
101 Session::State Session::state()
const
106 QString Session::userName()
const
111 QByteArray Session::serverGreeting()
const
116 int Session::jobQueueSize()
const
118 return d->queue.size() + ( d->jobRunning ? 1 : 0 );
121 void KIMAP::Session::close()
123 d->thread->closeSocket();
126 void SessionPrivate::handleSslError(
const KSslErrorUiData& errorData)
128 const bool ignoreSslError = uiProxy && uiProxy->ignoreSslError( errorData );
129 thread->sslErrorHandlerResponse(ignoreSslError);
132 SessionPrivate::SessionPrivate( Session *session )
133 : QObject( session ),
135 state( Session::Disconnected ),
139 sslVersion( KTcpSocket::UnknownSslVersion ),
140 socketTimerInterval( 30000 )
144 SessionPrivate::~SessionPrivate()
149 void SessionPrivate::addJob(Job *job)
152 emit q->jobQueueSizeChanged( q->jobQueueSize() );
154 QObject::connect( job, SIGNAL(result(KJob*)),
this, SLOT(jobDone(KJob*)) );
155 QObject::connect( job, SIGNAL(destroyed(QObject*)),
this, SLOT(jobDestroyed(QObject*)) );
157 if ( state != Session::Disconnected ) {
162 void SessionPrivate::startNext()
164 QMetaObject::invokeMethod(
this,
"doStartNext" );
167 void SessionPrivate::doStartNext()
169 if ( queue.isEmpty() || jobRunning || !isSocketConnected ) {
176 currentJob = queue.dequeue();
177 currentJob->doStart();
180 void SessionPrivate::jobDone( KJob *job )
183 Q_ASSERT( job == currentJob );
188 if ( state!=Session::Disconnected ) {
194 emit q->jobQueueSizeChanged( q->jobQueueSize() );
198 void SessionPrivate::jobDestroyed( QObject *job )
200 queue.removeAll( static_cast<KIMAP::Job*>( job ) );
201 if ( currentJob == job ) {
206 void SessionPrivate::responseReceived(
const Message &response )
208 if ( logger && ( state == Session::Authenticated || state == Session::Selected ) ) {
209 logger->dataReceived( response.toString() );
215 if ( response.content.size()>=1 ) {
216 tag = response.content[0].toString();
219 if ( response.content.size()>=2 ) {
220 code = response.content[1].toString();
224 case Session::Disconnected:
225 if ( socketTimer.isActive() ) {
228 if ( code ==
"OK" ) {
229 setState( Session::NotAuthenticated );
231 Message simplified = response;
232 simplified.content.removeFirst();
233 simplified.content.removeFirst();
234 greeting = simplified.toString().trimmed();
237 }
else if ( code ==
"PREAUTH" ) {
238 setState( Session::Authenticated );
240 Message simplified = response;
241 simplified.content.removeFirst();
242 simplified.content.removeFirst();
243 greeting = simplified.toString().trimmed();
247 thread->closeSocket();
250 case Session::NotAuthenticated:
251 if ( code ==
"OK" && tag == authTag ) {
252 setState( Session::Authenticated );
255 case Session::Authenticated:
256 if ( code ==
"OK" && tag == selectTag ) {
257 setState( Session::Selected );
258 currentMailBox = upcomingMailBox;
261 case Session::Selected:
262 if ( ( code ==
"OK" && tag == closeTag ) ||
263 ( code !=
"OK" && tag == selectTag ) ) {
264 setState( Session::Authenticated );
265 currentMailBox = QByteArray();
266 }
else if ( code ==
"OK" && tag == selectTag ) {
267 currentMailBox = upcomingMailBox;
272 if ( tag == authTag ) {
275 if ( tag == selectTag ) {
278 if ( tag == closeTag ) {
283 if ( currentJob != 0 ) {
284 restartSocketTimer();
285 currentJob->handleResponse( response );
287 qWarning() <<
"A message was received from the server with no job to handle it:"
288 << response.toString()
289 <<
'(' + response.toString().toHex() +
')';
293 void SessionPrivate::setState(Session::State s)
296 Session::State oldState = state;
298 emit q->stateChanged( state, oldState );
302 QByteArray SessionPrivate::sendCommand(
const QByteArray &command,
const QByteArray &args )
304 QByteArray tag =
'A' + QByteArray::number( ++tagCount ).rightJustified( 6,
'0' );
306 QByteArray payload = tag +
' ' + command;
307 if ( !args.isEmpty() ) {
308 payload +=
' ' + args;
313 if ( command ==
"LOGIN" || command ==
"AUTHENTICATE" ) {
315 }
else if ( command ==
"SELECT" || command ==
"EXAMINE" ) {
317 upcomingMailBox = args;
318 upcomingMailBox.remove( 0, 1 );
319 upcomingMailBox = upcomingMailBox.left( upcomingMailBox.indexOf(
'\"') );
320 upcomingMailBox = KIMAP::decodeImapFolderName( upcomingMailBox );
321 }
else if ( command ==
"CLOSE" ) {
327 void SessionPrivate::sendData(
const QByteArray &data )
329 restartSocketTimer();
331 if ( logger && ( state == Session::Authenticated || state == Session::Selected ) ) {
332 logger->dataSent( data );
335 thread->sendData( data +
"\r\n" );
338 void SessionPrivate::socketConnected()
341 isSocketConnected =
true;
343 bool willUseSsl =
false;
344 if ( !queue.isEmpty() ) {
345 KIMAP::LoginJob *login = qobject_cast<KIMAP::LoginJob*>( queue.first() );
347 willUseSsl = ( login->encryptionMode() == KIMAP::LoginJob::SslV2 ) ||
348 ( login->encryptionMode() == KIMAP::LoginJob::SslV3 ) ||
349 ( login->encryptionMode() == KIMAP::LoginJob::SslV3_1 ) ||
350 ( login->encryptionMode() == KIMAP::LoginJob::AnySslVersion );
352 userName = login->userName();
356 if ( state == Session::Disconnected && willUseSsl ) {
363 void SessionPrivate::socketDisconnected()
365 if ( socketTimer.isActive() ) {
369 if ( logger && ( state == Session::Authenticated || state == Session::Selected ) ) {
370 logger->disconnectionOccured();
373 if ( state != Session::Disconnected ) {
374 setState( Session::Disconnected );
375 emit q->connectionLost();
377 emit q->connectionFailed();
380 isSocketConnected =
false;
385 void SessionPrivate::socketActivity()
387 restartSocketTimer();
390 void SessionPrivate::socketError()
392 if ( socketTimer.isActive() ) {
396 if ( isSocketConnected ) {
397 thread->closeSocket();
399 emit q->connectionFailed();
400 emit q->connectionLost();
405 void SessionPrivate::clearJobQueue()
408 currentJob->connectionLost();
409 }
else if ( !queue.isEmpty() ) {
410 currentJob = queue.takeFirst();
411 currentJob->connectionLost();
414 QQueue<Job*> queueCopy = queue;
415 qDeleteAll(queueCopy);
417 emit q->jobQueueSizeChanged( 0 );
420 void SessionPrivate::startSsl(
const KTcpSocket::SslVersion &version)
422 thread->startSsl( version );
425 QString Session::selectedMailBox()
const
427 return QString::fromUtf8( d->currentMailBox );
430 void SessionPrivate::onEncryptionNegotiationResult(
bool isEncrypted, KTcpSocket::SslVersion version)
433 sslVersion = version;
435 sslVersion = KTcpSocket::UnknownSslVersion;
437 emit encryptionNegotiationResult( isEncrypted );
440 KTcpSocket::SslVersion SessionPrivate::negotiatedEncryption()
const
445 void SessionPrivate::setSocketTimeout(
int ms )
447 bool timerActive = socketTimer.isActive();
453 socketTimerInterval = ms;
460 int SessionPrivate::socketTimeout()
const
462 return socketTimerInterval;
465 void SessionPrivate::startSocketTimer()
467 if ( socketTimerInterval < 0 ) {
470 Q_ASSERT( !socketTimer.isActive() );
472 connect( &socketTimer, SIGNAL(timeout()),
473 this, SLOT(onSocketTimeout()) );
475 socketTimer.setSingleShot(
true );
476 socketTimer.start( socketTimerInterval );
479 void SessionPrivate::stopSocketTimer()
481 if ( socketTimerInterval < 0 ) {
487 disconnect( &socketTimer, SIGNAL(timeout()),
488 this, SLOT(onSocketTimeout()) );
491 void SessionPrivate::restartSocketTimer()
493 if ( socketTimer.isActive() ) {
499 void SessionPrivate::onSocketTimeout()
501 kDebug() <<
"Socket timeout!";
502 thread->closeSocket();
505 void Session::setTimeout(
int timeout )
507 d->setSocketTimeout( timeout * 1000 );
510 #include "moc_session.cpp"
511 #include "moc_session_p.cpp"
This file is part of the IMAP support library and defines the RfcCodecs class.
Interface to display communication errors and wait for user feedback.