• Skip to content
  • Skip to link menu
KDE 4.5 API Reference
  • KDE API Reference
  • KDE-PIM Libraries
  • Sitemap
  • 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 <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) // By default timeouts on 30s
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   // If we're in disconnected state it's because we ended up
00163   // here because the inactivity timer triggered, so no need to
00164   // stop it (it is single shot)
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(); // Strip the tag
00206       simplified.content.removeFirst(); // Strip the code
00207       greeting = simplified.toString().trimmed(); // Save the server greeting
00208 
00209       startNext();
00210     } else if ( code=="PREAUTH" ) {
00211       state = Session::Authenticated;
00212 
00213       Message simplified = response;
00214       simplified.content.removeFirst(); // Strip the tag
00215       simplified.content.removeFirst(); // Strip the code
00216       greeting = simplified.toString().trimmed(); // Save the server greeting
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   // If a job is running forward it the response
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   //qWarning() << "Socket error occurred:" << socket->errorString();
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"

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
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • 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.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