00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "session.h"
00024 #include "session_p.h"
00025 #include "sessionuiproxy.h"
00026
00027 #include <QtCore/QDebug>
00028 #include <QtCore/QTimer>
00029
00030 #include <KDE/KMessageBox>
00031 #include <KDE/KLocale>
00032
00033 #include "job.h"
00034 #include "loginjob.h"
00035 #include "message_p.h"
00036 #include "sessionlogger_p.h"
00037 #include "sessionthread_p.h"
00038 #include "rfccodecs.h"
00039
00040 Q_DECLARE_METATYPE(KTcpSocket::SslVersion)
00041 Q_DECLARE_METATYPE(QSslSocket::SslMode)
00042 static const int _kimap_sslVersionId = qRegisterMetaType<KTcpSocket::SslVersion>();
00043
00044 using namespace KIMAP;
00045
00046 Session::Session( const QString &hostName, quint16 port, QObject *parent)
00047 : QObject(parent), d(new SessionPrivate(this))
00048 {
00049 if ( !qgetenv( "KIMAP_LOGFILE" ).isEmpty() ) {
00050 d->logger = SessionLogger::self();
00051 }
00052
00053 d->isSocketConnected = false;
00054 d->state = Disconnected;
00055 d->jobRunning = false;
00056
00057 d->thread = new SessionThread(hostName, port, this);
00058 connect(d->thread, SIGNAL(encryptionNegotiationResult(bool, KTcpSocket::SslVersion)),
00059 d, SLOT(onEncryptionNegotiationResult(bool, KTcpSocket::SslVersion)));
00060 connect(d->thread, SIGNAL(sslError(const KSslErrorUiData&)), this, SLOT(handleSslError(const KSslErrorUiData&)));
00061
00062 d->thread->start();
00063 }
00064
00065 Session::~Session()
00066 {
00067 delete d->thread;
00068 }
00069
00070 void Session::setUiProxy(SessionUiProxy::Ptr proxy)
00071 {
00072 d->uiProxy = proxy;
00073 }
00074
00075 void Session::setUiProxy(SessionUiProxy *proxy)
00076 {
00077 setUiProxy( SessionUiProxy::Ptr( proxy ) );
00078 }
00079
00080 QString Session::hostName() const
00081 {
00082 return d->thread->hostName();
00083 }
00084
00085 quint16 Session::port() const
00086 {
00087 return d->thread->port();
00088 }
00089
00090 Session::State Session::state() const
00091 {
00092 return d->state;
00093 }
00094
00095 QByteArray Session::serverGreeting() const
00096 {
00097 return d->greeting;
00098 }
00099
00100 int Session::jobQueueSize() const
00101 {
00102 return d->queue.size() + ( d->jobRunning ? 1 : 0 );
00103 }
00104
00105 void SessionPrivate::handleSslError(const KSslErrorUiData& errorData)
00106 {
00107 if (uiProxy && uiProxy->ignoreSslError(errorData)) {
00108 QMetaObject::invokeMethod( thread, "sslErrorHandlerResponse", Q_ARG(bool, true) );
00109 } else {
00110 QMetaObject::invokeMethod( thread, "sslErrorHandlerResponse", Q_ARG(bool, false) );
00111 }
00112 }
00113
00114 SessionPrivate::SessionPrivate( Session *session )
00115 : QObject( session ),
00116 q(session),
00117 state(Session::Disconnected),
00118 logger(0),
00119 currentJob(0),
00120 tagCount(0),
00121 sslVersion(KTcpSocket::UnknownSslVersion),
00122 socketTimerInterval(30000)
00123 {
00124 }
00125
00126 void SessionPrivate::addJob(Job *job)
00127 {
00128 queue.append(job);
00129 emit q->jobQueueSizeChanged( q->jobQueueSize() );
00130
00131 QObject::connect( job, SIGNAL(result(KJob*)), q, SLOT(jobDone(KJob*)) );
00132 QObject::connect( job, SIGNAL(destroyed(QObject*)), q, SLOT(jobDestroyed(QObject*)) );
00133
00134 if ( state!=Session::Disconnected ) {
00135 startNext();
00136 }
00137 }
00138
00139 void SessionPrivate::startNext()
00140 {
00141 QTimer::singleShot( 0, q, SLOT(doStartNext()) );
00142 }
00143
00144 void SessionPrivate::doStartNext()
00145 {
00146 if ( queue.isEmpty() || jobRunning || !isSocketConnected ) {
00147 return;
00148 }
00149
00150 startSocketTimer();
00151 jobRunning = true;
00152
00153 currentJob = queue.dequeue();
00154 currentJob->doStart();
00155 }
00156
00157 void SessionPrivate::jobDone( KJob *job )
00158 {
00159 Q_UNUSED( job );
00160 Q_ASSERT( job == currentJob );
00161
00162
00163
00164
00165 if ( state!=Session::Disconnected ) {
00166 stopSocketTimer();
00167 }
00168
00169 jobRunning = false;
00170 currentJob = 0;
00171 emit q->jobQueueSizeChanged( q->jobQueueSize() );
00172 startNext();
00173 }
00174
00175 void SessionPrivate::jobDestroyed( QObject *job )
00176 {
00177 queue.removeAll( static_cast<KIMAP::Job*>( job ) );
00178 if ( currentJob == job )
00179 currentJob = 0;
00180 }
00181
00182 void SessionPrivate::responseReceived( const Message &response )
00183 {
00184 if ( logger && ( state==Session::Authenticated || state==Session::Selected ) ) {
00185 logger->dataReceived( response.toString() );
00186 }
00187
00188 QByteArray tag;
00189 QByteArray code;
00190
00191 if ( response.content.size()>=1 ) {
00192 tag = response.content[0].toString();
00193 }
00194
00195 if ( response.content.size()>=2 ) {
00196 code = response.content[1].toString();
00197 }
00198
00199 switch ( state ) {
00200 case Session::Disconnected:
00201 if ( code=="OK" ) {
00202 state = Session::NotAuthenticated;
00203
00204 Message simplified = response;
00205 simplified.content.removeFirst();
00206 simplified.content.removeFirst();
00207 greeting = simplified.toString().trimmed();
00208
00209 startNext();
00210 } else if ( code=="PREAUTH" ) {
00211 state = Session::Authenticated;
00212
00213 Message simplified = response;
00214 simplified.content.removeFirst();
00215 simplified.content.removeFirst();
00216 greeting = simplified.toString().trimmed();
00217
00218 startNext();
00219 } else {
00220 thread->closeSocket();
00221 QTimer::singleShot( 1000, thread, SLOT( reconnect() ) );
00222 }
00223 return;
00224 case Session::NotAuthenticated:
00225 if ( code=="OK" && tag==authTag ) {
00226 state = Session::Authenticated;
00227 }
00228 break;
00229 case Session::Authenticated:
00230 if ( code=="OK" && tag==selectTag ) {
00231 state = Session::Selected;
00232 currentMailBox = upcomingMailBox;
00233 }
00234 break;
00235 case Session::Selected:
00236 if ( ( code=="OK" && tag==closeTag )
00237 || ( code!="OK" && tag==selectTag) ) {
00238 state = Session::Authenticated;
00239 currentMailBox = QByteArray();
00240 } else if ( code=="OK" && tag==selectTag ) {
00241 currentMailBox = upcomingMailBox;
00242 }
00243 break;
00244 }
00245
00246 if (tag==authTag) authTag.clear();
00247 if (tag==selectTag) selectTag.clear();
00248 if (tag==closeTag) closeTag.clear();
00249
00250
00251 if ( currentJob!=0 ) {
00252 restartSocketTimer();
00253 currentJob->handleResponse( response );
00254 } else {
00255 qWarning() << "A message was received from the server with no job to handle it:"
00256 << response.toString()
00257 << '('+response.toString().toHex()+')';
00258 }
00259 }
00260
00261 QByteArray SessionPrivate::sendCommand( const QByteArray &command, const QByteArray &args )
00262 {
00263 QByteArray tag = 'A' + QByteArray::number(++tagCount).rightJustified(6, '0');
00264
00265 QByteArray payload = tag+' '+command;
00266 if ( !args.isEmpty() ) {
00267 payload+= ' '+args;
00268 }
00269
00270 sendData( payload );
00271
00272 if ( command=="LOGIN" || command=="AUTHENTICATE" ) {
00273 authTag = tag;
00274 } else if ( command=="SELECT" || command=="EXAMINE" ) {
00275 selectTag = tag;
00276 upcomingMailBox = args;
00277 upcomingMailBox.remove( 0, 1 );
00278 upcomingMailBox.chop( 1 );
00279 upcomingMailBox = KIMAP::decodeImapFolderName( upcomingMailBox );
00280 } else if ( command=="CLOSE" ) {
00281 closeTag = tag;
00282 }
00283
00284 return tag;
00285 }
00286
00287 void SessionPrivate::sendData( const QByteArray &data )
00288 {
00289 restartSocketTimer();
00290
00291 if ( logger && ( state==Session::Authenticated || state==Session::Selected ) ) {
00292 logger->dataSent( data );
00293 }
00294
00295 thread->sendData(data+"\r\n");
00296 }
00297
00298 void SessionPrivate::socketConnected()
00299 {
00300 isSocketConnected = true;
00301
00302 bool willUseSsl = false;
00303 if ( !queue.isEmpty() ) {
00304 KIMAP::LoginJob *login = qobject_cast<KIMAP::LoginJob*>( queue.first() );
00305 if ( login ) {
00306 willUseSsl = ( login->encryptionMode() == KIMAP::LoginJob::SslV2 )
00307 || ( login->encryptionMode() == KIMAP::LoginJob::SslV3 )
00308 || ( login->encryptionMode() == KIMAP::LoginJob::SslV3_1 )
00309 || ( login->encryptionMode() == KIMAP::LoginJob::AnySslVersion );
00310 }
00311 }
00312
00313 if ( state == Session::Disconnected && willUseSsl ) {
00314 startNext();
00315 }
00316 }
00317
00318 void SessionPrivate::socketDisconnected()
00319 {
00320 if ( logger && ( state==Session::Authenticated || state==Session::Selected ) ) {
00321 logger->disconnectionOccured();
00322 }
00323
00324
00325 if ( state==Session::Authenticated || state==Session::Selected ) {
00326 emit q->connectionLost();
00327 }
00328
00329 isSocketConnected = false;
00330 state = Session::Disconnected;
00331 thread->closeSocket();
00332
00333 if ( currentJob ) {
00334 currentJob->connectionLost();
00335 }
00336 }
00337
00338 void SessionPrivate::socketError()
00339 {
00340
00341 socketDisconnected();
00342 }
00343
00344 void SessionPrivate::startSsl(const KTcpSocket::SslVersion &version)
00345 {
00346 QMetaObject::invokeMethod( thread, "startSsl", Qt::QueuedConnection, Q_ARG(KTcpSocket::SslVersion, version) );
00347 }
00348
00349 QString Session::selectedMailBox() const
00350 {
00351 return QString::fromUtf8( d->currentMailBox );
00352 }
00353
00354 void SessionPrivate::onEncryptionNegotiationResult(bool isEncrypted, KTcpSocket::SslVersion version)
00355 {
00356 if ( isEncrypted ) {
00357 sslVersion = version;
00358 } else {
00359 sslVersion = KTcpSocket::UnknownSslVersion;
00360 }
00361 emit encryptionNegotiationResult( isEncrypted );
00362 }
00363
00364 KTcpSocket::SslVersion SessionPrivate::negotiatedEncryption() const
00365 {
00366 return sslVersion;
00367 }
00368
00369 void SessionPrivate::setSocketTimeout( int ms )
00370 {
00371 bool timerActive = socketTimer.isActive();
00372
00373 if ( timerActive ) {
00374 stopSocketTimer();
00375 }
00376
00377 socketTimerInterval = ms;
00378
00379 if ( timerActive ) {
00380 startSocketTimer();
00381 }
00382 }
00383
00384 int SessionPrivate::socketTimeout() const
00385 {
00386 return socketTimerInterval;
00387 }
00388
00389 void SessionPrivate::startSocketTimer()
00390 {
00391 if ( socketTimerInterval<0 ) {
00392 return;
00393 }
00394 Q_ASSERT( !socketTimer.isActive() );
00395
00396 connect( &socketTimer, SIGNAL(timeout()),
00397 this, SLOT(onSocketTimeout()) );
00398
00399 socketTimer.setSingleShot( true );
00400 socketTimer.start( socketTimerInterval );
00401 }
00402
00403 void SessionPrivate::stopSocketTimer()
00404 {
00405 if ( socketTimerInterval<0 ) {
00406 return;
00407 }
00408 Q_ASSERT( socketTimer.isActive() );
00409
00410 socketTimer.stop();
00411
00412 disconnect( &socketTimer, SIGNAL(timeout()),
00413 this, SLOT(onSocketTimeout()) );
00414 }
00415
00416 void SessionPrivate::restartSocketTimer()
00417 {
00418 stopSocketTimer();
00419 startSocketTimer();
00420 }
00421
00422 void SessionPrivate::onSocketTimeout()
00423 {
00424 socketDisconnected();
00425 }
00426
00427 #include "session.moc"
00428 #include "session_p.moc"