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

mailtransport

  • mailtransport
transportmanager.cpp
1 /*
2  Copyright (c) 2006 - 2007 Volker Krause <vkrause@kde.org>
3 
4  This library is free software; you can redistribute it and/or modify it
5  under the terms of the GNU Library General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or (at your
7  option) any later version.
8 
9  This library is distributed in the hope that it will be useful, but WITHOUT
10  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12  License for more details.
13 
14  You should have received a copy of the GNU Library General Public License
15  along with this library; see the file COPYING.LIB. If not, write to the
16  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  02110-1301, USA.
18 */
19 
20 #include "transportmanager.h"
21 #include "resourcesendjob_p.h"
22 #include "mailtransport_defs.h"
23 #include "sendmailjob.h"
24 #include "smtpjob.h"
25 #include "transport.h"
26 #include "transport_p.h"
27 #include "transportjob.h"
28 #include "transporttype.h"
29 #include "transporttype_p.h"
30 #include "addtransportdialog.h"
31 #include "transportconfigdialog.h"
32 #include "transportconfigwidget.h"
33 #include "sendmailconfigwidget.h"
34 #include "smtpconfigwidget.h"
35 
36 #include <QApplication>
37 #include <QtDBus/QDBusConnection>
38 #include <QtDBus/QDBusConnectionInterface>
39 #include <QtDBus/QDBusServiceWatcher>
40 #include <QPointer>
41 #include <QRegExp>
42 #include <QStringList>
43 
44 #include <KConfig>
45 #include <KConfigGroup>
46 #include <KDebug>
47 #include <KEMailSettings>
48 #include <KLocale>
49 #include <KMessageBox>
50 #include <KRandom>
51 #include <KUrl>
52 #include <KWallet/Wallet>
53 
54 #include <akonadi/agentinstance.h>
55 #include <akonadi/agentmanager.h>
56 
57 using namespace MailTransport;
58 using namespace KWallet;
59 
60 namespace MailTransport {
65 class TransportManagerPrivate
66 {
67  public:
68  TransportManagerPrivate( TransportManager *parent )
69  : q( parent )
70  {
71  }
72 
73  ~TransportManagerPrivate() {
74  delete config;
75  qDeleteAll( transports );
76  }
77 
78  KConfig *config;
79  QList<Transport *> transports;
80  TransportType::List types;
81  bool myOwnChange;
82  bool appliedChange;
83  KWallet::Wallet *wallet;
84  bool walletOpenFailed;
85  bool walletAsyncOpen;
86  int defaultTransportId;
87  bool isMainInstance;
88  QList<TransportJob *> walletQueue;
89  TransportManager *q;
90 
91  void readConfig();
92  void writeConfig();
93  void fillTypes();
94  int createId() const;
95  void prepareWallet();
96  void validateDefault();
97  void migrateToWallet();
98 
99  // Slots
100  void slotTransportsChanged();
101  void slotWalletOpened( bool success );
102  void dbusServiceUnregistered();
103  void agentTypeAdded( const Akonadi::AgentType &atype );
104  void agentTypeRemoved( const Akonadi::AgentType &atype );
105  void jobResult( KJob *job );
106 };
107 
108 }
109 
110 class StaticTransportManager : public TransportManager
111 {
112  public:
113  StaticTransportManager() : TransportManager() {}
114 };
115 
116 StaticTransportManager *sSelf = 0;
117 
118 static void destroyStaticTransportManager() {
119  delete sSelf;
120 }
121 
122 TransportManager::TransportManager()
123  : QObject(), d( new TransportManagerPrivate( this ) )
124 {
125  KGlobal::locale()->insertCatalog( QLatin1String( "libmailtransport" ) );
126  KGlobal::locale()->insertCatalog( QLatin1String( "libakonadi-kmime" ) );
127  qAddPostRoutine( destroyStaticTransportManager );
128  d->myOwnChange = false;
129  d->appliedChange = false;
130  d->wallet = 0;
131  d->walletOpenFailed = false;
132  d->walletAsyncOpen = false;
133  d->defaultTransportId = -1;
134  d->config = new KConfig( QLatin1String( "mailtransports" ) );
135 
136  QDBusConnection::sessionBus().registerObject( DBUS_OBJECT_PATH, this,
137  QDBusConnection::ExportScriptableSlots |
138  QDBusConnection::ExportScriptableSignals );
139 
140  QDBusServiceWatcher *watcher =
141  new QDBusServiceWatcher( DBUS_SERVICE_NAME, QDBusConnection::sessionBus(),
142  QDBusServiceWatcher::WatchForUnregistration, this );
143  connect( watcher, SIGNAL(serviceUnregistered(QString)),
144  SLOT(dbusServiceUnregistered()) );
145 
146  QDBusConnection::sessionBus().connect( QString(), QString(),
147  DBUS_INTERFACE_NAME, DBUS_CHANGE_SIGNAL,
148  this, SLOT(slotTransportsChanged()) );
149 
150  d->isMainInstance = QDBusConnection::sessionBus().registerService( DBUS_SERVICE_NAME );
151 
152  d->fillTypes();
153 }
154 
155 TransportManager::~TransportManager()
156 {
157  qRemovePostRoutine( destroyStaticTransportManager );
158  delete d;
159 }
160 
161 TransportManager *TransportManager::self()
162 {
163  if ( !sSelf ) {
164  sSelf = new StaticTransportManager;
165  sSelf->d->readConfig();
166  }
167  return sSelf;
168 }
169 
170 Transport *TransportManager::transportById( int id, bool def ) const
171 {
172  foreach ( Transport *t, d->transports ) {
173  if ( t->id() == id ) {
174  return t;
175  }
176  }
177 
178  if ( def || ( id == 0 && d->defaultTransportId != id ) ) {
179  return transportById( d->defaultTransportId, false );
180  }
181  return 0;
182 }
183 
184 Transport *TransportManager::transportByName( const QString &name, bool def ) const
185 {
186  foreach ( Transport *t, d->transports ) {
187  if ( t->name() == name ) {
188  return t;
189  }
190  }
191  if ( def ) {
192  return transportById( 0, false );
193  }
194  return 0;
195 }
196 
197 QList< Transport * > TransportManager::transports() const
198 {
199  return d->transports;
200 }
201 
202 TransportType::List TransportManager::types() const
203 {
204  return d->types;
205 }
206 
207 Transport *TransportManager::createTransport() const
208 {
209  int id = d->createId();
210  Transport *t = new Transport( QString::number( id ) );
211  t->setId( id );
212  return t;
213 }
214 
215 void TransportManager::addTransport( Transport *transport )
216 {
217  if ( d->transports.contains( transport ) ) {
218  kDebug() << "Already have this transport.";
219  return;
220  }
221 
222  kDebug() << "Added transport" << transport;
223  d->transports.append( transport );
224  d->validateDefault();
225  emitChangesCommitted();
226 }
227 
228 void TransportManager::schedule( TransportJob *job )
229 {
230  connect( job, SIGNAL(result(KJob*)), SLOT(jobResult(KJob*)) );
231 
232  // check if the job is waiting for the wallet
233  if ( !job->transport()->isComplete() ) {
234  kDebug() << "job waits for wallet:" << job;
235  d->walletQueue << job;
236  loadPasswordsAsync();
237  return;
238  }
239 
240  job->start();
241 }
242 
243 void TransportManager::createDefaultTransport()
244 {
245  KEMailSettings kes;
246  Transport *t = createTransport();
247  t->setName( i18n( "Default Transport" ) );
248  t->setHost( kes.getSetting( KEMailSettings::OutServer ) );
249  if ( t->isValid() ) {
250  t->writeConfig();
251  addTransport( t );
252  } else {
253  kWarning() << "KEMailSettings does not contain a valid transport.";
254  }
255 }
256 
257 bool TransportManager::showTransportCreationDialog( QWidget *parent,
258  ShowCondition showCondition )
259 {
260  if ( showCondition == IfNoTransportExists ) {
261  if ( !isEmpty() ) {
262  return true;
263  }
264 
265  const int response = KMessageBox::messageBox( parent,
266  KMessageBox::WarningContinueCancel,
267  i18n( "You must create an outgoing account before sending." ),
268  i18n( "Create Account Now?" ),
269  KGuiItem( i18n( "Create Account Now" ) ) );
270  if ( response != KMessageBox::Continue ) {
271  return false;
272  }
273  }
274 
275  QPointer<AddTransportDialog> dialog = new AddTransportDialog( parent );
276  const bool accepted = ( dialog->exec() == QDialog::Accepted );
277  delete dialog;
278  return accepted;
279 }
280 
281 bool TransportManager::configureTransport( Transport *transport, QWidget *parent )
282 {
283  if ( transport->type() == Transport::EnumType::Akonadi ) {
284  using namespace Akonadi;
285  AgentInstance instance = AgentManager::self()->instance( transport->host() );
286  if ( !instance.isValid() ) {
287  kWarning() << "Invalid resource instance" << transport->host();
288  }
289  instance.configure( parent ); // Async...
290  transport->writeConfig();
291  return true; // No way to know here if the user cancelled or not.
292  }
293 
294  QPointer<KDialog> dialog = new KDialog( parent );
295  TransportConfigWidget *configWidget = 0;
296  switch ( transport->type() ) {
297  case Transport::EnumType::SMTP:
298  {
299  configWidget = new SMTPConfigWidget( transport, dialog );
300  break;
301  }
302  case Transport::EnumType::Sendmail:
303  {
304  configWidget = new SendmailConfigWidget( transport, dialog );
305  break;
306  }
307  default:
308  {
309  Q_ASSERT( false );
310  delete dialog;
311  return false;
312  }
313  }
314  dialog->setMainWidget( configWidget );
315  dialog->setCaption( i18n( "Configure account" ) );
316  dialog->setButtons( KDialog::Ok | KDialog::Cancel );
317  bool okClicked = ( dialog->exec() == QDialog::Accepted );
318  if ( okClicked ) {
319  configWidget->apply(); // calls transport->writeConfig()
320  }
321  delete dialog;
322  return okClicked;
323 }
324 
325 TransportJob *TransportManager::createTransportJob( int transportId )
326 {
327  Transport *t = transportById( transportId, false );
328  if ( !t ) {
329  return 0;
330  }
331  t = t->clone(); // Jobs delete their transports.
332  t->updatePasswordState();
333  switch ( t->type() ) {
334  case Transport::EnumType::SMTP:
335  return new SmtpJob( t, this );
336  case Transport::EnumType::Sendmail:
337  return new SendmailJob( t, this );
338  case Transport::EnumType::Akonadi:
339  return new ResourceSendJob( t, this );
340  }
341  Q_ASSERT( false );
342  return 0;
343 }
344 
345 TransportJob *TransportManager::createTransportJob( const QString &transport )
346 {
347  bool ok = false;
348  Transport *t = 0;
349 
350  int transportId = transport.toInt( &ok );
351  if ( ok ) {
352  t = transportById( transportId );
353  }
354 
355  if ( !t ) {
356  t = transportByName( transport, false );
357  }
358 
359  if ( t ) {
360  return createTransportJob( t->id() );
361  }
362 
363  return 0;
364 }
365 
366 bool TransportManager::isEmpty() const
367 {
368  return d->transports.isEmpty();
369 }
370 
371 QList<int> TransportManager::transportIds() const
372 {
373  QList<int> rv;
374  foreach ( Transport *t, d->transports ) {
375  rv << t->id();
376  }
377  return rv;
378 }
379 
380 QStringList TransportManager::transportNames() const
381 {
382  QStringList rv;
383  foreach ( Transport *t, d->transports ) {
384  rv << t->name();
385  }
386  return rv;
387 }
388 
389 QString TransportManager::defaultTransportName() const
390 {
391  Transport *t = transportById( d->defaultTransportId, false );
392  if ( t ) {
393  return t->name();
394  }
395  return QString();
396 }
397 
398 int TransportManager::defaultTransportId() const
399 {
400  return d->defaultTransportId;
401 }
402 
403 void TransportManager::setDefaultTransport( int id )
404 {
405  if ( id == d->defaultTransportId || !transportById( id, false ) ) {
406  return;
407  }
408  d->defaultTransportId = id;
409  d->writeConfig();
410 }
411 
412 void TransportManager::removeTransport( int id )
413 {
414  Transport *t = transportById( id, false );
415  if ( !t ) {
416  return;
417  }
418  emit transportRemoved( t->id(), t->name() );
419 
420  // Kill the resource, if Akonadi-type transport.
421  if ( t->type() == Transport::EnumType::Akonadi ) {
422  using namespace Akonadi;
423  const AgentInstance instance = AgentManager::self()->instance( t->host() );
424  if ( !instance.isValid() ) {
425  kWarning() << "Could not find resource instance.";
426  }
427  AgentManager::self()->removeInstance( instance );
428  }
429 
430  d->transports.removeAll( t );
431  d->validateDefault();
432  QString group = t->currentGroup();
433  delete t;
434  d->config->deleteGroup( group );
435  d->writeConfig();
436 
437 }
438 
439 void TransportManagerPrivate::readConfig()
440 {
441  QList<Transport *> oldTransports = transports;
442  transports.clear();
443 
444  QRegExp re( QLatin1String( "^Transport (.+)$" ) );
445  QStringList groups = config->groupList().filter( re );
446  foreach ( const QString &s, groups ) {
447  re.indexIn( s );
448  Transport *t = 0;
449 
450  // see if we happen to have that one already
451  foreach ( Transport *old, oldTransports ) {
452  if ( old->currentGroup() == QLatin1String( "Transport " ) + re.cap( 1 ) ) {
453  kDebug() << "reloading existing transport:" << s;
454  t = old;
455  t->d->passwordNeedsUpdateFromWallet = true;
456  t->readConfig();
457  oldTransports.removeAll( old );
458  break;
459  }
460  }
461 
462  if ( !t ) {
463  t = new Transport( re.cap( 1 ) );
464  }
465  if ( t->id() <= 0 ) {
466  t->setId( createId() );
467  t->writeConfig();
468  }
469  transports.append( t );
470  }
471 
472  qDeleteAll( oldTransports );
473  oldTransports.clear();
474 
475  // read default transport
476  KConfigGroup group( config, "General" );
477  defaultTransportId = group.readEntry( "default-transport", 0 );
478  if ( defaultTransportId == 0 ) {
479  // migrated default transport contains the name instead
480  QString name = group.readEntry( "default-transport", QString() );
481  if ( !name.isEmpty() ) {
482  Transport *t = q->transportByName( name, false );
483  if ( t ) {
484  defaultTransportId = t->id();
485  writeConfig();
486  }
487  }
488  }
489  validateDefault();
490  migrateToWallet();
491  q->loadPasswordsAsync();
492 }
493 
494 void TransportManagerPrivate::writeConfig()
495 {
496  KConfigGroup group( config, "General" );
497  group.writeEntry( "default-transport", defaultTransportId );
498  config->sync();
499  q->emitChangesCommitted();
500 }
501 
502 void TransportManagerPrivate::fillTypes()
503 {
504  Q_ASSERT( types.isEmpty() );
505 
506  // SMTP.
507  {
508  TransportType type;
509  type.d->mType = Transport::EnumType::SMTP;
510  type.d->mName = i18nc( "@option SMTP transport", "SMTP" );
511  type.d->mDescription = i18n( "An SMTP server on the Internet" );
512  types << type;
513  }
514 
515  // Sendmail.
516  {
517  TransportType type;
518  type.d->mType = Transport::EnumType::Sendmail;
519  type.d->mName = i18nc( "@option sendmail transport", "Sendmail" );
520  type.d->mDescription = i18n( "A local sendmail installation" );
521  types << type;
522  }
523 
524  // All Akonadi resources with MailTransport capability.
525  {
526  using namespace Akonadi;
527  foreach ( const AgentType &atype, AgentManager::self()->types() ) {
528  // TODO probably the string "MailTransport" should be #defined somewhere
529  // and used like that in the resources (?)
530  if ( atype.capabilities().contains( QLatin1String( "MailTransport" ) ) ) {
531  TransportType type;
532  type.d->mType = Transport::EnumType::Akonadi;
533  type.d->mAgentType = atype;
534  type.d->mName = atype.name();
535  type.d->mDescription = atype.description();
536  types << type;
537  kDebug() << "Found Akonadi type" << atype.name();
538  }
539  }
540 
541  // Watch for appearing and disappearing types.
542  QObject::connect( AgentManager::self(), SIGNAL(typeAdded(Akonadi::AgentType)),
543  q, SLOT(agentTypeAdded(Akonadi::AgentType)) );
544  QObject::connect( AgentManager::self(), SIGNAL(typeRemoved(Akonadi::AgentType)),
545  q, SLOT(agentTypeRemoved(Akonadi::AgentType)) );
546  }
547 
548  kDebug() << "Have SMTP, Sendmail, and" << types.count() - 2 << "Akonadi types.";
549 }
550 
551 void TransportManager::emitChangesCommitted()
552 {
553  d->myOwnChange = true; // prevent us from reading our changes again
554  d->appliedChange = false; // but we have to read them at least once
555  emit transportsChanged();
556  emit changesCommitted();
557 }
558 
559 void TransportManagerPrivate::slotTransportsChanged()
560 {
561  if ( myOwnChange && appliedChange ) {
562  myOwnChange = false;
563  appliedChange = false;
564  return;
565  }
566 
567  kDebug();
568  config->reparseConfiguration();
569  // FIXME: this deletes existing transport objects!
570  readConfig();
571  appliedChange = true; // to prevent recursion
572  emit q->transportsChanged();
573 }
574 
575 int TransportManagerPrivate::createId() const
576 {
577  QList<int> usedIds;
578  foreach ( Transport *t, transports ) {
579  usedIds << t->id();
580  }
581  usedIds << 0; // 0 is default for unknown
582  int newId;
583  do {
584  newId = KRandom::random();
585  } while ( usedIds.contains( newId ) );
586  return newId;
587 }
588 
589 KWallet::Wallet * TransportManager::wallet()
590 {
591  if ( d->wallet && d->wallet->isOpen() ) {
592  return d->wallet;
593  }
594 
595  if ( !Wallet::isEnabled() || d->walletOpenFailed ) {
596  return 0;
597  }
598 
599  WId window = 0;
600  if ( qApp->activeWindow() ) {
601  window = qApp->activeWindow()->winId();
602  } else if ( !QApplication::topLevelWidgets().isEmpty() ) {
603  window = qApp->topLevelWidgets().first()->winId();
604  }
605 
606  delete d->wallet;
607  d->wallet = Wallet::openWallet( Wallet::NetworkWallet(), window );
608 
609  if ( !d->wallet ) {
610  d->walletOpenFailed = true;
611  return 0;
612  }
613 
614  d->prepareWallet();
615  return d->wallet;
616 }
617 
618 void TransportManagerPrivate::prepareWallet()
619 {
620  if ( !wallet ) {
621  return;
622  }
623  if ( !wallet->hasFolder( WALLET_FOLDER ) ) {
624  wallet->createFolder( WALLET_FOLDER );
625  }
626  wallet->setFolder( WALLET_FOLDER );
627 }
628 
629 void TransportManager::loadPasswords()
630 {
631  foreach ( Transport *t, d->transports ) {
632  t->readPassword();
633  }
634 
635  // flush the wallet queue
636  const QList<TransportJob*> copy = d->walletQueue;
637  d->walletQueue.clear();
638  foreach ( TransportJob *job, copy ) {
639  job->start();
640  }
641 
642  emit passwordsChanged();
643 }
644 
645 void TransportManager::loadPasswordsAsync()
646 {
647  kDebug();
648 
649  // check if there is anything to do at all
650  bool found = false;
651  foreach ( Transport *t, d->transports ) {
652  if ( !t->isComplete() ) {
653  found = true;
654  break;
655  }
656  }
657  if ( !found ) {
658  return;
659  }
660 
661  // async wallet opening
662  if ( !d->wallet && !d->walletOpenFailed ) {
663  WId window = 0;
664  if ( qApp->activeWindow() ) {
665  window = qApp->activeWindow()->winId();
666  } else if ( !QApplication::topLevelWidgets().isEmpty() ) {
667  window = qApp->topLevelWidgets().first()->winId();
668  }
669 
670  d->wallet = Wallet::openWallet( Wallet::NetworkWallet(), window,
671  Wallet::Asynchronous );
672  if ( d->wallet ) {
673  connect( d->wallet, SIGNAL(walletOpened(bool)), SLOT(slotWalletOpened(bool)) );
674  d->walletAsyncOpen = true;
675  } else {
676  d->walletOpenFailed = true;
677  loadPasswords();
678  }
679  return;
680  }
681  if ( d->wallet && !d->walletAsyncOpen ) {
682  loadPasswords();
683  }
684 }
685 
686 void TransportManagerPrivate::slotWalletOpened( bool success )
687 {
688  kDebug();
689  walletAsyncOpen = false;
690  if ( !success ) {
691  walletOpenFailed = true;
692  delete wallet;
693  wallet = 0;
694  } else {
695  prepareWallet();
696  }
697  q->loadPasswords();
698 }
699 
700 void TransportManagerPrivate::validateDefault()
701 {
702  if ( !q->transportById( defaultTransportId, false ) ) {
703  if ( q->isEmpty() ) {
704  defaultTransportId = -1;
705  } else {
706  defaultTransportId = transports.first()->id();
707  writeConfig();
708  }
709  }
710 }
711 
712 void TransportManagerPrivate::migrateToWallet()
713 {
714  // check if we tried this already
715  static bool firstRun = true;
716  if ( !firstRun ) {
717  return;
718  }
719  firstRun = false;
720 
721  // check if we are the main instance
722  if ( !isMainInstance ) {
723  return;
724  }
725 
726  // check if migration is needed
727  QStringList names;
728  foreach ( Transport *t, transports ) {
729  if ( t->needsWalletMigration() ) {
730  names << t->name();
731  }
732  }
733  if ( names.isEmpty() ) {
734  return;
735  }
736 
737  // ask user if he wants to migrate
738  int result = KMessageBox::questionYesNoList(
739  0,
740  i18n( "The following mail transports store their passwords in an "
741  "unencrypted configuration file.\nFor security reasons, "
742  "please consider migrating these passwords to KWallet, the "
743  "KDE Wallet management tool,\nwhich stores sensitive data "
744  "for you in a strongly encrypted file.\n"
745  "Do you want to migrate your passwords to KWallet?" ),
746  names, i18n( "Question" ),
747  KGuiItem( i18n( "Migrate" ) ), KGuiItem( i18n( "Keep" ) ),
748  QString::fromLatin1( "WalletMigrate" ) );
749  if ( result != KMessageBox::Yes ) {
750  return;
751  }
752 
753  // perform migration
754  foreach ( Transport *t, transports ) {
755  if ( t->needsWalletMigration() ) {
756  t->migrateToWallet();
757  }
758  }
759 }
760 
761 void TransportManagerPrivate::dbusServiceUnregistered()
762 {
763  QDBusConnection::sessionBus().registerService( DBUS_SERVICE_NAME );
764 }
765 
766 void TransportManagerPrivate::agentTypeAdded( const Akonadi::AgentType &atype )
767 {
768  using namespace Akonadi;
769  if ( atype.capabilities().contains( QLatin1String( "MailTransport" ) ) ) {
770  TransportType type;
771  type.d->mType = Transport::EnumType::Akonadi;
772  type.d->mAgentType = atype;
773  type.d->mName = atype.name();
774  type.d->mDescription = atype.description();
775  types << type;
776  kDebug() << "Added new Akonadi type" << atype.name();
777  }
778 }
779 
780 void TransportManagerPrivate::agentTypeRemoved( const Akonadi::AgentType &atype )
781 {
782  using namespace Akonadi;
783  foreach ( const TransportType &type, types ) {
784  if ( type.type() == Transport::EnumType::Akonadi &&
785  type.agentType() == atype ) {
786  types.removeAll( type );
787  kDebug() << "Removed Akonadi type" << atype.name();
788  }
789  }
790 }
791 
792 void TransportManagerPrivate::jobResult( KJob *job )
793 {
794  walletQueue.removeAll( static_cast<TransportJob*>( job ) );
795 }
796 
797 #include "moc_transportmanager.cpp"
This file is part of the KDE documentation.
Documentation copyright © 1996-2013 The KDE developers.
Generated on Fri Mar 8 2013 21:49:19 by doxygen 1.8.3.1 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

mailtransport

Skip menu "mailtransport"
  • Main Page
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • Related Pages

kdepimlibs-4.10.0 API Reference

Skip menu "kdepimlibs-4.10.0 API Reference"
  • akonadi
  •   contact
  •   kmime
  •   socialutils
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
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