MailTransport

transportmanager.cpp
1 /*
2  SPDX-FileCopyrightText: 2006-2007 Volker Krause <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "transportmanager.h"
8 #include "mailtransport_defs.h"
9 #include "transport.h"
10 #include "transport_p.h"
11 #include "transportjob.h"
12 #include "transporttype.h"
13 #include "transporttype_p.h"
14 #include "plugins/transportpluginmanager.h"
15 #include "plugins/transportabstractplugin.h"
16 #include "widgets/addtransportdialogng.h"
17 #include <MailTransport/TransportAbstractPlugin>
18 
19 #include <QApplication>
20 #include <QDBusConnection>
21 #include <QDBusConnectionInterface>
22 #include <QDBusServiceWatcher>
23 #include <QPointer>
24 #include <QRandomGenerator>
25 #include <QRegularExpression>
26 #include <QStringList>
27 
28 #include <KConfig>
29 #include <KConfigGroup>
30 #include "mailtransport_debug.h"
31 #include <KEMailSettings>
32 #include <KLocalizedString>
33 #include <KMessageBox>
34 #include <Kdelibs4ConfigMigrator>
35 
36 #include <KWallet>
37 
38 using namespace MailTransport;
39 using namespace KWallet;
40 
41 namespace MailTransport {
46 class TransportManagerPrivate
47 {
48 public:
49  TransportManagerPrivate(TransportManager *parent)
50  : q(parent)
51  {
52  }
53 
54  ~TransportManagerPrivate()
55  {
56  delete config;
57  qDeleteAll(transports);
58  }
59 
60  KConfig *config = nullptr;
61  QList<Transport *> transports;
63  bool myOwnChange;
64  bool appliedChange;
65  KWallet::Wallet *wallet = nullptr;
66  bool walletOpenFailed;
67  bool walletAsyncOpen;
68  int defaultTransportId;
69  bool isMainInstance;
70  QList<TransportJob *> walletQueue;
71  TransportManager *const q;
72 
73  void readConfig();
74  void writeConfig();
75  void fillTypes();
76  int createId() const;
77  void prepareWallet();
78  void validateDefault();
79  void migrateToWallet();
80  void updatePluginList();
81 
82  // Slots
83  void slotTransportsChanged();
84  void slotWalletOpened(bool success);
85  void dbusServiceUnregistered();
86  void jobResult(KJob *job);
87 };
88 }
89 
90 class StaticTransportManager : public TransportManager
91 {
92 public:
93  StaticTransportManager() : TransportManager()
94  {
95  }
96 };
97 
98 StaticTransportManager *sSelf = nullptr;
99 
100 static void destroyStaticTransportManager()
101 {
102  delete sSelf;
103 }
104 
106  : QObject()
107  , d(new TransportManagerPrivate(this))
108 {
109  Kdelibs4ConfigMigrator migrate(QStringLiteral("transportmanager"));
110  migrate.setConfigFiles(QStringList() << QStringLiteral("mailtransports"));
111  migrate.migrate();
112 
113  qAddPostRoutine(destroyStaticTransportManager);
114  d->myOwnChange = false;
115  d->appliedChange = false;
116  d->wallet = nullptr;
117  d->walletOpenFailed = false;
118  d->walletAsyncOpen = false;
119  d->defaultTransportId = -1;
120  d->config = new KConfig(QStringLiteral("mailtransports"));
121 
122  QDBusConnection::sessionBus().registerObject(DBUS_OBJECT_PATH, this,
125 
126  QDBusServiceWatcher *watcher
127  = new QDBusServiceWatcher(DBUS_SERVICE_NAME, QDBusConnection::sessionBus(),
129  connect(watcher, &QDBusServiceWatcher::serviceUnregistered, this, [this]() {
130  d->dbusServiceUnregistered();
131  });
132 
134  DBUS_INTERFACE_NAME, DBUS_CHANGE_SIGNAL,
135  this, SLOT(slotTransportsChanged()));
136 
137  d->isMainInstance = QDBusConnection::sessionBus().registerService(DBUS_SERVICE_NAME);
138 
139  d->fillTypes();
140 }
141 
143 {
144  qRemovePostRoutine(destroyStaticTransportManager);
145  delete d;
146 }
147 
149 {
150  if (!sSelf) {
151  sSelf = new StaticTransportManager;
152  sSelf->d->readConfig();
153  }
154  return sSelf;
155 }
156 
158 {
159  for (Transport *t : qAsConst(d->transports)) {
160  if (t->id() == id) {
161  return t;
162  }
163  }
164 
165  if (def || (id == 0 && d->defaultTransportId != id)) {
166  return transportById(d->defaultTransportId, false);
167  }
168  return nullptr;
169 }
170 
172 {
173  for (Transport *t : qAsConst(d->transports)) {
174  if (t->name() == name) {
175  return t;
176  }
177  }
178  if (def) {
179  return transportById(0, false);
180  }
181  return nullptr;
182 }
183 
185 {
186  return d->transports;
187 }
188 
190 {
191  return d->types;
192 }
193 
195 {
196  int id = d->createId();
197  Transport *t = new Transport(QString::number(id));
198  t->setId(id);
199  return t;
200 }
201 
203 {
204  if (d->transports.contains(transport)) {
205  qCDebug(MAILTRANSPORT_LOG) << "Already have this transport.";
206  return;
207  }
208 
209  qCDebug(MAILTRANSPORT_LOG) << "Added transport" << transport;
210  d->transports.append(transport);
211  d->validateDefault();
212  emitChangesCommitted();
213 }
214 
216 {
217  connect(job, &TransportJob::result, this, [this](KJob *job) {
218  d->jobResult(job);
219  });
220 
221  // check if the job is waiting for the wallet
222  if (!job->transport()->isComplete()) {
223  qCDebug(MAILTRANSPORT_LOG) << "job waits for wallet:" << job;
224  d->walletQueue << job;
226  return;
227  }
228 
229  job->start();
230 }
231 
233 {
234  KEMailSettings kes;
235  Transport *t = createTransport();
236  t->setName(i18n("Default Transport"));
237  t->setHost(kes.getSetting(KEMailSettings::OutServer));
238  if (t->isValid()) {
239  t->save();
240  addTransport(t);
241  } else {
242  qCWarning(MAILTRANSPORT_LOG) << "KEMailSettings does not contain a valid transport.";
243  }
244 }
245 
247 {
248  if (showCondition == IfNoTransportExists) {
249  if (!isEmpty()) {
250  return true;
251  }
252 
253  const int response = KMessageBox::messageBox(parent,
254  KMessageBox::WarningContinueCancel,
255  i18n("You must create an outgoing account before sending."),
256  i18n("Create Account Now?"),
257  KGuiItem(i18n("Create Account Now")));
258  if (response != KMessageBox::Continue) {
259  return false;
260  }
261  }
262 
264  const bool accepted = (dialog->exec() == QDialog::Accepted);
265  delete dialog;
266  return accepted;
267 }
268 
269 void TransportManager::initializeTransport(const QString &identifier, Transport *transport)
270 {
271  TransportAbstractPlugin *plugin = TransportPluginManager::self()->plugin(identifier);
272  if (plugin) {
273  plugin->initializeTransport(transport, identifier);
274  }
275 }
276 
278 {
279  TransportAbstractPlugin *plugin = TransportPluginManager::self()->plugin(identifier);
280  if (plugin) {
281  return plugin->configureTransport(identifier, transport, parent);
282  }
283  return false;
284 }
285 
287 {
288  Transport *t = transportById(transportId, false);
289  if (!t) {
290  return nullptr;
291  }
292  t = t->clone(); // Jobs delete their transports.
293  t->updatePasswordState();
294  TransportAbstractPlugin *plugin = TransportPluginManager::self()->plugin(t->identifier());
295  if (plugin) {
296  return plugin->createTransportJob(t, t->identifier());
297  }
298  Q_ASSERT(false);
299  return nullptr;
300 }
301 
303 {
304  bool ok = false;
305  Transport *t = nullptr;
306 
307  int transportId = transport.toInt(&ok);
308  if (ok) {
309  t = transportById(transportId);
310  }
311 
312  if (!t) {
313  t = transportByName(transport, false);
314  }
315 
316  if (t) {
317  return createTransportJob(t->id());
318  }
319 
320  return nullptr;
321 }
322 
324 {
325  return d->transports.isEmpty();
326 }
327 
329 {
330  QVector<int> rv;
331  rv.reserve(d->transports.count());
332  for (Transport *t : qAsConst(d->transports)) {
333  rv << t->id();
334  }
335  return rv;
336 }
337 
339 {
340  QStringList rv;
341  rv.reserve(d->transports.count());
342  for (Transport *t : qAsConst(d->transports)) {
343  rv << t->name();
344  }
345  return rv;
346 }
347 
349 {
350  Transport *t = transportById(d->defaultTransportId, false);
351  if (t) {
352  return t->name();
353  }
354  return QString();
355 }
356 
358 {
359  return d->defaultTransportId;
360 }
361 
363 {
364  if (id == d->defaultTransportId || !transportById(id, false)) {
365  return;
366  }
367  d->defaultTransportId = id;
368  d->writeConfig();
369 }
370 
371 void TransportManager::removePasswordFromWallet(int id)
372 {
373  Wallet *currentWallet = wallet();
374  if (currentWallet) {
375  currentWallet->removeEntry(QString::number(id));
376  }
377 }
378 
380 {
381  Transport *t = transportById(id, false);
382  if (!t) {
383  return;
384  }
385  auto plugin = MailTransport::TransportPluginManager::self()->plugin(t->identifier());
386  if (plugin) {
387  plugin->cleanUp(t);
388  }
389  Q_EMIT transportRemoved(t->id(), t->name());
390 
391  d->transports.removeAll(t);
392  d->validateDefault();
393  QString group = t->currentGroup();
394  if (t->storePassword()) {
395  Wallet *currentWallet = wallet();
396  if (currentWallet) {
397  currentWallet->removeEntry(QString::number(t->id()));
398  }
399  }
400  delete t;
401  d->config->deleteGroup(group);
402  d->writeConfig();
403 }
404 
405 void TransportManagerPrivate::readConfig()
406 {
407  QList<Transport *> oldTransports = transports;
408  transports.clear();
409 
410  QRegularExpression re(QStringLiteral("^Transport (.+)$"));
411  const QStringList groups = config->groupList().filter(re);
412  for (const QString &s : groups) {
413  QRegularExpressionMatch match = re.match(s);
414  if (!match.hasMatch()) {
415  continue;
416  }
417  Transport *t = nullptr;
418  // see if we happen to have that one already
419  for (Transport *old : oldTransports) {
420  if (old->currentGroup() == QLatin1String("Transport ") + match.captured(1)) {
421  qCDebug(MAILTRANSPORT_LOG) << "reloading existing transport:" << s;
422  t = old;
423  t->d->passwordNeedsUpdateFromWallet = true;
424  t->load();
425  oldTransports.removeAll(old);
426  break;
427  }
428  }
429 
430  if (!t) {
431  t = new Transport(match.captured(1));
432  }
433  if (t->id() <= 0) {
434  t->setId(createId());
435  t->save();
436  }
437  transports.append(t);
438  }
439 
440  qDeleteAll(oldTransports);
441  oldTransports.clear();
442 
443  // read default transport
444  KConfigGroup group(config, "General");
445  defaultTransportId = group.readEntry("default-transport", 0);
446  if (defaultTransportId == 0) {
447  // migrated default transport contains the name instead
448  QString name = group.readEntry("default-transport", QString());
449  if (!name.isEmpty()) {
450  Transport *t = q->transportByName(name, false);
451  if (t) {
452  defaultTransportId = t->id();
453  writeConfig();
454  }
455  }
456  }
457  validateDefault();
458  migrateToWallet();
459  q->loadPasswordsAsync();
460 }
461 
462 void TransportManagerPrivate::writeConfig()
463 {
464  KConfigGroup group(config, "General");
465  group.writeEntry("default-transport", defaultTransportId);
466  config->sync();
467  q->emitChangesCommitted();
468 }
469 
470 void TransportManagerPrivate::fillTypes()
471 {
472  Q_ASSERT(types.isEmpty());
473 
474  updatePluginList();
475  QObject::connect(MailTransport::TransportPluginManager::self(), &TransportPluginManager::updatePluginList, q, &TransportManager::updatePluginList);
476 }
477 
478 void TransportManagerPrivate::updatePluginList()
479 {
480  types.clear();
481  const QVector<MailTransport::TransportAbstractPlugin *> lstPlugins = MailTransport::TransportPluginManager::self()->pluginsList();
482  for (MailTransport::TransportAbstractPlugin *plugin : lstPlugins) {
483  if (plugin->names().isEmpty()) {
484  qCDebug(MAILTRANSPORT_LOG) << "Plugin " << plugin << " doesn't provide plugin";
485  }
486  const QVector<TransportAbstractPluginInfo> lstInfos = plugin->names();
487  for (const MailTransport::TransportAbstractPluginInfo &info : lstInfos) {
488  TransportType type;
489  type.d->mName = info.name;
490  type.d->mDescription = info.description;
491  type.d->mIdentifier = info.identifier;
492  type.d->mIsAkonadiResource = info.isAkonadi;
493  types << type;
494  }
495  }
496 }
497 
498 void TransportManager::updatePluginList()
499 {
500  d->updatePluginList();
501 }
502 
503 void TransportManager::emitChangesCommitted()
504 {
505  d->myOwnChange = true; // prevent us from reading our changes again
506  d->appliedChange = false; // but we have to read them at least once
509 }
510 
511 void TransportManagerPrivate::slotTransportsChanged()
512 {
513  if (myOwnChange && appliedChange) {
514  myOwnChange = false;
515  appliedChange = false;
516  return;
517  }
518 
519  qCDebug(MAILTRANSPORT_LOG);
520  config->reparseConfiguration();
521  // FIXME: this deletes existing transport objects!
522  readConfig();
523  appliedChange = true; // to prevent recursion
524  Q_EMIT q->transportsChanged();
525 }
526 
527 int TransportManagerPrivate::createId() const
528 {
529  QVector<int> usedIds;
530  usedIds.reserve(1 + transports.count());
531  for (Transport *t : qAsConst(transports)) {
532  usedIds << t->id();
533  }
534  usedIds << 0; // 0 is default for unknown
535  int newId;
536  do {
537  newId = QRandomGenerator::global()->generate();
538  } while (usedIds.contains(newId));
539  return newId;
540 }
541 
543 {
544  if (d->wallet && d->wallet->isOpen()) {
545  return d->wallet;
546  }
547 
548  if (!Wallet::isEnabled() || d->walletOpenFailed) {
549  return nullptr;
550  }
551 
552  WId window = 0;
553  if (qApp->activeWindow()) {
554  window = qApp->activeWindow()->winId();
555  } else if (!QApplication::topLevelWidgets().isEmpty()) {
556  window = qApp->topLevelWidgets().first()->winId();
557  }
558 
559  delete d->wallet;
560  d->wallet = Wallet::openWallet(Wallet::NetworkWallet(), window);
561 
562  if (!d->wallet) {
563  d->walletOpenFailed = true;
564  return nullptr;
565  }
566 
567  d->prepareWallet();
568  return d->wallet;
569 }
570 
571 void TransportManagerPrivate::prepareWallet()
572 {
573  if (!wallet) {
574  return;
575  }
576  if (!wallet->hasFolder(WALLET_FOLDER)) {
577  wallet->createFolder(WALLET_FOLDER);
578  }
579  wallet->setFolder(WALLET_FOLDER);
580 }
581 
583 {
584  for (Transport *t : qAsConst(d->transports)) {
585  t->readPassword();
586  }
587 
588  // flush the wallet queue
589  const QList<TransportJob *> copy = d->walletQueue;
590  d->walletQueue.clear();
591  for (TransportJob *job : copy) {
592  job->start();
593  }
594 
596 }
597 
599 {
600  qCDebug(MAILTRANSPORT_LOG);
601 
602  // check if there is anything to do at all
603  bool found = false;
604  for (Transport *t : qAsConst(d->transports)) {
605  if (!t->isComplete()) {
606  found = true;
607  break;
608  }
609  }
610  if (!found) {
611  return;
612  }
613 
614  // async wallet opening
615  if (!d->wallet && !d->walletOpenFailed) {
616  WId window = 0;
617  if (qApp->activeWindow()) {
618  window = qApp->activeWindow()->winId();
619  } else if (!QApplication::topLevelWidgets().isEmpty()) {
620  window = qApp->topLevelWidgets().first()->winId();
621  }
622 
623  d->wallet = Wallet::openWallet(Wallet::NetworkWallet(), window,
624  Wallet::Asynchronous);
625  if (d->wallet) {
626  connect(d->wallet, &KWallet::Wallet::walletOpened, this, [this](bool status) {
627  d->slotWalletOpened(status);
628  });
629  d->walletAsyncOpen = true;
630  } else {
631  d->walletOpenFailed = true;
632  loadPasswords();
633  }
634  return;
635  }
636  if (d->wallet && !d->walletAsyncOpen) {
637  loadPasswords();
638  }
639 }
640 
641 void TransportManagerPrivate::slotWalletOpened(bool success)
642 {
643  qCDebug(MAILTRANSPORT_LOG);
644  walletAsyncOpen = false;
645  if (!success) {
646  walletOpenFailed = true;
647  delete wallet;
648  wallet = nullptr;
649  } else {
650  prepareWallet();
651  }
652  q->loadPasswords();
653 }
654 
655 void TransportManagerPrivate::validateDefault()
656 {
657  if (!q->transportById(defaultTransportId, false)) {
658  if (q->isEmpty()) {
659  defaultTransportId = -1;
660  } else {
661  defaultTransportId = transports.constFirst()->id();
662  writeConfig();
663  }
664  }
665 }
666 
667 void TransportManagerPrivate::migrateToWallet()
668 {
669  // check if we tried this already
670  static bool firstRun = true;
671  if (!firstRun) {
672  return;
673  }
674  firstRun = false;
675 
676  // check if we are the main instance
677  if (!isMainInstance) {
678  return;
679  }
680 
681  // check if migration is needed
682  QStringList names;
683  for (Transport *t : qAsConst(transports)) {
684  if (t->needsWalletMigration()) {
685  names << t->name();
686  }
687  }
688  if (names.isEmpty()) {
689  return;
690  }
691 
692  // ask user if he wants to migrate
693  int result = KMessageBox::questionYesNoList(
694  nullptr,
695  i18n("The following mail transports store their passwords in an "
696  "unencrypted configuration file.\nFor security reasons, "
697  "please consider migrating these passwords to KWallet, the "
698  "KDE Wallet management tool,\nwhich stores sensitive data "
699  "for you in a strongly encrypted file.\n"
700  "Do you want to migrate your passwords to KWallet?"),
701  names, i18n("Question"),
702  KGuiItem(i18n("Migrate")), KGuiItem(i18n("Keep")),
703  QStringLiteral("WalletMigrate"));
704  if (result != KMessageBox::Yes) {
705  return;
706  }
707 
708  // perform migration
709  for (Transport *t : qAsConst(transports)) {
710  if (t->needsWalletMigration()) {
711  t->migrateToWallet();
712  }
713  }
714 }
715 
716 void TransportManagerPrivate::dbusServiceUnregistered()
717 {
718  QDBusConnection::sessionBus().registerService(DBUS_SERVICE_NAME);
719 }
720 
721 void TransportManagerPrivate::jobResult(KJob *job)
722 {
723  walletQueue.removeAll(static_cast<TransportJob *>(job));
724 }
725 
726 #include "moc_transportmanager.cpp"
A representation of a transport type.
Definition: transporttype.h:29
void clear()
void transportRemoved(int id, const QString &name)
Emitted when a transport is deleted.
QString captured(int nth) const const
QString name() const
Returns the i18n&#39;ed name of the transport type.
void loadPasswords()
Loads all passwords synchronously.
QRegularExpressionMatch match(const QString &subject, int offset, QRegularExpression::MatchType matchType, QRegularExpression::MatchOptions matchOptions) const const
virtual bool setFolder(const QString &f)
void addTransport(Transport *transport)
Adds the given transport.
quint32 generate()
Q_SCRIPTABLE void transportsChanged()
Emitted when transport settings have changed (by this or any other TransportManager instance)...
Q_SCRIPTABLE void setDefaultTransport(int id)
Sets the default transport.
void reserve(int alloc)
static TransportManager * self()
Returns the TransportManager instance.
KWallet::Wallet * wallet()
Returns a pointer to an open wallet if available, 0 otherwise.
MAILTRANSPORT_DEPRECATED void schedule(TransportJob *job)
Executes the given transport job.
Central transport management interface.
bool configureTransport(const QString &identifier, Transport *transport, QWidget *parent)
Open a configuration dialog for an existing transport.
Q_SCRIPTABLE int defaultTransportId() const
Returns the default transport identifier.
bool registerObject(const QString &path, QObject *object, QDBusConnection::RegisterOptions options)
void writeEntry(const QString &key, const QVariant &value, WriteConfigFlags pFlags=Normal)
QDBusConnection sessionBus()
virtual int removeEntry(const QString &key)
ButtonCode questionYesNoList(QWidget *parent, const QString &text, const QStringList &strlist, const QString &caption=QString(), const KGuiItem &buttonYes=KStandardGuiItem::yes(), const KGuiItem &buttonNo=KStandardGuiItem::no(), const QString &dontAskAgainName=QString(), Options options=Notify)
Q_SCRIPTABLE bool isEmpty() const
Returns true if there are no mail transports at all.
Q_SCRIPTABLE QStringList transportNames() const
Returns a list of transport names.
KSharedConfigPtr config()
void walletOpened(bool success)
void setConfigFiles(const QStringList &configFileNameList)
ShowCondition
Describes when to show the transport creation dialog.
void createDefaultTransport()
Tries to create a transport based on KEMailSettings.
QString identifier() const
Returns a plugin identifier.
void clear()
QString description() const
Returns a description of the transport type.
virtual bool hasFolder(const QString &f)
QString number(int n, int base)
bool contains(const T &value) const const
Q_SCRIPTABLE QString defaultTransportName() const
Returns the default transport name.
bool showTransportCreationDialog(QWidget *parent, ShowCondition showCondition=Always)
Shows a dialog for creating and configuring a new transport.
int toInt(bool *ok, int base) const const
bool isEmpty() const const
bool isEmpty() const const
void serviceUnregistered(const QString &serviceName)
~TransportManager() override
Destructor.
The TransportAbstractPluginInfo struct.
bool hasMatch() const const
WId winId() const const
Transport * createTransport() const
Creates a new, empty Transport object.
void passwordsChanged()
Emitted when passwords have been loaded from the wallet.
void reserve(int size)
bool isValid() const
Returns true if this transport is valid, ie.
Definition: transport.cpp:43
Abstract base class for all mail transport jobs.
Definition: transportjob.h:27
TransportManager()
Singleton class, the only instance resides in the static object sSelf.
Q_SCRIPTABLE QVector< int > transportIds() const
Returns a list of transport identifiers.
QList< Transport * > transports() const
Returns a list of all available transports.
QString i18n(const char *text, const TYPE &arg...)
void readConfig()
MAILTRANSPORT_DEPRECATED TransportJob * createTransportJob(int transportId)
Creates a mail transport job for the given transport identifier.
ButtonCode messageBox(QWidget *parent, DialogType type, const QString &text, const QString &caption=QString(), const KGuiItem &buttonYes=KStandardGuiItem::yes(), const KGuiItem &buttonNo=KStandardGuiItem::no(), const KGuiItem &buttonCancel=KStandardGuiItem::cancel(), const QString &dontShowAskAgainName=QString(), Options options=Notify)
Transport * transportByName(const QString &name, bool def=true) const
Returns the transport object with the given name.
bool isEmpty() const const
Q_SCRIPTABLE void changesCommitted()
Internal signal to synchronize all TransportManager instances.
Transport * clone() const
Returns a deep copy of this Transport object which will no longer be automatically updated...
Definition: transport.cpp:314
Internal file containing constant definitions etc.
virtual Q_SCRIPTABLE void start()=0
void loadPasswordsAsync()
Tries to load passwords asynchronously from KWallet if needed.
virtual bool createFolder(const QString &f)
Only show the transport creation dialog if no transport currently.
QString getSetting(KEMailSettings::Setting s) const
void updatePasswordState()
This function synchronizes the password of this transport with the password of the transport with the...
Definition: transport.cpp:85
void result(KJob *job)
bool connect(const QString &service, const QString &path, const QString &interface, const QString &name, QObject *receiver, const char *slot)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject * parent() const const
Represents the settings of a specific mail transport.
Definition: transport.h:27
Transport * transportById(int id, bool def=true) const
Returns the Transport object with the given id.
QRandomGenerator * global()
T readEntry(const QString &key, const T &aDefault) const
QWidgetList topLevelWidgets()
The TransportAbstractPlugin class.
Q_EMITQ_EMIT
bool registerService(const QString &serviceName)
Q_SCRIPTABLE void removeTransport(int id)
Deletes the specified transport.
Types types(const QStringList &mimeTypes)
TransportType::List types() const
Returns a list of all available transport types.
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Wed Sep 23 2020 23:19:20 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.