25#include "sessionuiproxy.h"
27#include <QtCore/QDebug>
28#include <QtCore/QTimer>
31#include <KDE/KLocalizedString>
36#include "sessionlogger_p.h"
37#include "sessionthread_p.h"
40Q_DECLARE_METATYPE( KTcpSocket::SslVersion )
41Q_DECLARE_METATYPE( QSslSocket::SslMode )
42static const int _kimap_sslVersionId = qRegisterMetaType<KTcpSocket::SslVersion>();
46Session::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(KTcpSocket::Error)),
71 d, SLOT(socketError(KTcpSocket::Error)) );
73 d->socketTimer.setSingleShot(
true );
74 connect( &d->socketTimer, SIGNAL(timeout()),
75 d, SLOT(onSocketTimeout()) );
77 d->startSocketTimer();
86void Session::setUiProxy(SessionUiProxy::Ptr proxy)
93 setUiProxy( SessionUiProxy::Ptr( proxy ) );
96QString Session::hostName()
const
98 return d->thread->hostName();
101quint16 Session::port()
const
103 return d->thread->port();
106Session::State Session::state()
const
111QString Session::userName()
const
116QByteArray Session::serverGreeting()
const
121int Session::jobQueueSize()
const
123 return d->queue.size() + ( d->jobRunning ? 1 : 0 );
126void KIMAP::Session::close()
128 d->thread->closeSocket();
131void SessionPrivate::handleSslError(
const KSslErrorUiData& errorData)
133 const bool ignoreSslError = uiProxy && uiProxy->ignoreSslError( errorData );
136 thread->sslErrorHandlerResponse(ignoreSslError);
140SessionPrivate::SessionPrivate( Session *session )
141 : QObject( session ),
143 state( Session::Disconnected ),
147 sslVersion( KTcpSocket::UnknownSslVersion ),
148 socketTimerInterval( 30000 )
152SessionPrivate::~SessionPrivate()
157void SessionPrivate::addJob(Job *job)
160 emit q->jobQueueSizeChanged( q->jobQueueSize() );
162 QObject::connect( job, SIGNAL(result(KJob*)),
this, SLOT(jobDone(KJob*)) );
163 QObject::connect( job, SIGNAL(destroyed(QObject*)),
this, SLOT(jobDestroyed(QObject*)) );
165 if ( state != Session::Disconnected ) {
170void SessionPrivate::startNext()
172 QMetaObject::invokeMethod(
this,
"doStartNext" );
175void SessionPrivate::doStartNext()
177 if ( queue.isEmpty() || jobRunning || !isSocketConnected ) {
181 restartSocketTimer();
184 currentJob = queue.dequeue();
185 currentJob->doStart();
188void SessionPrivate::jobDone( KJob *job )
191 Q_ASSERT( job == currentJob );
197 emit q->jobQueueSizeChanged( q->jobQueueSize() );
201void SessionPrivate::jobDestroyed( QObject *job )
203 queue.removeAll(
static_cast<KIMAP::Job*
>( job ) );
204 if ( currentJob == job ) {
209void SessionPrivate::responseReceived(
const Message &response )
211 if ( logger && ( state == Session::Authenticated || state == Session::Selected ) ) {
212 logger->dataReceived( response.toString() );
218 if ( response.content.size()>=1 ) {
219 tag = response.content[0].toString();
222 if ( response.content.size()>=2 ) {
223 code = response.content[1].toString();
228 if ( code ==
"BYE" ) {
229 Message simplified = response;
230 if ( simplified.content.size() >= 2 ) {
231 simplified.content.removeFirst();
232 simplified.content.removeFirst();
234 kDebug() <<
"Received BYE: " << simplified.toString();
239 case Session::Disconnected:
240 if ( socketTimer.isActive() ) {
243 if ( code ==
"OK" ) {
244 setState( Session::NotAuthenticated );
246 Message simplified = response;
247 simplified.content.removeFirst();
248 simplified.content.removeFirst();
249 greeting = simplified.toString().trimmed();
252 }
else if ( code ==
"PREAUTH" ) {
253 setState( Session::Authenticated );
255 Message simplified = response;
256 simplified.content.removeFirst();
257 simplified.content.removeFirst();
258 greeting = simplified.toString().trimmed();
262 thread->closeSocket();
265 case Session::NotAuthenticated:
266 if ( code ==
"OK" && tag == authTag ) {
267 setState( Session::Authenticated );
270 case Session::Authenticated:
271 if ( code ==
"OK" && tag == selectTag ) {
272 setState( Session::Selected );
273 currentMailBox = upcomingMailBox;
276 case Session::Selected:
277 if ( ( code ==
"OK" && tag == closeTag ) ||
278 ( code !=
"OK" && tag == selectTag ) ) {
279 setState( Session::Authenticated );
280 currentMailBox = QByteArray();
281 }
else if ( code ==
"OK" && tag == selectTag ) {
282 currentMailBox = upcomingMailBox;
287 if ( tag == authTag ) {
290 if ( tag == selectTag ) {
293 if ( tag == closeTag ) {
298 if ( currentJob != 0 ) {
299 restartSocketTimer();
300 currentJob->handleResponse( response );
302 qWarning() <<
"A message was received from the server with no job to handle it:"
303 << response.toString()
304 <<
'(' + response.toString().toHex() +
')';
308void SessionPrivate::setState(Session::State s)
311 Session::State oldState = state;
313 emit q->stateChanged( state, oldState );
317QByteArray SessionPrivate::sendCommand(
const QByteArray &command,
const QByteArray &args )
319 QByteArray tag =
'A' + QByteArray::number( ++tagCount ).rightJustified( 6,
'0' );
321 QByteArray payload = tag +
' ' + command;
322 if ( !args.isEmpty() ) {
323 payload +=
' ' + args;
328 if ( command ==
"LOGIN" || command ==
"AUTHENTICATE" ) {
330 }
else if ( command ==
"SELECT" || command ==
"EXAMINE" ) {
332 upcomingMailBox = args;
333 upcomingMailBox.remove( 0, 1 );
334 upcomingMailBox = upcomingMailBox.left( upcomingMailBox.indexOf(
'\"') );
336 }
else if ( command ==
"CLOSE" ) {
342void SessionPrivate::sendData(
const QByteArray &data )
344 restartSocketTimer();
346 if ( logger && ( state == Session::Authenticated || state == Session::Selected ) ) {
347 logger->dataSent( data );
350 thread->sendData( data +
"\r\n" );
353void SessionPrivate::socketConnected()
356 isSocketConnected =
true;
358 bool willUseSsl =
false;
359 if ( !queue.isEmpty() ) {
360 KIMAP::LoginJob *login = qobject_cast<KIMAP::LoginJob*>( queue.first() );
362 willUseSsl = ( login->encryptionMode() == KIMAP::LoginJob::SslV2 ) ||
363 ( login->encryptionMode() == KIMAP::LoginJob::SslV3 ) ||
364 ( login->encryptionMode() == KIMAP::LoginJob::SslV3_1 ) ||
365 ( login->encryptionMode() == KIMAP::LoginJob::AnySslVersion );
367 userName = login->userName();
371 if ( state == Session::Disconnected && willUseSsl ) {
378void SessionPrivate::socketDisconnected()
380 if ( socketTimer.isActive() ) {
384 if ( logger && ( state == Session::Authenticated || state == Session::Selected ) ) {
385 logger->disconnectionOccured();
388 if ( state != Session::Disconnected ) {
389 setState( Session::Disconnected );
390 emit q->connectionLost();
392 emit q->connectionFailed();
395 isSocketConnected =
false;
400void SessionPrivate::socketActivity()
402 restartSocketTimer();
405void SessionPrivate::socketError(KTcpSocket::Error error)
407 if ( socketTimer.isActive() ) {
412 currentJob->setSocketError(error);
413 }
else if ( !queue.isEmpty() ) {
414 currentJob = queue.takeFirst();
415 currentJob->setSocketError(error);
418 if ( isSocketConnected ) {
419 thread->closeSocket();
421 emit q->connectionFailed();
422 emit q->connectionLost();
427void SessionPrivate::clearJobQueue()
430 currentJob->connectionLost();
431 }
else if ( !queue.isEmpty() ) {
432 currentJob = queue.takeFirst();
433 currentJob->connectionLost();
436 QQueue<Job*> queueCopy = queue;
437 qDeleteAll(queueCopy);
439 emit q->jobQueueSizeChanged( 0 );
442void SessionPrivate::startSsl(
const KTcpSocket::SslVersion &version)
444 thread->startSsl( version );
447QString Session::selectedMailBox()
const
449 return QString::fromUtf8( d->currentMailBox );
452void SessionPrivate::onEncryptionNegotiationResult(
bool isEncrypted, KTcpSocket::SslVersion version)
455 sslVersion = version;
457 sslVersion = KTcpSocket::UnknownSslVersion;
459 emit encryptionNegotiationResult( isEncrypted );
462KTcpSocket::SslVersion SessionPrivate::negotiatedEncryption()
const
467void SessionPrivate::setSocketTimeout(
int ms )
469 bool timerActive = socketTimer.isActive();
475 socketTimerInterval = ms;
482int SessionPrivate::socketTimeout()
const
484 return socketTimerInterval;
487void SessionPrivate::startSocketTimer()
489 if ( socketTimerInterval < 0 ) {
492 Q_ASSERT( !socketTimer.isActive() );
494 socketTimer.start( socketTimerInterval );
497void SessionPrivate::stopSocketTimer()
499 if ( socketTimerInterval < 0 ) {
506void SessionPrivate::restartSocketTimer()
508 if ( socketTimer.isActive() ) {
514void SessionPrivate::onSocketTimeout()
516 kDebug() <<
"Socket timeout!";
517 thread->closeSocket();
520void Session::setTimeout(
int timeout )
522 d->setSocketTimeout( timeout * 1000 );
525int Session::timeout()
const
527 return d->socketTimeout() / 1000;
530#include "moc_session.cpp"
531#include "moc_session_p.cpp"
Interface to display communication errors and wait for user feedback.
This file is part of the IMAP support library and defines the RfcCodecs class.
QByteArray decodeImapFolderName(const QByteArray &inSrc)
Converts an UTF-7 encoded IMAP mailbox to a QByteArray.