• Skip to content
  • Skip to link menu
KDE 4.8 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(KSslErrorUiData)), this, SLOT(handleSslError(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::socketActivity()
00373 {
00374   restartSocketTimer();
00375 }
00376 
00377 void SessionPrivate::socketError()
00378 {
00379   if (socketTimer.isActive()) {
00380     stopSocketTimer();
00381   }
00382 
00383   if ( isSocketConnected ) {
00384     thread->closeSocket();
00385   } else {
00386     emit q->connectionFailed();
00387     emit q->connectionLost();    // KDE5: Remove this. We shouldn't emit connectionLost() if we weren't connected in the first place
00388     clearJobQueue();
00389   }
00390 }
00391 
00392 void SessionPrivate::clearJobQueue()
00393 {
00394   if ( currentJob ) {
00395     currentJob->connectionLost();
00396   } else if ( !queue.isEmpty() ) {
00397     currentJob = queue.takeFirst();
00398     currentJob->connectionLost();
00399   }
00400 
00401   qDeleteAll(queue);
00402   queue.clear();
00403   emit q->jobQueueSizeChanged( 0 );
00404 }
00405 
00406 void SessionPrivate::startSsl(const KTcpSocket::SslVersion &version)
00407 {
00408   QMetaObject::invokeMethod( thread, "startSsl", Qt::QueuedConnection, Q_ARG(KTcpSocket::SslVersion, version) );
00409 }
00410 
00411 QString Session::selectedMailBox() const
00412 {
00413   return QString::fromUtf8( d->currentMailBox );
00414 }
00415 
00416 void SessionPrivate::onEncryptionNegotiationResult(bool isEncrypted, KTcpSocket::SslVersion version)
00417 {
00418   if ( isEncrypted ) {
00419     sslVersion = version;
00420   } else {
00421     sslVersion = KTcpSocket::UnknownSslVersion;
00422   }
00423   emit encryptionNegotiationResult( isEncrypted );
00424 }
00425 
00426 KTcpSocket::SslVersion SessionPrivate::negotiatedEncryption() const
00427 {
00428   return sslVersion;
00429 }
00430 
00431 void SessionPrivate::setSocketTimeout( int ms )
00432 {
00433   bool timerActive = socketTimer.isActive();
00434 
00435   if ( timerActive ) {
00436     stopSocketTimer();
00437   }
00438 
00439   socketTimerInterval = ms;
00440 
00441   if ( timerActive ) {
00442     startSocketTimer();
00443   }
00444 }
00445 
00446 int SessionPrivate::socketTimeout() const
00447 {
00448   return socketTimerInterval;
00449 }
00450 
00451 void SessionPrivate::startSocketTimer()
00452 {
00453   if ( socketTimerInterval<0 ) {
00454     return;
00455   }
00456   Q_ASSERT( !socketTimer.isActive() );
00457 
00458   connect( &socketTimer, SIGNAL(timeout()),
00459            this, SLOT(onSocketTimeout()) );
00460 
00461   socketTimer.setSingleShot( true );
00462   socketTimer.start( socketTimerInterval );
00463 }
00464 
00465 void SessionPrivate::stopSocketTimer()
00466 {
00467   if ( socketTimerInterval<0 ) {
00468     return;
00469   }
00470 
00471   socketTimer.stop();
00472 
00473   disconnect( &socketTimer, SIGNAL(timeout()),
00474               this, SLOT(onSocketTimeout()) );
00475 }
00476 
00477 void SessionPrivate::restartSocketTimer()
00478 {
00479   if ( socketTimer.isActive() ) {
00480     stopSocketTimer();
00481   }
00482   startSocketTimer();
00483 }
00484 
00485 void SessionPrivate::onSocketTimeout()
00486 {
00487   kDebug() << "Socket timeout!";
00488   thread->closeSocket();
00489 }
00490 
00491 void Session::setTimeout( int timeout )
00492 {
00493   d->setSocketTimeout( timeout * 1000 );
00494 }
00495 
00496 #include "session.moc"
00497 #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
  • kalarmcal
  • 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.6.1
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