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 )
198 void SessionPrivate::responseReceived(
const Message &response )
200 if ( logger && ( state==Session::Authenticated || state==Session::Selected ) ) {
201 logger->dataReceived( response.toString() );
207 if ( response.content.size()>=1 ) {
208 tag = response.content[0].toString();
211 if ( response.content.size()>=2 ) {
212 code = response.content[1].toString();
216 case Session::Disconnected:
217 if (socketTimer.isActive()) {
221 setState(Session::NotAuthenticated);
223 Message simplified = response;
224 simplified.content.removeFirst();
225 simplified.content.removeFirst();
226 greeting = simplified.toString().trimmed();
229 }
else if ( code==
"PREAUTH" ) {
230 setState(Session::Authenticated);
232 Message simplified = response;
233 simplified.content.removeFirst();
234 simplified.content.removeFirst();
235 greeting = simplified.toString().trimmed();
239 thread->closeSocket();
242 case Session::NotAuthenticated:
243 if ( code==
"OK" && tag==authTag ) {
244 setState(Session::Authenticated);
247 case Session::Authenticated:
248 if ( code==
"OK" && tag==selectTag ) {
249 setState(Session::Selected);
250 currentMailBox = upcomingMailBox;
253 case Session::Selected:
254 if ( ( code==
"OK" && tag==closeTag )
255 || ( code!=
"OK" && tag==selectTag) ) {
256 setState(Session::Authenticated);
257 currentMailBox = QByteArray();
258 }
else if ( code==
"OK" && tag==selectTag ) {
259 currentMailBox = upcomingMailBox;
264 if (tag==authTag) authTag.clear();
265 if (tag==selectTag) selectTag.clear();
266 if (tag==closeTag) closeTag.clear();
269 if ( currentJob!=0 ) {
270 restartSocketTimer();
271 currentJob->handleResponse( response );
273 qWarning() <<
"A message was received from the server with no job to handle it:"
274 << response.toString()
275 <<
'('+response.toString().toHex()+
')';
279 void SessionPrivate::setState(Session::State s)
282 Session::State oldState = state;
284 emit q->stateChanged(state, oldState);
288 QByteArray SessionPrivate::sendCommand(
const QByteArray &command,
const QByteArray &args )
290 QByteArray tag =
'A' + QByteArray::number(++tagCount).rightJustified(6,
'0');
292 QByteArray payload = tag+
' '+command;
293 if ( !args.isEmpty() ) {
299 if ( command==
"LOGIN" || command==
"AUTHENTICATE" ) {
301 }
else if ( command==
"SELECT" || command==
"EXAMINE" ) {
303 upcomingMailBox = args;
304 upcomingMailBox.remove( 0, 1 );
305 upcomingMailBox.chop( 1 );
306 upcomingMailBox = KIMAP::decodeImapFolderName( upcomingMailBox );
307 }
else if ( command==
"CLOSE" ) {
314 void SessionPrivate::sendData(
const QByteArray &data )
316 restartSocketTimer();
318 if ( logger && ( state==Session::Authenticated || state==Session::Selected ) ) {
319 logger->dataSent( data );
322 thread->sendData(data+
"\r\n");
325 void SessionPrivate::socketConnected()
328 isSocketConnected =
true;
330 bool willUseSsl =
false;
331 if ( !queue.isEmpty() ) {
332 KIMAP::LoginJob *login = qobject_cast<KIMAP::LoginJob*>( queue.first() );
334 willUseSsl = ( login->encryptionMode() == KIMAP::LoginJob::SslV2 )
335 || ( login->encryptionMode() == KIMAP::LoginJob::SslV3 )
336 || ( login->encryptionMode() == KIMAP::LoginJob::SslV3_1 )
337 || ( login->encryptionMode() == KIMAP::LoginJob::AnySslVersion );
339 userName = login->userName();
343 if ( state == Session::Disconnected && willUseSsl ) {
350 void SessionPrivate::socketDisconnected()
352 if (socketTimer.isActive()) {
356 if ( logger && ( state==Session::Authenticated || state==Session::Selected ) ) {
357 logger->disconnectionOccured();
360 if ( state != Session::Disconnected ) {
361 setState(Session::Disconnected);
362 emit q->connectionLost();
364 emit q->connectionFailed();
367 isSocketConnected =
false;
372 void SessionPrivate::socketActivity()
374 restartSocketTimer();
377 void SessionPrivate::socketError()
379 if (socketTimer.isActive()) {
383 if ( isSocketConnected ) {
384 thread->closeSocket();
386 emit q->connectionFailed();
387 emit q->connectionLost();
392 void SessionPrivate::clearJobQueue()
395 currentJob->connectionLost();
396 }
else if ( !queue.isEmpty() ) {
397 currentJob = queue.takeFirst();
398 currentJob->connectionLost();
403 emit q->jobQueueSizeChanged( 0 );
406 void SessionPrivate::startSsl(
const KTcpSocket::SslVersion &version)
408 QMetaObject::invokeMethod( thread,
"startSsl", Qt::QueuedConnection, Q_ARG(KTcpSocket::SslVersion, version) );
411 QString Session::selectedMailBox()
const
413 return QString::fromUtf8( d->currentMailBox );
416 void SessionPrivate::onEncryptionNegotiationResult(
bool isEncrypted, KTcpSocket::SslVersion version)
419 sslVersion = version;
421 sslVersion = KTcpSocket::UnknownSslVersion;
423 emit encryptionNegotiationResult( isEncrypted );
426 KTcpSocket::SslVersion SessionPrivate::negotiatedEncryption()
const
431 void SessionPrivate::setSocketTimeout(
int ms )
433 bool timerActive = socketTimer.isActive();
439 socketTimerInterval = ms;
446 int SessionPrivate::socketTimeout()
const
448 return socketTimerInterval;
451 void SessionPrivate::startSocketTimer()
453 if ( socketTimerInterval<0 ) {
456 Q_ASSERT( !socketTimer.isActive() );
458 connect( &socketTimer, SIGNAL(timeout()),
459 this, SLOT(onSocketTimeout()) );
461 socketTimer.setSingleShot(
true );
462 socketTimer.start( socketTimerInterval );
465 void SessionPrivate::stopSocketTimer()
467 if ( socketTimerInterval<0 ) {
473 disconnect( &socketTimer, SIGNAL(timeout()),
474 this, SLOT(onSocketTimeout()) );
477 void SessionPrivate::restartSocketTimer()
479 if ( socketTimer.isActive() ) {
485 void SessionPrivate::onSocketTimeout()
487 kDebug() <<
"Socket timeout!";
488 thread->closeSocket();
491 void Session::setTimeout(
int timeout )
493 d->setSocketTimeout( timeout * 1000 );
496 #include "session.moc"
497 #include "session_p.moc"