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 <KLocalizedString>
50 #include <KMessageBox>
54 #include <KWallet/Wallet>
56 #include <akonadi/agentinstance.h>
57 #include <akonadi/agentmanager.h>
59 using namespace MailTransport;
60 using namespace KWallet;
62 namespace MailTransport {
67 class TransportManagerPrivate
75 ~TransportManagerPrivate() {
77 qDeleteAll( transports );
81 QList<Transport *> transports;
85 KWallet::Wallet *wallet;
86 bool walletOpenFailed;
88 int defaultTransportId;
90 QList<TransportJob *> walletQueue;
98 void validateDefault();
99 void migrateToWallet();
102 void slotTransportsChanged();
103 void slotWalletOpened(
bool success );
104 void dbusServiceUnregistered();
105 void agentTypeAdded(
const Akonadi::AgentType &atype );
106 void agentTypeRemoved(
const Akonadi::AgentType &atype );
107 void jobResult( KJob *job );
118 StaticTransportManager *sSelf = 0;
120 static void destroyStaticTransportManager() {
125 : QObject(), d( new TransportManagerPrivate( this ) )
127 KGlobal::locale()->insertCatalog( QLatin1String(
"libmailtransport" ) );
128 KGlobal::locale()->insertCatalog( QLatin1String(
"libakonadi-kmime" ) );
129 qAddPostRoutine( destroyStaticTransportManager );
130 d->myOwnChange =
false;
131 d->appliedChange =
false;
133 d->walletOpenFailed =
false;
134 d->walletAsyncOpen =
false;
135 d->defaultTransportId = -1;
136 d->config =
new KConfig( QLatin1String(
"mailtransports" ) );
138 QDBusConnection::sessionBus().registerObject( DBUS_OBJECT_PATH,
this,
139 QDBusConnection::ExportScriptableSlots |
140 QDBusConnection::ExportScriptableSignals );
142 QDBusServiceWatcher *watcher =
143 new QDBusServiceWatcher( DBUS_SERVICE_NAME, QDBusConnection::sessionBus(),
144 QDBusServiceWatcher::WatchForUnregistration,
this );
145 connect( watcher, SIGNAL(serviceUnregistered(QString)),
146 SLOT(dbusServiceUnregistered()) );
148 QDBusConnection::sessionBus().connect( QString(), QString(),
149 DBUS_INTERFACE_NAME, DBUS_CHANGE_SIGNAL,
150 this, SLOT(slotTransportsChanged()) );
152 d->isMainInstance = QDBusConnection::sessionBus().registerService( DBUS_SERVICE_NAME );
159 qRemovePostRoutine( destroyStaticTransportManager );
166 sSelf =
new StaticTransportManager;
167 sSelf->d->readConfig();
174 foreach (
Transport *t, d->transports ) {
175 if ( t->id() == id ) {
180 if ( def || (
id == 0 && d->defaultTransportId !=
id ) ) {
188 foreach (
Transport *t, d->transports ) {
189 if ( t->name() == name ) {
201 return d->transports;
211 int id = d->createId();
219 if ( d->transports.contains( transport ) ) {
220 kDebug() <<
"Already have this transport.";
224 kDebug() <<
"Added transport" << transport;
225 d->transports.append( transport );
226 d->validateDefault();
227 emitChangesCommitted();
232 connect( job, SIGNAL(result(KJob*)), SLOT(jobResult(KJob*)) );
236 kDebug() <<
"job waits for wallet:" << job;
237 d->walletQueue << job;
249 t->setName( i18n(
"Default Transport" ) );
250 t->setHost( kes.getSetting( KEMailSettings::OutServer ) );
255 kWarning() <<
"KEMailSettings does not contain a valid transport.";
267 const int response = KMessageBox::messageBox( parent,
268 KMessageBox::WarningContinueCancel,
269 i18n(
"You must create an outgoing account before sending." ),
270 i18n(
"Create Account Now?" ),
271 KGuiItem( i18n(
"Create Account Now" ) ) );
272 if ( response != KMessageBox::Continue ) {
278 const bool accepted = ( dialog->exec() == QDialog::Accepted );
285 if ( transport->type() == Transport::EnumType::Akonadi ) {
286 using namespace Akonadi;
287 AgentInstance instance = AgentManager::self()->instance( transport->host() );
288 if ( !instance.isValid() ) {
289 kWarning() <<
"Invalid resource instance" << transport->host();
291 instance.configure( parent );
292 transport->writeConfig();
296 QPointer<TransportConfigDialog> transportConfigDialog =
298 transportConfigDialog->setCaption( i18n(
"Configure account" ) );
299 bool okClicked = ( transportConfigDialog->exec() == QDialog::Accepted );
300 delete transportConfigDialog;
312 switch ( t->type() ) {
313 case Transport::EnumType::SMTP:
315 case Transport::EnumType::Sendmail:
317 case Transport::EnumType::Akonadi:
329 int transportId = transport.toInt( &ok );
347 return d->transports.isEmpty();
353 foreach (
Transport *t, d->transports ) {
362 foreach (
Transport *t, d->transports ) {
379 return d->defaultTransportId;
384 if (
id == d->defaultTransportId || !
transportById(
id,
false ) ) {
387 d->defaultTransportId = id;
400 if ( t->type() == Transport::EnumType::Akonadi ) {
401 using namespace Akonadi;
402 const AgentInstance instance = AgentManager::self()->instance( t->host() );
403 if ( !instance.isValid() ) {
404 kWarning() <<
"Could not find resource instance.";
406 AgentManager::self()->removeInstance( instance );
409 d->transports.removeAll( t );
410 d->validateDefault();
411 QString group = t->currentGroup();
413 d->config->deleteGroup( group );
418 void TransportManagerPrivate::readConfig()
420 QList<Transport *> oldTransports =
transports;
423 QRegExp re( QLatin1String(
"^Transport (.+)$" ) );
424 QStringList groups = config->groupList().filter( re );
425 foreach (
const QString &s, groups ) {
430 foreach (
Transport *old, oldTransports ) {
431 if ( old->currentGroup() == QLatin1String(
"Transport " ) + re.cap( 1 ) ) {
432 kDebug() <<
"reloading existing transport:" << s;
434 t->d->passwordNeedsUpdateFromWallet =
true;
436 oldTransports.removeAll( old );
444 if ( t->id() <= 0 ) {
445 t->setId( createId() );
451 qDeleteAll( oldTransports );
452 oldTransports.clear();
455 KConfigGroup group( config,
"General" );
459 QString name = group.readEntry(
"default-transport", QString() );
460 if ( !name.isEmpty() ) {
461 Transport *t = q->transportByName( name,
false );
470 q->loadPasswordsAsync();
473 void TransportManagerPrivate::writeConfig()
475 KConfigGroup group( config,
"General" );
478 q->emitChangesCommitted();
481 void TransportManagerPrivate::fillTypes()
483 Q_ASSERT(
types.isEmpty() );
488 type.d->mType = Transport::EnumType::SMTP;
489 type.d->mName = i18nc(
"@option SMTP transport",
"SMTP" );
490 type.d->mDescription = i18n(
"An SMTP server on the Internet" );
497 type.d->mType = Transport::EnumType::Sendmail;
498 type.d->mName = i18nc(
"@option sendmail transport",
"Sendmail" );
499 type.d->mDescription = i18n(
"A local sendmail installation" );
505 using namespace Akonadi;
506 foreach (
const AgentType &atype, AgentManager::self()->
types() ) {
509 if ( atype.capabilities().contains( QLatin1String(
"MailTransport" ) ) ) {
511 type.d->mType = Transport::EnumType::Akonadi;
512 type.d->mAgentType = atype;
513 type.d->mName = atype.
name();
516 kDebug() <<
"Found Akonadi type" << atype.
name();
521 QObject::connect( AgentManager::self(), SIGNAL(typeAdded(Akonadi::AgentType)),
522 q, SLOT(agentTypeAdded(Akonadi::AgentType)) );
523 QObject::connect( AgentManager::self(), SIGNAL(typeRemoved(Akonadi::AgentType)),
524 q, SLOT(agentTypeRemoved(Akonadi::AgentType)) );
527 kDebug() <<
"Have SMTP, Sendmail, and" <<
types.count() - 2 <<
"Akonadi types.";
530 void TransportManager::emitChangesCommitted()
532 d->myOwnChange =
true;
533 d->appliedChange =
false;
538 void TransportManagerPrivate::slotTransportsChanged()
540 if ( myOwnChange && appliedChange ) {
542 appliedChange =
false;
547 config->reparseConfiguration();
550 appliedChange =
true;
551 emit q->transportsChanged();
554 int TransportManagerPrivate::createId()
const
563 newId = KRandom::random();
564 }
while ( usedIds.contains( newId ) );
570 if ( d->wallet && d->wallet->isOpen() ) {
574 if ( !Wallet::isEnabled() || d->walletOpenFailed ) {
579 if ( qApp->activeWindow() ) {
580 window = qApp->activeWindow()->winId();
581 }
else if ( !QApplication::topLevelWidgets().
isEmpty() ) {
582 window = qApp->topLevelWidgets().first()->winId();
586 d->wallet = Wallet::openWallet( Wallet::NetworkWallet(), window );
589 d->walletOpenFailed =
true;
597 void TransportManagerPrivate::prepareWallet()
602 if ( !
wallet->hasFolder( WALLET_FOLDER ) ) {
603 wallet->createFolder( WALLET_FOLDER );
605 wallet->setFolder( WALLET_FOLDER );
610 foreach (
Transport *t, d->transports ) {
615 const QList<TransportJob*> copy = d->walletQueue;
616 d->walletQueue.clear();
630 foreach (
Transport *t, d->transports ) {
641 if ( !d->wallet && !d->walletOpenFailed ) {
643 if ( qApp->activeWindow() ) {
644 window = qApp->activeWindow()->winId();
645 }
else if ( !QApplication::topLevelWidgets().
isEmpty() ) {
646 window = qApp->topLevelWidgets().first()->winId();
649 d->wallet = Wallet::openWallet( Wallet::NetworkWallet(), window,
650 Wallet::Asynchronous );
652 connect( d->wallet, SIGNAL(walletOpened(
bool)), SLOT(slotWalletOpened(
bool)) );
653 d->walletAsyncOpen =
true;
655 d->walletOpenFailed =
true;
660 if ( d->wallet && !d->walletAsyncOpen ) {
665 void TransportManagerPrivate::slotWalletOpened(
bool success )
668 walletAsyncOpen =
false;
670 walletOpenFailed =
true;
679 void TransportManagerPrivate::validateDefault()
682 if ( q->isEmpty() ) {
691 void TransportManagerPrivate::migrateToWallet()
694 static bool firstRun =
true;
701 if ( !isMainInstance ) {
712 if ( names.isEmpty() ) {
717 int result = KMessageBox::questionYesNoList(
719 i18n(
"The following mail transports store their passwords in an "
720 "unencrypted configuration file.\nFor security reasons, "
721 "please consider migrating these passwords to KWallet, the "
722 "KDE Wallet management tool,\nwhich stores sensitive data "
723 "for you in a strongly encrypted file.\n"
724 "Do you want to migrate your passwords to KWallet?" ),
725 names, i18n(
"Question" ),
726 KGuiItem( i18n(
"Migrate" ) ), KGuiItem( i18n(
"Keep" ) ),
727 QString::fromLatin1(
"WalletMigrate" ) );
728 if ( result != KMessageBox::Yes ) {
740 void TransportManagerPrivate::dbusServiceUnregistered()
742 QDBusConnection::sessionBus().registerService( DBUS_SERVICE_NAME );
745 void TransportManagerPrivate::agentTypeAdded(
const Akonadi::AgentType &atype )
747 using namespace Akonadi;
748 if ( atype.capabilities().contains( QLatin1String(
"MailTransport" ) ) ) {
750 type.d->mType = Transport::EnumType::Akonadi;
751 type.d->mAgentType = atype;
752 type.d->mName = atype.
name();
755 kDebug() <<
"Added new Akonadi type" << atype.
name();
759 void TransportManagerPrivate::agentTypeRemoved(
const Akonadi::AgentType &atype )
761 using namespace Akonadi;
763 if ( type.
type() == Transport::EnumType::Akonadi &&
765 types.removeAll( type );
766 kDebug() <<
"Removed Akonadi type" << atype.name();
771 void TransportManagerPrivate::jobResult( KJob *job )
773 walletQueue.removeAll( static_cast<TransportJob*>( job ) );
776 #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.