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 <KDebug>
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 = new SessionLogger;
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 KIMAP::Session::close()
00106 {
00107 d->socketDisconnected();
00108 }
00109
00110 void SessionPrivate::handleSslError(const KSslErrorUiData& errorData)
00111 {
00112 if (uiProxy && uiProxy->ignoreSslError(errorData)) {
00113 QMetaObject::invokeMethod( thread, "sslErrorHandlerResponse", Q_ARG(bool, true) );
00114 } else {
00115 QMetaObject::invokeMethod( thread, "sslErrorHandlerResponse", Q_ARG(bool, false) );
00116 }
00117 }
00118
00119 SessionPrivate::SessionPrivate( Session *session )
00120 : QObject( session ),
00121 q(session),
00122 state(Session::Disconnected),
00123 logger(0),
00124 currentJob(0),
00125 tagCount(0),
00126 sslVersion(KTcpSocket::UnknownSslVersion),
00127 socketTimerInterval(30000)
00128 {
00129 }
00130
00131 SessionPrivate::~SessionPrivate()
00132 {
00133 delete logger;
00134 }
00135
00136 void SessionPrivate::addJob(Job *job)
00137 {
00138 queue.append(job);
00139 emit q->jobQueueSizeChanged( q->jobQueueSize() );
00140
00141 QObject::connect( job, SIGNAL(result(KJob*)), q, SLOT(jobDone(KJob*)) );
00142 QObject::connect( job, SIGNAL(destroyed(QObject*)), q, SLOT(jobDestroyed(QObject*)) );
00143
00144 if ( state!=Session::Disconnected ) {
00145 startNext();
00146 }
00147 }
00148
00149 void SessionPrivate::startNext()
00150 {
00151 QTimer::singleShot( 0, q, SLOT(doStartNext()) );
00152 }
00153
00154 void SessionPrivate::doStartNext()
00155 {
00156 if ( queue.isEmpty() || jobRunning || !isSocketConnected ) {
00157 return;
00158 }
00159
00160 startSocketTimer();
00161 jobRunning = true;
00162
00163 currentJob = queue.dequeue();
00164 currentJob->doStart();
00165 }
00166
00167 void SessionPrivate::jobDone( KJob *job )
00168 {
00169 Q_UNUSED( job );
00170 Q_ASSERT( job == currentJob );
00171
00172
00173
00174
00175 if ( state!=Session::Disconnected ) {
00176 stopSocketTimer();
00177 }
00178
00179 jobRunning = false;
00180 currentJob = 0;
00181 emit q->jobQueueSizeChanged( q->jobQueueSize() );
00182 startNext();
00183 }
00184
00185 void SessionPrivate::jobDestroyed( QObject *job )
00186 {
00187 queue.removeAll( static_cast<KIMAP::Job*>( job ) );
00188 if ( currentJob == job )
00189 currentJob = 0;
00190 }
00191
00192 void SessionPrivate::responseReceived( const Message &response )
00193 {
00194 if ( logger && ( state==Session::Authenticated || state==Session::Selected ) ) {
00195 logger->dataReceived( response.toString() );
00196 }
00197
00198 QByteArray tag;
00199 QByteArray code;
00200
00201 if ( response.content.size()>=1 ) {
00202 tag = response.content[0].toString();
00203 }
00204
00205 if ( response.content.size()>=2 ) {
00206 code = response.content[1].toString();
00207 }
00208
00209 switch ( state ) {
00210 case Session::Disconnected:
00211 if ( code=="OK" ) {
00212 state = Session::NotAuthenticated;
00213
00214 Message simplified = response;
00215 simplified.content.removeFirst();
00216 simplified.content.removeFirst();
00217 greeting = simplified.toString().trimmed();
00218
00219 startNext();
00220 } else if ( code=="PREAUTH" ) {
00221 state = Session::Authenticated;
00222
00223 Message simplified = response;
00224 simplified.content.removeFirst();
00225 simplified.content.removeFirst();
00226 greeting = simplified.toString().trimmed();
00227
00228 startNext();
00229 } else {
00230 thread->closeSocket();
00231 QTimer::singleShot( 1000, thread, SLOT( reconnect() ) );
00232 }
00233 return;
00234 case Session::NotAuthenticated:
00235 if ( code=="OK" && tag==authTag ) {
00236 state = Session::Authenticated;
00237 }
00238 break;
00239 case Session::Authenticated:
00240 if ( code=="OK" && tag==selectTag ) {
00241 state = Session::Selected;
00242 currentMailBox = upcomingMailBox;
00243 }
00244 break;
00245 case Session::Selected:
00246 if ( ( code=="OK" && tag==closeTag )
00247 || ( code!="OK" && tag==selectTag) ) {
00248 state = Session::Authenticated;
00249 currentMailBox = QByteArray();
00250 } else if ( code=="OK" && tag==selectTag ) {
00251 currentMailBox = upcomingMailBox;
00252 }
00253 break;
00254 }
00255
00256 if (tag==authTag) authTag.clear();
00257 if (tag==selectTag) selectTag.clear();
00258 if (tag==closeTag) closeTag.clear();
00259
00260
00261 if ( currentJob!=0 ) {
00262 restartSocketTimer();
00263 currentJob->handleResponse( response );
00264 } else {
00265 qWarning() << "A message was received from the server with no job to handle it:"
00266 << response.toString()
00267 << '('+response.toString().toHex()+')';
00268 }
00269 }
00270
00271 QByteArray SessionPrivate::sendCommand( const QByteArray &command, const QByteArray &args )
00272 {
00273 QByteArray tag = 'A' + QByteArray::number(++tagCount).rightJustified(6, '0');
00274
00275 QByteArray payload = tag+' '+command;
00276 if ( !args.isEmpty() ) {
00277 payload+= ' '+args;
00278 }
00279
00280 sendData( payload );
00281
00282 if ( command=="LOGIN" || command=="AUTHENTICATE" ) {
00283 authTag = tag;
00284 } else if ( command=="SELECT" || command=="EXAMINE" ) {
00285 selectTag = tag;
00286 upcomingMailBox = args;
00287 upcomingMailBox.remove( 0, 1 );
00288 upcomingMailBox.chop( 1 );
00289 upcomingMailBox = KIMAP::decodeImapFolderName( upcomingMailBox );
00290 } else if ( command=="CLOSE" ) {
00291 closeTag = tag;
00292 }
00293
00294 return tag;
00295 }
00296
00297 void SessionPrivate::sendData( const QByteArray &data )
00298 {
00299 restartSocketTimer();
00300
00301 if ( logger && ( state==Session::Authenticated || state==Session::Selected ) ) {
00302 logger->dataSent( data );
00303 }
00304
00305 thread->sendData(data+"\r\n");
00306 }
00307
00308 void SessionPrivate::socketConnected()
00309 {
00310 isSocketConnected = true;
00311
00312 bool willUseSsl = false;
00313 if ( !queue.isEmpty() ) {
00314 KIMAP::LoginJob *login = qobject_cast<KIMAP::LoginJob*>( queue.first() );
00315 if ( login ) {
00316 willUseSsl = ( login->encryptionMode() == KIMAP::LoginJob::SslV2 )
00317 || ( login->encryptionMode() == KIMAP::LoginJob::SslV3 )
00318 || ( login->encryptionMode() == KIMAP::LoginJob::SslV3_1 )
00319 || ( login->encryptionMode() == KIMAP::LoginJob::AnySslVersion );
00320 }
00321 }
00322
00323 if ( state == Session::Disconnected && willUseSsl ) {
00324 startNext();
00325 }
00326 }
00327
00328 void SessionPrivate::socketDisconnected()
00329 {
00330 if ( logger && ( state==Session::Authenticated || state==Session::Selected ) ) {
00331 logger->disconnectionOccured();
00332 }
00333
00334
00335 if ( state != Session::Disconnected ) {
00336 emit q->connectionLost();
00337 }
00338
00339 isSocketConnected = false;
00340 state = Session::Disconnected;
00341 thread->closeSocket();
00342
00343 if ( currentJob ) {
00344 currentJob->connectionLost();
00345 }
00346 }
00347
00348 void SessionPrivate::socketError()
00349 {
00350
00351 if ( isSocketConnected )
00352 socketDisconnected();
00353 else
00354 emit q->connectionLost();
00355 }
00356
00357 void SessionPrivate::startSsl(const KTcpSocket::SslVersion &version)
00358 {
00359 QMetaObject::invokeMethod( thread, "startSsl", Qt::QueuedConnection, Q_ARG(KTcpSocket::SslVersion, version) );
00360 }
00361
00362 QString Session::selectedMailBox() const
00363 {
00364 return QString::fromUtf8( d->currentMailBox );
00365 }
00366
00367 void SessionPrivate::onEncryptionNegotiationResult(bool isEncrypted, KTcpSocket::SslVersion version)
00368 {
00369 if ( isEncrypted ) {
00370 sslVersion = version;
00371 } else {
00372 sslVersion = KTcpSocket::UnknownSslVersion;
00373 }
00374 emit encryptionNegotiationResult( isEncrypted );
00375 }
00376
00377 KTcpSocket::SslVersion SessionPrivate::negotiatedEncryption() const
00378 {
00379 return sslVersion;
00380 }
00381
00382 void SessionPrivate::setSocketTimeout( int ms )
00383 {
00384 bool timerActive = socketTimer.isActive();
00385
00386 if ( timerActive ) {
00387 stopSocketTimer();
00388 }
00389
00390 socketTimerInterval = ms;
00391
00392 if ( timerActive ) {
00393 startSocketTimer();
00394 }
00395 }
00396
00397 int SessionPrivate::socketTimeout() const
00398 {
00399 return socketTimerInterval;
00400 }
00401
00402 void SessionPrivate::startSocketTimer()
00403 {
00404 if ( socketTimerInterval<0 ) {
00405 return;
00406 }
00407 Q_ASSERT( !socketTimer.isActive() );
00408
00409 connect( &socketTimer, SIGNAL(timeout()),
00410 this, SLOT(onSocketTimeout()) );
00411
00412 socketTimer.setSingleShot( true );
00413 socketTimer.start( socketTimerInterval );
00414 }
00415
00416 void SessionPrivate::stopSocketTimer()
00417 {
00418 if ( socketTimerInterval<0 ) {
00419 return;
00420 }
00421 Q_ASSERT( socketTimer.isActive() );
00422
00423 socketTimer.stop();
00424
00425 disconnect( &socketTimer, SIGNAL(timeout()),
00426 this, SLOT(onSocketTimeout()) );
00427 }
00428
00429 void SessionPrivate::restartSocketTimer()
00430 {
00431 stopSocketTimer();
00432 startSocketTimer();
00433 }
00434
00435 void SessionPrivate::onSocketTimeout()
00436 {
00437 kDebug();
00438 socketDisconnected();
00439 }
00440
00441 void Session::setTimeout( int timeout )
00442 {
00443 d->setSocketTimeout( timeout * 1000 );
00444 }
00445
00446 #include "session.moc"
00447 #include "session_p.moc"