• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdepimlibs API Reference
  • KDE Home
  • Contact Us
 

KIMAP Library

  • sources
  • kde-4.12
  • kdepimlibs
  • kimap
session.cpp
1 /*
2  Copyright (c) 2009 Kevin Ottens <ervin@kde.org>
3 
4  Copyright (c) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
5  Author: Kevin Ottens <kevin@kdab.com>
6 
7  This library is free software; you can redistribute it and/or modify it
8  under the terms of the GNU Library General Public License as published by
9  the Free Software Foundation; either version 2 of the License, or (at your
10  option) any later version.
11 
12  This library is distributed in the hope that it will be useful, but WITHOUT
13  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
15  License for more details.
16 
17  You should have received a copy of the GNU Library General Public License
18  along with this library; see the file COPYING.LIB. If not, write to the
19  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20  02110-1301, USA.
21 */
22 
23 #include "session.h"
24 #include "session_p.h"
25 #include "sessionuiproxy.h"
26 
27 #include <QtCore/QDebug>
28 #include <QtCore/QTimer>
29 
30 #include <KDebug>
31 #include <KDE/KLocalizedString>
32 
33 #include "job.h"
34 #include "loginjob.h"
35 #include "message_p.h"
36 #include "sessionlogger_p.h"
37 #include "sessionthread_p.h"
38 #include "rfccodecs.h"
39 
40 Q_DECLARE_METATYPE( KTcpSocket::SslVersion )
41 Q_DECLARE_METATYPE( QSslSocket::SslMode )
42 static const int _kimap_sslVersionId = qRegisterMetaType<KTcpSocket::SslVersion>();
43 
44 using namespace KIMAP;
45 
46 Session::Session( const QString &hostName, quint16 port, QObject *parent)
47  : QObject( parent ), d( new SessionPrivate( this ) )
48 {
49  if ( !qgetenv( "KIMAP_LOGFILE" ).isEmpty() ) {
50  d->logger = new SessionLogger;
51  }
52 
53  d->isSocketConnected = false;
54  d->state = Disconnected;
55  d->jobRunning = false;
56 
57  d->thread = new SessionThread( hostName, port );
58  connect( d->thread, SIGNAL(encryptionNegotiationResult(bool,KTcpSocket::SslVersion)),
59  d, SLOT(onEncryptionNegotiationResult(bool,KTcpSocket::SslVersion)) );
60  connect( d->thread, SIGNAL(sslError(KSslErrorUiData)),
61  d, SLOT(handleSslError(KSslErrorUiData)) );
62  connect( d->thread, SIGNAL(socketDisconnected()),
63  d, SLOT(socketDisconnected()) );
64  connect( d->thread, SIGNAL(responseReceived(KIMAP::Message)),
65  d, SLOT(responseReceived(KIMAP::Message)) );
66  connect( d->thread, SIGNAL(socketConnected()),
67  d, SLOT(socketConnected()) );
68  connect( d->thread, SIGNAL(socketActivity()),
69  d, SLOT(socketActivity()) );
70  connect( d->thread, SIGNAL(socketError()),
71  d, SLOT(socketError()) );
72 
73  d->startSocketTimer();
74 }
75 
76 Session::~Session()
77 {
78  delete d->thread;
79 }
80 
81 void Session::setUiProxy(SessionUiProxy::Ptr proxy)
82 {
83  d->uiProxy = proxy;
84 }
85 
86 void Session::setUiProxy(SessionUiProxy *proxy)
87 {
88  setUiProxy( SessionUiProxy::Ptr( proxy ) );
89 }
90 
91 QString Session::hostName() const
92 {
93  return d->thread->hostName();
94 }
95 
96 quint16 Session::port() const
97 {
98  return d->thread->port();
99 }
100 
101 Session::State Session::state() const
102 {
103  return d->state;
104 }
105 
106 QString Session::userName() const
107 {
108  return d->userName;
109 }
110 
111 QByteArray Session::serverGreeting() const
112 {
113  return d->greeting;
114 }
115 
116 int Session::jobQueueSize() const
117 {
118  return d->queue.size() + ( d->jobRunning ? 1 : 0 );
119 }
120 
121 void KIMAP::Session::close()
122 {
123  d->thread->closeSocket();
124 }
125 
126 void SessionPrivate::handleSslError(const KSslErrorUiData& errorData)
127 {
128  const bool ignoreSslError = uiProxy && uiProxy->ignoreSslError( errorData );
129  thread->sslErrorHandlerResponse(ignoreSslError);
130 }
131 
132 SessionPrivate::SessionPrivate( Session *session )
133  : QObject( session ),
134  q( session ),
135  state( Session::Disconnected ),
136  logger( 0 ),
137  currentJob( 0 ),
138  tagCount( 0 ),
139  sslVersion( KTcpSocket::UnknownSslVersion ),
140  socketTimerInterval( 30000 ) // By default timeouts on 30s
141 {
142 }
143 
144 SessionPrivate::~SessionPrivate()
145 {
146  delete logger;
147 }
148 
149 void SessionPrivate::addJob(Job *job)
150 {
151  queue.append( job );
152  emit q->jobQueueSizeChanged( q->jobQueueSize() );
153 
154  QObject::connect( job, SIGNAL(result(KJob*)), this, SLOT(jobDone(KJob*)) );
155  QObject::connect( job, SIGNAL(destroyed(QObject*)), this, SLOT(jobDestroyed(QObject*)) );
156 
157  if ( state != Session::Disconnected ) {
158  startNext();
159  }
160 }
161 
162 void SessionPrivate::startNext()
163 {
164  QMetaObject::invokeMethod( this, "doStartNext" );
165 }
166 
167 void SessionPrivate::doStartNext()
168 {
169  if ( queue.isEmpty() || jobRunning || !isSocketConnected ) {
170  return;
171  }
172 
173  startSocketTimer();
174  jobRunning = true;
175 
176  currentJob = queue.dequeue();
177  currentJob->doStart();
178 }
179 
180 void SessionPrivate::jobDone( KJob *job )
181 {
182  Q_UNUSED( job );
183  Q_ASSERT( job == currentJob );
184 
185  // If we're in disconnected state it's because we ended up
186  // here because the inactivity timer triggered, so no need to
187  // stop it (it is single shot)
188  if ( state!=Session::Disconnected ) {
189  stopSocketTimer();
190  }
191 
192  jobRunning = false;
193  currentJob = 0;
194  emit q->jobQueueSizeChanged( q->jobQueueSize() );
195  startNext();
196 }
197 
198 void SessionPrivate::jobDestroyed( QObject *job )
199 {
200  queue.removeAll( static_cast<KIMAP::Job*>( job ) );
201  if ( currentJob == job ) {
202  currentJob = 0;
203  }
204 }
205 
206 void SessionPrivate::responseReceived( const Message &response )
207 {
208  if ( logger && ( state == Session::Authenticated || state == Session::Selected ) ) {
209  logger->dataReceived( response.toString() );
210  }
211 
212  QByteArray tag;
213  QByteArray code;
214 
215  if ( response.content.size()>=1 ) {
216  tag = response.content[0].toString();
217  }
218 
219  if ( response.content.size()>=2 ) {
220  code = response.content[1].toString();
221  }
222 
223  switch ( state ) {
224  case Session::Disconnected:
225  if ( socketTimer.isActive() ) {
226  stopSocketTimer();
227  }
228  if ( code == "OK" ) {
229  setState( Session::NotAuthenticated );
230 
231  Message simplified = response;
232  simplified.content.removeFirst(); // Strip the tag
233  simplified.content.removeFirst(); // Strip the code
234  greeting = simplified.toString().trimmed(); // Save the server greeting
235 
236  startNext();
237  } else if ( code == "PREAUTH" ) {
238  setState( Session::Authenticated );
239 
240  Message simplified = response;
241  simplified.content.removeFirst(); // Strip the tag
242  simplified.content.removeFirst(); // Strip the code
243  greeting = simplified.toString().trimmed(); // Save the server greeting
244 
245  startNext();
246  } else {
247  thread->closeSocket();
248  }
249  return;
250  case Session::NotAuthenticated:
251  if ( code == "OK" && tag == authTag ) {
252  setState( Session::Authenticated );
253  }
254  break;
255  case Session::Authenticated:
256  if ( code == "OK" && tag == selectTag ) {
257  setState( Session::Selected );
258  currentMailBox = upcomingMailBox;
259  }
260  break;
261  case Session::Selected:
262  if ( ( code == "OK" && tag == closeTag ) ||
263  ( code != "OK" && tag == selectTag ) ) {
264  setState( Session::Authenticated );
265  currentMailBox = QByteArray();
266  } else if ( code == "OK" && tag == selectTag ) {
267  currentMailBox = upcomingMailBox;
268  }
269  break;
270  }
271 
272  if ( tag == authTag ) {
273  authTag.clear();
274  }
275  if ( tag == selectTag ) {
276  selectTag.clear();
277  }
278  if ( tag == closeTag ) {
279  closeTag.clear();
280  }
281 
282  // If a job is running forward it the response
283  if ( currentJob != 0 ) {
284  restartSocketTimer();
285  currentJob->handleResponse( response );
286  } else {
287  qWarning() << "A message was received from the server with no job to handle it:"
288  << response.toString()
289  << '(' + response.toString().toHex() + ')';
290  }
291 }
292 
293 void SessionPrivate::setState(Session::State s)
294 {
295  if ( s != state ) {
296  Session::State oldState = state;
297  state = s;
298  emit q->stateChanged( state, oldState );
299  }
300 }
301 
302 QByteArray SessionPrivate::sendCommand( const QByteArray &command, const QByteArray &args )
303 {
304  QByteArray tag = 'A' + QByteArray::number( ++tagCount ).rightJustified( 6, '0' );
305 
306  QByteArray payload = tag + ' ' + command;
307  if ( !args.isEmpty() ) {
308  payload += ' ' + args;
309  }
310 
311  sendData( payload );
312 
313  if ( command == "LOGIN" || command == "AUTHENTICATE" ) {
314  authTag = tag;
315  } else if ( command == "SELECT" || command == "EXAMINE" ) {
316  selectTag = tag;
317  upcomingMailBox = args;
318  upcomingMailBox.remove( 0, 1 );
319  upcomingMailBox = upcomingMailBox.left( upcomingMailBox.indexOf( '\"') );
320  upcomingMailBox = KIMAP::decodeImapFolderName( upcomingMailBox );
321  } else if ( command == "CLOSE" ) {
322  closeTag = tag;
323  }
324  return tag;
325 }
326 
327 void SessionPrivate::sendData( const QByteArray &data )
328 {
329  restartSocketTimer();
330 
331  if ( logger && ( state == Session::Authenticated || state == Session::Selected ) ) {
332  logger->dataSent( data );
333  }
334 
335  thread->sendData( data + "\r\n" );
336 }
337 
338 void SessionPrivate::socketConnected()
339 {
340  stopSocketTimer();
341  isSocketConnected = true;
342 
343  bool willUseSsl = false;
344  if ( !queue.isEmpty() ) {
345  KIMAP::LoginJob *login = qobject_cast<KIMAP::LoginJob*>( queue.first() );
346  if ( login ) {
347  willUseSsl = ( login->encryptionMode() == KIMAP::LoginJob::SslV2 ) ||
348  ( login->encryptionMode() == KIMAP::LoginJob::SslV3 ) ||
349  ( login->encryptionMode() == KIMAP::LoginJob::SslV3_1 ) ||
350  ( login->encryptionMode() == KIMAP::LoginJob::AnySslVersion );
351 
352  userName = login->userName();
353  }
354  }
355 
356  if ( state == Session::Disconnected && willUseSsl ) {
357  startNext();
358  } else {
359  startSocketTimer();
360  }
361 }
362 
363 void SessionPrivate::socketDisconnected()
364 {
365  if ( socketTimer.isActive() ) {
366  stopSocketTimer();
367  }
368 
369  if ( logger && ( state == Session::Authenticated || state == Session::Selected ) ) {
370  logger->disconnectionOccured();
371  }
372 
373  if ( state != Session::Disconnected ) {
374  setState( Session::Disconnected );
375  emit q->connectionLost();
376  } else {
377  emit q->connectionFailed();
378  }
379 
380  isSocketConnected = false;
381 
382  clearJobQueue();
383 }
384 
385 void SessionPrivate::socketActivity()
386 {
387  restartSocketTimer();
388 }
389 
390 void SessionPrivate::socketError()
391 {
392  if ( socketTimer.isActive() ) {
393  stopSocketTimer();
394  }
395 
396  if ( isSocketConnected ) {
397  thread->closeSocket();
398  } else {
399  emit q->connectionFailed();
400  emit q->connectionLost(); // KDE5: Remove this. We shouldn't emit connectionLost() if we weren't connected in the first place
401  clearJobQueue();
402  }
403 }
404 
405 void SessionPrivate::clearJobQueue()
406 {
407  if ( currentJob ) {
408  currentJob->connectionLost();
409  } else if ( !queue.isEmpty() ) {
410  currentJob = queue.takeFirst();
411  currentJob->connectionLost();
412  }
413 
414  QQueue<Job*> queueCopy = queue; // copy because jobDestroyed calls removeAll
415  qDeleteAll(queueCopy);
416  queue.clear();
417  emit q->jobQueueSizeChanged( 0 );
418 }
419 
420 void SessionPrivate::startSsl(const KTcpSocket::SslVersion &version)
421 {
422  thread->startSsl( version );
423 }
424 
425 QString Session::selectedMailBox() const
426 {
427  return QString::fromUtf8( d->currentMailBox );
428 }
429 
430 void SessionPrivate::onEncryptionNegotiationResult(bool isEncrypted, KTcpSocket::SslVersion version)
431 {
432  if ( isEncrypted ) {
433  sslVersion = version;
434  } else {
435  sslVersion = KTcpSocket::UnknownSslVersion;
436  }
437  emit encryptionNegotiationResult( isEncrypted );
438 }
439 
440 KTcpSocket::SslVersion SessionPrivate::negotiatedEncryption() const
441 {
442  return sslVersion;
443 }
444 
445 void SessionPrivate::setSocketTimeout( int ms )
446 {
447  bool timerActive = socketTimer.isActive();
448 
449  if ( timerActive ) {
450  stopSocketTimer();
451  }
452 
453  socketTimerInterval = ms;
454 
455  if ( timerActive ) {
456  startSocketTimer();
457  }
458 }
459 
460 int SessionPrivate::socketTimeout() const
461 {
462  return socketTimerInterval;
463 }
464 
465 void SessionPrivate::startSocketTimer()
466 {
467  if ( socketTimerInterval < 0 ) {
468  return;
469  }
470  Q_ASSERT( !socketTimer.isActive() );
471 
472  connect( &socketTimer, SIGNAL(timeout()),
473  this, SLOT(onSocketTimeout()) );
474 
475  socketTimer.setSingleShot( true );
476  socketTimer.start( socketTimerInterval );
477 }
478 
479 void SessionPrivate::stopSocketTimer()
480 {
481  if ( socketTimerInterval < 0 ) {
482  return;
483  }
484 
485  socketTimer.stop();
486 
487  disconnect( &socketTimer, SIGNAL(timeout()),
488  this, SLOT(onSocketTimeout()) );
489 }
490 
491 void SessionPrivate::restartSocketTimer()
492 {
493  if ( socketTimer.isActive() ) {
494  stopSocketTimer();
495  }
496  startSocketTimer();
497 }
498 
499 void SessionPrivate::onSocketTimeout()
500 {
501  kDebug() << "Socket timeout!";
502  thread->closeSocket();
503 }
504 
505 void Session::setTimeout( int timeout )
506 {
507  d->setSocketTimeout( timeout * 1000 );
508 }
509 
510 #include "moc_session.cpp"
511 #include "moc_session_p.cpp"
rfccodecs.h
This file is part of the IMAP support library and defines the RfcCodecs class.
KIMAP::SessionUiProxy
Interface to display communication errors and wait for user feedback.
Definition: sessionuiproxy.h:34
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 23:00:08 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KIMAP Library

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

kdepimlibs API Reference

Skip menu "kdepimlibs API Reference"
  • akonadi
  •   contact
  •   kmime
  •   socialutils
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kldap
  • kmbox
  • kmime
  • kpimidentities
  • kpimtextedit
  • kresources
  • ktnef
  • kxmlrpcclient
  • microblog

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal