20#include "transportmanager.h"
21#include "resourcesendjob_p.h"
23#include "sendmailjob.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"
36#include <QApplication>
37#include <QtDBus/QDBusConnection>
38#include <QtDBus/QDBusConnectionInterface>
39#include <QtDBus/QDBusServiceWatcher>
45#include <KConfigGroup>
47#include <KEMailSettings>
49#include <KLocalizedString>
53#include <KWallet/Wallet>
55#include <akonadi/agentinstance.h>
56#include <akonadi/agentmanager.h>
58using namespace MailTransport;
59using namespace KWallet;
61namespace MailTransport {
66class TransportManagerPrivate
74 ~TransportManagerPrivate() {
76 qDeleteAll( transports );
80 QList<Transport *> transports;
84 KWallet::Wallet *wallet;
85 bool walletOpenFailed;
87 int defaultTransportId;
89 QList<TransportJob *> walletQueue;
97 void validateDefault();
98 void migrateToWallet();
101 void slotTransportsChanged();
102 void slotWalletOpened(
bool success );
103 void dbusServiceUnregistered();
104 void agentTypeAdded(
const Akonadi::AgentType &atype );
105 void agentTypeRemoved(
const Akonadi::AgentType &atype );
106 void jobResult( KJob *job );
117StaticTransportManager *sSelf = 0;
119static void destroyStaticTransportManager() {
124 : QObject(), d( new TransportManagerPrivate( this ) )
126 KGlobal::locale()->insertCatalog( QLatin1String(
"libmailtransport" ) );
127 KGlobal::locale()->insertCatalog( QLatin1String(
"libakonadi-kmime" ) );
128 qAddPostRoutine( destroyStaticTransportManager );
129 d->myOwnChange =
false;
130 d->appliedChange =
false;
132 d->walletOpenFailed =
false;
133 d->walletAsyncOpen =
false;
134 d->defaultTransportId = -1;
135 d->config =
new KConfig( QLatin1String(
"mailtransports" ) );
137 QDBusConnection::sessionBus().registerObject( DBUS_OBJECT_PATH,
this,
138 QDBusConnection::ExportScriptableSlots |
139 QDBusConnection::ExportScriptableSignals );
141 QDBusServiceWatcher *watcher =
142 new QDBusServiceWatcher( DBUS_SERVICE_NAME, QDBusConnection::sessionBus(),
143 QDBusServiceWatcher::WatchForUnregistration,
this );
144 connect( watcher, SIGNAL(serviceUnregistered(QString)),
145 SLOT(dbusServiceUnregistered()) );
147 QDBusConnection::sessionBus().connect( QString(), QString(),
148 DBUS_INTERFACE_NAME, DBUS_CHANGE_SIGNAL,
149 this, SLOT(slotTransportsChanged()) );
151 d->isMainInstance = QDBusConnection::sessionBus().registerService( DBUS_SERVICE_NAME );
158 qRemovePostRoutine( destroyStaticTransportManager );
165 sSelf =
new StaticTransportManager;
166 sSelf->d->readConfig();
173 foreach (
Transport *t, d->transports ) {
174 if ( t->id() ==
id ) {
179 if ( def || (
id == 0 && d->defaultTransportId !=
id ) ) {
187 foreach (
Transport *t, d->transports ) {
188 if ( t->name() == name ) {
200 return d->transports;
210 int id = d->createId();
218 if ( d->transports.contains( transport ) ) {
219 kDebug() <<
"Already have this transport.";
223 kDebug() <<
"Added transport" << transport;
224 d->transports.append( transport );
225 d->validateDefault();
226 emitChangesCommitted();
231 connect( job, SIGNAL(result(KJob*)), SLOT(jobResult(KJob*)) );
235 kDebug() <<
"job waits for wallet:" << job;
236 d->walletQueue << job;
248 t->setName( i18n(
"Default Transport" ) );
249 t->setHost( kes.getSetting( KEMailSettings::OutServer ) );
254 kWarning() <<
"KEMailSettings does not contain a valid transport.";
266 const int response = KMessageBox::messageBox( parent,
267 KMessageBox::WarningContinueCancel,
268 i18n(
"You must create an outgoing account before sending." ),
269 i18n(
"Create Account Now?" ),
270 KGuiItem( i18n(
"Create Account Now" ) ) );
271 if ( response != KMessageBox::Continue ) {
277 const bool accepted = ( dialog->exec() == QDialog::Accepted );
284 if ( transport->type() == Transport::EnumType::Akonadi ) {
285 using namespace Akonadi;
286 AgentInstance instance = AgentManager::self()->instance( transport->host() );
287 if ( !instance.isValid() ) {
288 kWarning() <<
"Invalid resource instance" << transport->host();
290 instance.configure( parent );
291 transport->writeConfig();
295 QPointer<TransportConfigDialog> transportConfigDialog =
297 transportConfigDialog->setCaption( i18n(
"Configure account" ) );
298 bool okClicked = ( transportConfigDialog->exec() == QDialog::Accepted );
299 delete transportConfigDialog;
311 switch ( t->type() ) {
312 case Transport::EnumType::SMTP:
314 case Transport::EnumType::Sendmail:
316 case Transport::EnumType::Akonadi:
328 int transportId = transport.toInt( &ok );
346 return d->transports.isEmpty();
352 foreach (
Transport *t, d->transports ) {
361 foreach (
Transport *t, d->transports ) {
378 return d->defaultTransportId;
383 if (
id == d->defaultTransportId || !
transportById(
id,
false ) ) {
386 d->defaultTransportId = id;
399 if ( t->type() == Transport::EnumType::Akonadi ) {
400 using namespace Akonadi;
401 const AgentInstance instance = AgentManager::self()->instance( t->host() );
402 if ( !instance.isValid() ) {
403 kWarning() <<
"Could not find resource instance.";
405 AgentManager::self()->removeInstance( instance );
408 d->transports.removeAll( t );
409 d->validateDefault();
410 QString group = t->currentGroup();
411 if (t->storePassword()) {
412 Wallet *currentWallet =
wallet();
413 if ( currentWallet ) {
414 currentWallet->removeEntry( QString::number(t->id()) );
418 d->config->deleteGroup( group );
423void TransportManagerPrivate::readConfig()
425 QList<Transport *> oldTransports = transports;
428 QRegExp re( QLatin1String(
"^Transport (.+)$" ) );
429 QStringList groups = config->groupList().filter( re );
430 foreach (
const QString &s, groups ) {
431 if (re.indexIn( s ) == -1)
436 foreach (
Transport *old, oldTransports ) {
437 if ( old->currentGroup() == QLatin1String(
"Transport " ) + re.cap( 1 ) ) {
438 kDebug() <<
"reloading existing transport:" << s;
440 t->d->passwordNeedsUpdateFromWallet =
true;
442 oldTransports.removeAll( old );
450 if ( t->id() <= 0 ) {
451 t->setId( createId() );
454 transports.append( t );
457 qDeleteAll( oldTransports );
458 oldTransports.clear();
461 KConfigGroup group( config,
"General" );
462 defaultTransportId = group.readEntry(
"default-transport", 0 );
463 if ( defaultTransportId == 0 ) {
465 QString name = group.readEntry(
"default-transport", QString() );
466 if ( !name.isEmpty() ) {
469 defaultTransportId = t->id();
479void TransportManagerPrivate::writeConfig()
481 KConfigGroup group( config,
"General" );
482 group.writeEntry(
"default-transport", defaultTransportId );
484 q->emitChangesCommitted();
487void TransportManagerPrivate::fillTypes()
489 Q_ASSERT( types.isEmpty() );
494 type.d->mType = Transport::EnumType::SMTP;
495 type.d->mName = i18nc(
"@option SMTP transport",
"SMTP" );
496 type.d->mDescription = i18n(
"An SMTP server on the Internet" );
503 type.d->mType = Transport::EnumType::Sendmail;
504 type.d->mName = i18nc(
"@option sendmail transport",
"Sendmail" );
505 type.d->mDescription = i18n(
"A local sendmail installation" );
511 using namespace Akonadi;
512 foreach (
const AgentType &atype, AgentManager::self()->types() ) {
515 if ( atype.capabilities().contains( QLatin1String(
"MailTransport" ) ) ) {
517 type.d->mType = Transport::EnumType::Akonadi;
518 type.d->mAgentType = atype;
519 type.d->mName = atype.name();
520 type.d->mDescription = atype.description();
522 kDebug() <<
"Found Akonadi type" << atype.name();
527 QObject::connect( AgentManager::self(), SIGNAL(typeAdded(Akonadi::AgentType)),
528 q, SLOT(agentTypeAdded(Akonadi::AgentType)) );
529 QObject::connect( AgentManager::self(), SIGNAL(typeRemoved(Akonadi::AgentType)),
530 q, SLOT(agentTypeRemoved(Akonadi::AgentType)) );
533 kDebug() <<
"Have SMTP, Sendmail, and" << types.count() - 2 <<
"Akonadi types.";
536void TransportManager::emitChangesCommitted()
538 d->myOwnChange =
true;
539 d->appliedChange =
false;
544void TransportManagerPrivate::slotTransportsChanged()
546 if ( myOwnChange && appliedChange ) {
548 appliedChange =
false;
553 config->reparseConfiguration();
556 appliedChange =
true;
560int TransportManagerPrivate::createId()
const
569 newId = KRandom::random();
570 }
while ( usedIds.contains( newId ) );
576 if ( d->wallet && d->wallet->isOpen() ) {
580 if ( !Wallet::isEnabled() || d->walletOpenFailed ) {
585 if ( qApp->activeWindow() ) {
586 window = qApp->activeWindow()->winId();
587 }
else if ( !QApplication::topLevelWidgets().
isEmpty() ) {
588 window = qApp->topLevelWidgets().first()->winId();
592 d->wallet = Wallet::openWallet( Wallet::NetworkWallet(), window );
595 d->walletOpenFailed =
true;
603void TransportManagerPrivate::prepareWallet()
608 if ( !wallet->hasFolder( WALLET_FOLDER ) ) {
609 wallet->createFolder( WALLET_FOLDER );
611 wallet->setFolder( WALLET_FOLDER );
616 foreach (
Transport *t, d->transports ) {
621 const QList<TransportJob*> copy = d->walletQueue;
622 d->walletQueue.clear();
636 foreach (
Transport *t, d->transports ) {
647 if ( !d->wallet && !d->walletOpenFailed ) {
649 if ( qApp->activeWindow() ) {
650 window = qApp->activeWindow()->winId();
651 }
else if ( !QApplication::topLevelWidgets().
isEmpty() ) {
652 window = qApp->topLevelWidgets().first()->winId();
655 d->wallet = Wallet::openWallet( Wallet::NetworkWallet(), window,
656 Wallet::Asynchronous );
658 connect( d->wallet, SIGNAL(walletOpened(
bool)), SLOT(slotWalletOpened(
bool)) );
659 d->walletAsyncOpen =
true;
661 d->walletOpenFailed =
true;
666 if ( d->wallet && !d->walletAsyncOpen ) {
671void TransportManagerPrivate::slotWalletOpened(
bool success )
674 walletAsyncOpen =
false;
676 walletOpenFailed =
true;
685void TransportManagerPrivate::validateDefault()
689 defaultTransportId = -1;
691 defaultTransportId = transports.first()->id();
697void TransportManagerPrivate::migrateToWallet()
700 static bool firstRun =
true;
707 if ( !isMainInstance ) {
718 if ( names.isEmpty() ) {
723 int result = KMessageBox::questionYesNoList(
725 i18n(
"The following mail transports store their passwords in an "
726 "unencrypted configuration file.\nFor security reasons, "
727 "please consider migrating these passwords to KWallet, the "
728 "KDE Wallet management tool,\nwhich stores sensitive data "
729 "for you in a strongly encrypted file.\n"
730 "Do you want to migrate your passwords to KWallet?" ),
731 names, i18n(
"Question" ),
732 KGuiItem( i18n(
"Migrate" ) ), KGuiItem( i18n(
"Keep" ) ),
733 QString::fromLatin1(
"WalletMigrate" ) );
734 if ( result != KMessageBox::Yes ) {
746void TransportManagerPrivate::dbusServiceUnregistered()
748 QDBusConnection::sessionBus().registerService( DBUS_SERVICE_NAME );
751void TransportManagerPrivate::agentTypeAdded(
const Akonadi::AgentType &atype )
753 using namespace Akonadi;
754 if ( atype.capabilities().contains( QLatin1String(
"MailTransport" ) ) ) {
756 type.d->mType = Transport::EnumType::Akonadi;
757 type.d->mAgentType = atype;
758 type.d->mName = atype.
name();
761 kDebug() <<
"Added new Akonadi type" << atype.
name();
765void TransportManagerPrivate::agentTypeRemoved(
const Akonadi::AgentType &atype )
767 using namespace Akonadi;
769 if ( type.
type() == Transport::EnumType::Akonadi &&
771 types.removeAll( type );
772 kDebug() <<
"Removed Akonadi type" << atype.name();
777void TransportManagerPrivate::jobResult( KJob *job )
779 walletQueue.removeAll(
static_cast<TransportJob*
>( job ) );
782#include "moc_transportmanager.cpp"
Mail transport job for an Akonadi resource-based transport.
Mail transport job for sendmail.
Mail transport job for SMTP.
Configuration dialog for a mail transport.
Abstract base class for all mail transport jobs.
Transport * transport() const
Returns the Transport object containing the mail transport settings.
virtual void start()
Starts this job.
Central transport management interface.
MAILTRANSPORT_DEPRECATED void schedule(TransportJob *job)
Executes the given transport job.
void createDefaultTransport()
Tries to create a transport based on KEMailSettings.
Transport * transportById(int id, bool def=true) const
Returns the Transport object with the given id.
Q_SCRIPTABLE QStringList transportNames() const
Returns a list of transport names.
virtual ~TransportManager()
Destructor.
void transportRemoved(int id, const QString &name)
Emitted when a transport is deleted.
Q_SCRIPTABLE void removeTransport(int id)
Deletes the specified transport.
Q_SCRIPTABLE void changesCommitted()
Internal signal to synchronize all TransportManager instances.
bool configureTransport(Transport *transport, QWidget *parent)
Open a configuration dialog for an existing transport.
void loadPasswordsAsync()
Tries to load passwords asynchronously from KWallet if needed.
void passwordsChanged()
Emitted when passwords have been loaded from the wallet.
TransportType::List types() const
Returns a list of all available transport types.
Q_SCRIPTABLE void setDefaultTransport(int id)
Sets the default transport.
void loadPasswords()
Loads all passwords synchronously.
Q_SCRIPTABLE QString defaultTransportName() const
Returns the default transport name.
Q_SCRIPTABLE void transportsChanged()
Emitted when transport settings have changed (by this or any other TransportManager instance).
void addTransport(Transport *transport)
Adds the given transport.
MAILTRANSPORT_DEPRECATED TransportJob * createTransportJob(int transportId)
Creates a mail transport job for the given transport identifier.
static TransportManager * self()
Returns the TransportManager instance.
Transport * transportByName(const QString &name, bool def=true) const
Returns the transport object with the given name.
bool showTransportCreationDialog(QWidget *parent, ShowCondition showCondition=Always)
Shows a dialog for creating and configuring a new transport.
ShowCondition
Describes when to show the transport creation dialog.
@ IfNoTransportExists
Only show the transport creation dialog if no transport currently exists.
KWallet::Wallet * wallet()
Returns a pointer to an open wallet if available, 0 otherwise.
Q_SCRIPTABLE bool isEmpty() const
Returns true if there are no mail transports at all.
Q_SCRIPTABLE int defaultTransportId() const
Returns the default transport identifier.
QList< Transport * > transports() const
Returns a list of all available transports.
Q_SCRIPTABLE QList< int > transportIds() const
Returns a list of transport identifiers.
TransportManager()
Singleton class, the only instance resides in the static object sSelf.
Transport * createTransport() const
Creates a new, empty Transport object.
A representation of a transport type.
TransportBase::EnumType::type type() const
QString description() const
Returns a description of the transport type.
Akonadi::AgentType agentType() const
Returns the corresponding Akonadi::AgentType that this transport type represents.
QList< TransportType > List
Describes a list of transport types.
QString name() const
Returns the i18n'ed name of the transport type.
Represents the settings of a specific mail transport.
void migrateToWallet()
Try to migrate the password from the config file to the wallet.
bool needsWalletMigration() const
Returns true if the password was not stored in the wallet.
bool isComplete() const
Returns true if all settings have been loaded.
Transport * clone() const
Returns a deep copy of this Transport object which will no longer be automatically updated.
void updatePasswordState()
This function synchronizes the password of this transport with the password of the transport with the...
bool isValid() const
Returns true if this transport is valid, ie.
Internal file containing constant definitions etc.