• Skip to content
  • Skip to link menu
KDE 4.7 API Reference
  • KDE API Reference
  • KDE-PIM Libraries
  • KDE Home
  • Contact Us
 

KIMAP Library

session.cpp
00001 /*
00002     Copyright (c) 2009 Kevin Ottens <ervin@kde.org>
00003 
00004     Copyright (c) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
00005     Author: Kevin Ottens <kevin@kdab.com>
00006 
00007     This library is free software; you can redistribute it and/or modify it
00008     under the terms of the GNU Library General Public License as published by
00009     the Free Software Foundation; either version 2 of the License, or (at your
00010     option) any later version.
00011 
00012     This library is distributed in the hope that it will be useful, but WITHOUT
00013     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00014     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
00015     License for more details.
00016 
00017     You should have received a copy of the GNU Library General Public License
00018     along with this library; see the file COPYING.LIB.  If not, write to the
00019     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00020     02110-1301, USA.
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->startSocketTimer();
00063   d->thread->start();
00064 }
00065 
00066 Session::~Session()
00067 {
00068   delete d->thread;
00069 }
00070 
00071 void Session::setUiProxy(SessionUiProxy::Ptr proxy)
00072 {
00073   d->uiProxy = proxy;
00074 }
00075 
00076 void Session::setUiProxy(SessionUiProxy *proxy)
00077 {
00078   setUiProxy( SessionUiProxy::Ptr( proxy ) );
00079 }
00080 
00081 QString Session::hostName() const
00082 {
00083   return d->thread->hostName();
00084 }
00085 
00086 quint16 Session::port() const
00087 {
00088   return d->thread->port();
00089 }
00090 
00091 Session::State Session::state() const
00092 {
00093   return d->state;
00094 }
00095 
00096 QString Session::userName() const
00097 {
00098     return d->userName;
00099 }
00100 
00101 QByteArray Session::serverGreeting() const
00102 {
00103   return d->greeting;
00104 }
00105 
00106 int Session::jobQueueSize() const
00107 {
00108   return d->queue.size() + ( d->jobRunning ? 1 : 0 );
00109 }
00110 
00111 void KIMAP::Session::close()
00112 {
00113   d->thread->closeSocket();
00114 }
00115 
00116 void SessionPrivate::handleSslError(const KSslErrorUiData& errorData)
00117 {
00118   if (uiProxy && uiProxy->ignoreSslError(errorData)) {
00119     QMetaObject::invokeMethod( thread, "sslErrorHandlerResponse", Q_ARG(bool, true) );
00120   } else {
00121     QMetaObject::invokeMethod( thread, "sslErrorHandlerResponse", Q_ARG(bool, false) );
00122   }
00123 }
00124 
00125 SessionPrivate::SessionPrivate( Session *session )
00126   : QObject( session ),
00127     q(session),
00128     state(Session::Disconnected),
00129     logger(0),
00130     currentJob(0),
00131     tagCount(0),
00132     sslVersion(KTcpSocket::UnknownSslVersion),
00133     socketTimerInterval(30000) // By default timeouts on 30s
00134 {
00135 }
00136 
00137 SessionPrivate::~SessionPrivate()
00138 {
00139   delete logger;
00140 }
00141 
00142 void SessionPrivate::addJob(Job *job)
00143 {
00144   queue.append(job);
00145   emit q->jobQueueSizeChanged( q->jobQueueSize() );
00146 
00147   QObject::connect( job, SIGNAL(result(KJob*)), q, SLOT(jobDone(KJob*)) );
00148   QObject::connect( job, SIGNAL(destroyed(QObject*)), q, SLOT(jobDestroyed(QObject*)) );
00149 
00150   if ( state!=Session::Disconnected ) {
00151     startNext();
00152   }
00153 }
00154 
00155 void SessionPrivate::startNext()
00156 {
00157   QTimer::singleShot( 0, q, SLOT(doStartNext()) );
00158 }
00159 
00160 void SessionPrivate::doStartNext()
00161 {
00162   if ( queue.isEmpty() || jobRunning || !isSocketConnected ) {
00163     return;
00164   }
00165 
00166   startSocketTimer();
00167   jobRunning = true;
00168 
00169   currentJob = queue.dequeue();
00170   currentJob->doStart();
00171 }
00172 
00173 void SessionPrivate::jobDone( KJob *job )
00174 {
00175   Q_UNUSED( job );
00176   Q_ASSERT( job == currentJob );
00177 
00178   // If we're in disconnected state it's because we ended up
00179   // here because the inactivity timer triggered, so no need to
00180   // stop it (it is single shot)
00181   if ( state!=Session::Disconnected ) {
00182     stopSocketTimer();
00183   }
00184 
00185   jobRunning = false;
00186   currentJob = 0;
00187   emit q->jobQueueSizeChanged( q->jobQueueSize() );
00188   startNext();
00189 }
00190 
00191 void SessionPrivate::jobDestroyed( QObject *job )
00192 {
00193   queue.removeAll( static_cast<KIMAP::Job*>( job ) );
00194   if ( currentJob == job )
00195     currentJob = 0;
00196 }
00197 
00198 void SessionPrivate::responseReceived( const Message &response )
00199 {
00200   if ( logger && ( state==Session::Authenticated || state==Session::Selected ) ) {
00201     logger->dataReceived( response.toString() );
00202   }
00203 
00204   QByteArray tag;
00205   QByteArray code;
00206 
00207   if ( response.content.size()>=1 ) {
00208     tag = response.content[0].toString();
00209   }
00210 
00211   if ( response.content.size()>=2 ) {
00212     code = response.content[1].toString();
00213   }
00214 
00215   switch ( state ) {
00216   case Session::Disconnected:
00217     if (socketTimer.isActive()) {
00218       stopSocketTimer();
00219     }
00220     if ( code=="OK" ) {
00221       setState(Session::NotAuthenticated);
00222 
00223       Message simplified = response;
00224       simplified.content.removeFirst(); // Strip the tag
00225       simplified.content.removeFirst(); // Strip the code
00226       greeting = simplified.toString().trimmed(); // Save the server greeting
00227 
00228       startNext();
00229     } else if ( code=="PREAUTH" ) {
00230       setState(Session::Authenticated);
00231 
00232       Message simplified = response;
00233       simplified.content.removeFirst(); // Strip the tag
00234       simplified.content.removeFirst(); // Strip the code
00235       greeting = simplified.toString().trimmed(); // Save the server greeting
00236 
00237       startNext();
00238     } else {
00239       thread->closeSocket();
00240     }
00241     return;
00242   case Session::NotAuthenticated:
00243     if ( code=="OK" && tag==authTag ) {
00244       setState(Session::Authenticated);
00245     }
00246     break;
00247   case Session::Authenticated:
00248     if ( code=="OK" && tag==selectTag ) {
00249       setState(Session::Selected);
00250       currentMailBox = upcomingMailBox;
00251     }
00252     break;
00253   case Session::Selected:
00254     if ( ( code=="OK" && tag==closeTag )
00255       || ( code!="OK" && tag==selectTag) ) {
00256       setState(Session::Authenticated);
00257       currentMailBox = QByteArray();
00258     } else if ( code=="OK" && tag==selectTag ) {
00259       currentMailBox = upcomingMailBox;
00260     }
00261     break;
00262   }
00263 
00264   if (tag==authTag) authTag.clear();
00265   if (tag==selectTag) selectTag.clear();
00266   if (tag==closeTag) closeTag.clear();
00267 
00268   // If a job is running forward it the response
00269   if ( currentJob!=0 ) {
00270     restartSocketTimer();
00271     currentJob->handleResponse( response );
00272   } else {
00273     qWarning() << "A message was received from the server with no job to handle it:"
00274                << response.toString()
00275                << '('+response.toString().toHex()+')';
00276   }
00277 }
00278 
00279 void SessionPrivate::setState(Session::State s)
00280 {
00281   if (s != state) {
00282     Session::State oldState = state;
00283     state = s;
00284     emit q->stateChanged(state, oldState);
00285   }
00286 }
00287 
00288 QByteArray SessionPrivate::sendCommand( const QByteArray &command, const QByteArray &args )
00289 {
00290   QByteArray tag = 'A' + QByteArray::number(++tagCount).rightJustified(6, '0');
00291 
00292   QByteArray payload = tag+' '+command;
00293   if ( !args.isEmpty() ) {
00294     payload+= ' '+args;
00295   }
00296 
00297   sendData( payload );
00298 
00299   if ( command=="LOGIN" || command=="AUTHENTICATE" ) {
00300     authTag = tag;
00301   } else if ( command=="SELECT" || command=="EXAMINE" ) {
00302     selectTag = tag;
00303     upcomingMailBox = args;
00304     upcomingMailBox.remove( 0, 1 );
00305     upcomingMailBox.chop( 1 );
00306     upcomingMailBox = KIMAP::decodeImapFolderName( upcomingMailBox );
00307   } else if ( command=="CLOSE" ) {
00308     closeTag = tag;
00309   }
00310 
00311   return tag;
00312 }
00313 
00314 void SessionPrivate::sendData( const QByteArray &data )
00315 {
00316   restartSocketTimer();
00317 
00318   if ( logger && ( state==Session::Authenticated || state==Session::Selected ) ) {
00319     logger->dataSent( data );
00320   }
00321 
00322   thread->sendData(data+"\r\n");
00323 }
00324 
00325 void SessionPrivate::socketConnected()
00326 {
00327   stopSocketTimer();
00328   isSocketConnected = true;
00329 
00330   bool willUseSsl = false;
00331   if ( !queue.isEmpty() ) {
00332     KIMAP::LoginJob *login = qobject_cast<KIMAP::LoginJob*>( queue.first() );
00333     if ( login ) {
00334       willUseSsl = ( login->encryptionMode() == KIMAP::LoginJob::SslV2 )
00335                 || ( login->encryptionMode() == KIMAP::LoginJob::SslV3 )
00336                 || ( login->encryptionMode() == KIMAP::LoginJob::SslV3_1 )
00337                 || ( login->encryptionMode() == KIMAP::LoginJob::AnySslVersion );
00338 
00339       userName = login->userName();
00340     }
00341   }
00342 
00343   if ( state == Session::Disconnected && willUseSsl ) {
00344     startNext();
00345   } else {
00346     startSocketTimer();
00347   }
00348 }
00349 
00350 void SessionPrivate::socketDisconnected()
00351 {
00352   if (socketTimer.isActive()) {
00353     stopSocketTimer();
00354   }
00355 
00356   if ( logger && ( state==Session::Authenticated || state==Session::Selected ) ) {
00357     logger->disconnectionOccured();
00358   }
00359 
00360   if ( state != Session::Disconnected ) {
00361     setState(Session::Disconnected);
00362     emit q->connectionLost();
00363   } else {
00364     emit q->connectionFailed();
00365   }
00366 
00367   isSocketConnected = false;
00368 
00369   clearJobQueue();
00370 }
00371 
00372 void SessionPrivate::socketError()
00373 {
00374   if (socketTimer.isActive()) {
00375     stopSocketTimer();
00376   }
00377 
00378   if ( isSocketConnected ) {
00379     thread->closeSocket();
00380   } else {
00381     emit q->connectionFailed();
00382     emit q->connectionLost();    // KDE5: Remove this. We shouldn't emit connectionLost() if we weren't connected in the first place
00383     clearJobQueue();
00384   }
00385 }
00386 
00387 void SessionPrivate::clearJobQueue()
00388 {
00389   if ( currentJob ) {
00390     currentJob->connectionLost();
00391   } else if ( !queue.isEmpty() ) {
00392     currentJob = queue.takeFirst();
00393     currentJob->connectionLost();
00394   }
00395 
00396   qDeleteAll(queue);
00397   queue.clear();
00398   emit q->jobQueueSizeChanged( 0 );
00399 }
00400 
00401 void SessionPrivate::startSsl(const KTcpSocket::SslVersion &version)
00402 {
00403   QMetaObject::invokeMethod( thread, "startSsl", Qt::QueuedConnection, Q_ARG(KTcpSocket::SslVersion, version) );
00404 }
00405 
00406 QString Session::selectedMailBox() const
00407 {
00408   return QString::fromUtf8( d->currentMailBox );
00409 }
00410 
00411 void SessionPrivate::onEncryptionNegotiationResult(bool isEncrypted, KTcpSocket::SslVersion version)
00412 {
00413   if ( isEncrypted ) {
00414     sslVersion = version;
00415   } else {
00416     sslVersion = KTcpSocket::UnknownSslVersion;
00417   }
00418   emit encryptionNegotiationResult( isEncrypted );
00419 }
00420 
00421 KTcpSocket::SslVersion SessionPrivate::negotiatedEncryption() const
00422 {
00423   return sslVersion;
00424 }
00425 
00426 void SessionPrivate::setSocketTimeout( int ms )
00427 {
00428   bool timerActive = socketTimer.isActive();
00429 
00430   if ( timerActive ) {
00431     stopSocketTimer();
00432   }
00433 
00434   socketTimerInterval = ms;
00435 
00436   if ( timerActive ) {
00437     startSocketTimer();
00438   }
00439 }
00440 
00441 int SessionPrivate::socketTimeout() const
00442 {
00443   return socketTimerInterval;
00444 }
00445 
00446 void SessionPrivate::startSocketTimer()
00447 {
00448   if ( socketTimerInterval<0 ) {
00449     return;
00450   }
00451   Q_ASSERT( !socketTimer.isActive() );
00452 
00453   connect( &socketTimer, SIGNAL(timeout()),
00454            this, SLOT(onSocketTimeout()) );
00455 
00456   socketTimer.setSingleShot( true );
00457   socketTimer.start( socketTimerInterval );
00458 }
00459 
00460 void SessionPrivate::stopSocketTimer()
00461 {
00462   if ( socketTimerInterval<0 ) {
00463     return;
00464   }
00465 
00466   socketTimer.stop();
00467 
00468   disconnect( &socketTimer, SIGNAL(timeout()),
00469               this, SLOT(onSocketTimeout()) );
00470 }
00471 
00472 void SessionPrivate::restartSocketTimer()
00473 {
00474   if ( socketTimer.isActive() ) {
00475     stopSocketTimer();
00476   }
00477   startSocketTimer();
00478 }
00479 
00480 void SessionPrivate::onSocketTimeout()
00481 {
00482   thread->closeSocket();
00483 }
00484 
00485 void Session::setTimeout( int timeout )
00486 {
00487   d->setSocketTimeout( timeout * 1000 );
00488 }
00489 
00490 #include "session.moc"
00491 #include "session_p.moc"

KIMAP Library

Skip menu "KIMAP Library"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • Related Pages

KDE-PIM Libraries

Skip menu "KDE-PIM Libraries"
  • akonadi
  •   contact
  •   kmime
  • kabc
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.7.5
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal