• Skip to content
  • Skip to link menu
KDE 4.6 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->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) // By default timeouts on 30s
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   // If we're in disconnected state it's because we ended up
00173   // here because the inactivity timer triggered, so no need to
00174   // stop it (it is single shot)
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(); // Strip the tag
00216       simplified.content.removeFirst(); // Strip the code
00217       greeting = simplified.toString().trimmed(); // Save the server greeting
00218 
00219       startNext();
00220     } else if ( code=="PREAUTH" ) {
00221       state = Session::Authenticated;
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 {
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   // If a job is running forward it the response
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   //qWarning() << "Socket error occurred:" << socket->errorString();
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   if ( socketTimer.isActive() ) {
00432     stopSocketTimer();
00433   }
00434   startSocketTimer();
00435 }
00436 
00437 void SessionPrivate::onSocketTimeout()
00438 {
00439   kDebug();
00440   socketDisconnected();
00441 }
00442 
00443 void Session::setTimeout( int timeout )
00444 {
00445   d->setSocketTimeout( timeout * 1000 );
00446 }
00447 
00448 #include "session.moc"
00449 #include "session_p.moc"

KIMAP Library

Skip menu "KIMAP Library"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • 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.4
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