20 #include "transportmanager.h"
21 #include "resourcesendjob_p.h"
23 #include "sendmailjob.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"
36 #include <QApplication>
37 #include <QtDBus/QDBusConnection>
38 #include <QtDBus/QDBusConnectionInterface>
39 #include <QtDBus/QDBusServiceWatcher>
42 #include <QStringList>
45 #include <KConfigGroup>
47 #include <KEMailSettings>
49 #include <KMessageBox>
52 #include <KWallet/Wallet>
54 #include <akonadi/agentinstance.h>
55 #include <akonadi/agentmanager.h>
57 using namespace MailTransport;
58 using namespace KWallet;
60 namespace MailTransport {
65 class TransportManagerPrivate
73 ~TransportManagerPrivate() {
75 qDeleteAll( transports );
79 QList<Transport *> transports;
83 KWallet::Wallet *wallet;
84 bool walletOpenFailed;
86 int defaultTransportId;
88 QList<TransportJob *> walletQueue;
96 void validateDefault();
97 void migrateToWallet();
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 );
116 StaticTransportManager *sSelf = 0;
118 static void destroyStaticTransportManager() {
123 : QObject(), d( new TransportManagerPrivate( this ) )
125 KGlobal::locale()->insertCatalog( QLatin1String(
"libmailtransport" ) );
126 KGlobal::locale()->insertCatalog( QLatin1String(
"libakonadi-kmime" ) );
127 qAddPostRoutine( destroyStaticTransportManager );
128 d->myOwnChange =
false;
129 d->appliedChange =
false;
131 d->walletOpenFailed =
false;
132 d->walletAsyncOpen =
false;
133 d->defaultTransportId = -1;
134 d->config =
new KConfig( QLatin1String(
"mailtransports" ) );
136 QDBusConnection::sessionBus().registerObject( DBUS_OBJECT_PATH,
this,
137 QDBusConnection::ExportScriptableSlots |
138 QDBusConnection::ExportScriptableSignals );
140 QDBusServiceWatcher *watcher =
141 new QDBusServiceWatcher( DBUS_SERVICE_NAME, QDBusConnection::sessionBus(),
142 QDBusServiceWatcher::WatchForUnregistration,
this );
143 connect( watcher, SIGNAL(serviceUnregistered(QString)),
144 SLOT(dbusServiceUnregistered()) );
146 QDBusConnection::sessionBus().connect( QString(), QString(),
147 DBUS_INTERFACE_NAME, DBUS_CHANGE_SIGNAL,
148 this, SLOT(slotTransportsChanged()) );
150 d->isMainInstance = QDBusConnection::sessionBus().registerService( DBUS_SERVICE_NAME );
157 qRemovePostRoutine( destroyStaticTransportManager );
164 sSelf =
new StaticTransportManager;
165 sSelf->d->readConfig();
172 foreach (
Transport *t, d->transports ) {
173 if ( t->id() == id ) {
178 if ( def || (
id == 0 && d->defaultTransportId !=
id ) ) {
186 foreach (
Transport *t, d->transports ) {
187 if ( t->name() == name ) {
199 return d->transports;
209 int id = d->createId();
217 if ( d->transports.contains( transport ) ) {
218 kDebug() <<
"Already have this transport.";
222 kDebug() <<
"Added transport" << transport;
223 d->transports.append( transport );
224 d->validateDefault();
225 emitChangesCommitted();
230 connect( job, SIGNAL(result(KJob*)), SLOT(jobResult(KJob*)) );
234 kDebug() <<
"job waits for wallet:" << job;
235 d->walletQueue << job;
247 t->setName( i18n(
"Default Transport" ) );
248 t->setHost( kes.getSetting( KEMailSettings::OutServer ) );
253 kWarning() <<
"KEMailSettings does not contain a valid transport.";
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 ) {
276 const bool accepted = ( dialog->exec() == QDialog::Accepted );
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();
289 instance.configure( parent );
290 transport->writeConfig();
294 QPointer<TransportConfigDialog> transportConfigDialog =
new TransportConfigDialog(transport, parent);
295 transportConfigDialog->setCaption( i18n(
"Configure account" ) );
296 bool okClicked = ( transportConfigDialog->exec() == QDialog::Accepted );
297 delete transportConfigDialog;
309 switch ( t->type() ) {
310 case Transport::EnumType::SMTP:
312 case Transport::EnumType::Sendmail:
314 case Transport::EnumType::Akonadi:
326 int transportId = transport.toInt( &ok );
344 return d->transports.isEmpty();
350 foreach (
Transport *t, d->transports ) {
359 foreach (
Transport *t, d->transports ) {
376 return d->defaultTransportId;
381 if (
id == d->defaultTransportId || !
transportById(
id,
false ) ) {
384 d->defaultTransportId = id;
397 if ( t->type() == Transport::EnumType::Akonadi ) {
398 using namespace Akonadi;
399 const AgentInstance instance = AgentManager::self()->instance( t->host() );
400 if ( !instance.isValid() ) {
401 kWarning() <<
"Could not find resource instance.";
403 AgentManager::self()->removeInstance( instance );
406 d->transports.removeAll( t );
407 d->validateDefault();
408 QString group = t->currentGroup();
410 d->config->deleteGroup( group );
415 void TransportManagerPrivate::readConfig()
417 QList<Transport *> oldTransports =
transports;
420 QRegExp re( QLatin1String(
"^Transport (.+)$" ) );
421 QStringList groups = config->groupList().filter( re );
422 foreach (
const QString &s, groups ) {
427 foreach (
Transport *old, oldTransports ) {
428 if ( old->currentGroup() == QLatin1String(
"Transport " ) + re.cap( 1 ) ) {
429 kDebug() <<
"reloading existing transport:" << s;
431 t->d->passwordNeedsUpdateFromWallet =
true;
433 oldTransports.removeAll( old );
441 if ( t->id() <= 0 ) {
442 t->setId( createId() );
448 qDeleteAll( oldTransports );
449 oldTransports.clear();
452 KConfigGroup group( config,
"General" );
456 QString name = group.readEntry(
"default-transport", QString() );
457 if ( !name.isEmpty() ) {
458 Transport *t = q->transportByName( name,
false );
467 q->loadPasswordsAsync();
470 void TransportManagerPrivate::writeConfig()
472 KConfigGroup group( config,
"General" );
475 q->emitChangesCommitted();
478 void TransportManagerPrivate::fillTypes()
480 Q_ASSERT(
types.isEmpty() );
485 type.d->mType = Transport::EnumType::SMTP;
486 type.d->mName = i18nc(
"@option SMTP transport",
"SMTP" );
487 type.d->mDescription = i18n(
"An SMTP server on the Internet" );
494 type.d->mType = Transport::EnumType::Sendmail;
495 type.d->mName = i18nc(
"@option sendmail transport",
"Sendmail" );
496 type.d->mDescription = i18n(
"A local sendmail installation" );
502 using namespace Akonadi;
503 foreach (
const AgentType &atype, AgentManager::self()->
types() ) {
506 if ( atype.capabilities().contains( QLatin1String(
"MailTransport" ) ) ) {
508 type.d->mType = Transport::EnumType::Akonadi;
509 type.d->mAgentType = atype;
510 type.d->mName = atype.
name();
513 kDebug() <<
"Found Akonadi type" << atype.
name();
518 QObject::connect( AgentManager::self(), SIGNAL(typeAdded(Akonadi::AgentType)),
519 q, SLOT(agentTypeAdded(Akonadi::AgentType)) );
520 QObject::connect( AgentManager::self(), SIGNAL(typeRemoved(Akonadi::AgentType)),
521 q, SLOT(agentTypeRemoved(Akonadi::AgentType)) );
524 kDebug() <<
"Have SMTP, Sendmail, and" <<
types.count() - 2 <<
"Akonadi types.";
527 void TransportManager::emitChangesCommitted()
529 d->myOwnChange =
true;
530 d->appliedChange =
false;
535 void TransportManagerPrivate::slotTransportsChanged()
537 if ( myOwnChange && appliedChange ) {
539 appliedChange =
false;
544 config->reparseConfiguration();
547 appliedChange =
true;
548 emit q->transportsChanged();
551 int TransportManagerPrivate::createId()
const
560 newId = KRandom::random();
561 }
while ( usedIds.contains( newId ) );
567 if ( d->wallet && d->wallet->isOpen() ) {
571 if ( !Wallet::isEnabled() || d->walletOpenFailed ) {
576 if ( qApp->activeWindow() ) {
577 window = qApp->activeWindow()->winId();
578 }
else if ( !QApplication::topLevelWidgets().
isEmpty() ) {
579 window = qApp->topLevelWidgets().first()->winId();
583 d->wallet = Wallet::openWallet( Wallet::NetworkWallet(), window );
586 d->walletOpenFailed =
true;
594 void TransportManagerPrivate::prepareWallet()
599 if ( !
wallet->hasFolder( WALLET_FOLDER ) ) {
600 wallet->createFolder( WALLET_FOLDER );
602 wallet->setFolder( WALLET_FOLDER );
607 foreach (
Transport *t, d->transports ) {
612 const QList<TransportJob*> copy = d->walletQueue;
613 d->walletQueue.clear();
627 foreach (
Transport *t, d->transports ) {
638 if ( !d->wallet && !d->walletOpenFailed ) {
640 if ( qApp->activeWindow() ) {
641 window = qApp->activeWindow()->winId();
642 }
else if ( !QApplication::topLevelWidgets().
isEmpty() ) {
643 window = qApp->topLevelWidgets().first()->winId();
646 d->wallet = Wallet::openWallet( Wallet::NetworkWallet(), window,
647 Wallet::Asynchronous );
649 connect( d->wallet, SIGNAL(walletOpened(
bool)), SLOT(slotWalletOpened(
bool)) );
650 d->walletAsyncOpen =
true;
652 d->walletOpenFailed =
true;
657 if ( d->wallet && !d->walletAsyncOpen ) {
662 void TransportManagerPrivate::slotWalletOpened(
bool success )
665 walletAsyncOpen =
false;
667 walletOpenFailed =
true;
676 void TransportManagerPrivate::validateDefault()
679 if ( q->isEmpty() ) {
688 void TransportManagerPrivate::migrateToWallet()
691 static bool firstRun =
true;
698 if ( !isMainInstance ) {
709 if ( names.isEmpty() ) {
714 int result = KMessageBox::questionYesNoList(
716 i18n(
"The following mail transports store their passwords in an "
717 "unencrypted configuration file.\nFor security reasons, "
718 "please consider migrating these passwords to KWallet, the "
719 "KDE Wallet management tool,\nwhich stores sensitive data "
720 "for you in a strongly encrypted file.\n"
721 "Do you want to migrate your passwords to KWallet?" ),
722 names, i18n(
"Question" ),
723 KGuiItem( i18n(
"Migrate" ) ), KGuiItem( i18n(
"Keep" ) ),
724 QString::fromLatin1(
"WalletMigrate" ) );
725 if ( result != KMessageBox::Yes ) {
737 void TransportManagerPrivate::dbusServiceUnregistered()
739 QDBusConnection::sessionBus().registerService( DBUS_SERVICE_NAME );
742 void TransportManagerPrivate::agentTypeAdded(
const Akonadi::AgentType &atype )
744 using namespace Akonadi;
745 if ( atype.capabilities().contains( QLatin1String(
"MailTransport" ) ) ) {
747 type.d->mType = Transport::EnumType::Akonadi;
748 type.d->mAgentType = atype;
749 type.d->mName = atype.
name();
752 kDebug() <<
"Added new Akonadi type" << atype.
name();
756 void TransportManagerPrivate::agentTypeRemoved(
const Akonadi::AgentType &atype )
758 using namespace Akonadi;
760 if ( type.
type() == Transport::EnumType::Akonadi &&
762 types.removeAll( type );
763 kDebug() <<
"Removed Akonadi type" << atype.name();
768 void TransportManagerPrivate::jobResult( KJob *job )
770 walletQueue.removeAll( static_cast<TransportJob*>( job ) );
773 #include "moc_transportmanager.cpp"
A representation of a transport type.
void transportRemoved(int id, const QString &name)
Emitted when a transport is deleted.
QString name() const
Returns the i18n'ed name of the transport type.
void migrateToWallet()
Try to migrate the password from the config file to the wallet.
void loadPasswords()
Loads all passwords synchronously.
void addTransport(Transport *transport)
Adds the given transport.
Q_SCRIPTABLE void transportsChanged()
Emitted when transport settings have changed (by this or any other TransportManager instance)...
bool isComplete() const
Returns true if all settings have been loaded.
Q_SCRIPTABLE void setDefaultTransport(int id)
Sets the default transport.
static TransportManager * self()
Returns the TransportManager instance.
KWallet::Wallet * wallet()
Returns a pointer to an open wallet if available, 0 otherwise.
virtual ~TransportManager()
Destructor.
MAILTRANSPORT_DEPRECATED void schedule(TransportJob *job)
Executes the given transport job.
Central transport management interface.
Q_SCRIPTABLE int defaultTransportId() const
Returns the default transport identifier.
Mail transport job for SMTP.
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.
ShowCondition
Describes when to show the transport creation dialog.
void createDefaultTransport()
Tries to create a transport based on KEMailSettings.
bool configureTransport(Transport *transport, QWidget *parent)
Open a configuration dialog for an existing transport.
QString description() const
Returns a description of the transport type.
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.
Mail transport job for sendmail.
Mail transport job for an Akonadi resource-based transport.
Transport * createTransport() const
Creates a new, empty Transport object.
bool needsWalletMigration() const
Returns true if the password was not stored in the wallet.
void passwordsChanged()
Emitted when passwords have been loaded from the wallet.
bool isValid() const
Returns true if this transport is valid, ie.
Abstract base class for all mail transport jobs.
TransportManager()
Singleton class, the only instance resides in the static object sSelf.
QList< Transport * > transports() const
Returns a list of all available transports.
MAILTRANSPORT_DEPRECATED TransportJob * createTransportJob(int transportId)
Creates a mail transport job for the given transport identifier.
Transport * transportByName(const QString &name, bool def=true) const
Returns the transport object with the given name.
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...
Internal file containing constant definitions etc.
virtual void start()
Starts this job.
void loadPasswordsAsync()
Tries to load passwords asynchronously from KWallet if needed.
Configuration dialog for a mail transport.
Only show the transport creation dialog if no transport currently exists.
Q_SCRIPTABLE QList< int > transportIds() const
Returns a list of transport identifiers.
QList< TransportType > List
Describes a list of transport types.
void updatePasswordState()
This function synchronizes the password of this transport with the password of the transport with the...
TransportBase::EnumType::type type() const
Represents the settings of a specific mail transport.
Transport * transportById(int id, bool def=true) const
Returns the Transport object with the given id.
Akonadi::AgentType agentType() const
Returns the corresponding Akonadi::AgentType that this transport type represents. ...
Q_SCRIPTABLE void removeTransport(int id)
Deletes the specified transport.
TransportType::List types() const
Returns a list of all available transport types.
Transport * transport() const
Returns the Transport object containing the mail transport settings.