00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "transportmanager.h"
00021 #include "mailtransport_defs.h"
00022 #include "transport.h"
00023 #include "smtpjob.h"
00024 #include "sendmailjob.h"
00025
00026 #include <kconfig.h>
00027 #include <kdebug.h>
00028 #include <kemailsettings.h>
00029 #include <klocale.h>
00030 #include <kmessagebox.h>
00031 #include <krandom.h>
00032 #include <kurl.h>
00033 #include <kwallet.h>
00034 #include <kconfiggroup.h>
00035
00036 #include <QApplication>
00037 #include <QtDBus/QDBusConnection>
00038 #include <QtDBus/QDBusConnectionInterface>
00039 #include <QRegExp>
00040 #include <QStringList>
00041
00042 using namespace MailTransport;
00043 using namespace KWallet;
00044
00049 class TransportManager::Private
00050 {
00051 public:
00052 Private() {}
00053 ~Private() {
00054 delete config;
00055 qDeleteAll( transports );
00056 }
00057
00058 KConfig *config;
00059 QList<Transport *> transports;
00060 bool myOwnChange;
00061 bool appliedChange;
00062 KWallet::Wallet *wallet;
00063 bool walletOpenFailed;
00064 bool walletAsyncOpen;
00065 int defaultTransportId;
00066 bool isMainInstance;
00067 QList<TransportJob *> walletQueue;
00068 };
00069
00070 class StaticTransportManager : public TransportManager
00071 {
00072 public:
00073 StaticTransportManager() : TransportManager() {}
00074 };
00075
00076 StaticTransportManager *sSelf = 0;
00077
00078 static void destroyStaticTransportManager() {
00079 delete sSelf;
00080 }
00081
00082 TransportManager::TransportManager()
00083 : QObject(), d( new Private )
00084 {
00085 KGlobal::locale()->insertCatalog( QLatin1String( "libmailtransport" ) );
00086 qAddPostRoutine( destroyStaticTransportManager );
00087 d->myOwnChange = false;
00088 d->appliedChange = false;
00089 d->wallet = 0;
00090 d->walletOpenFailed = false;
00091 d->walletAsyncOpen = false;
00092 d->defaultTransportId = -1;
00093 d->config = new KConfig( QLatin1String( "mailtransports" ) );
00094
00095 QDBusConnection::sessionBus().registerObject( DBUS_OBJECT_PATH, this,
00096 QDBusConnection::ExportScriptableSlots |
00097 QDBusConnection::ExportScriptableSignals );
00098
00099 QDBusConnection::sessionBus().connect( QString(), QString(),
00100 DBUS_INTERFACE_NAME, DBUS_CHANGE_SIGNAL,
00101 this, SLOT(slotTransportsChanged()) );
00102
00103 d->isMainInstance =
00104 QDBusConnection::sessionBus().registerService( DBUS_SERVICE_NAME );
00105 connect( QDBusConnection::sessionBus().interface(),
00106 SIGNAL(serviceOwnerChanged(QString,QString,QString)),
00107 SLOT(dbusServiceOwnerChanged(QString,QString,QString)) );
00108 }
00109
00110 TransportManager::~TransportManager()
00111 {
00112 qRemovePostRoutine( destroyStaticTransportManager );
00113 delete d;
00114 }
00115
00116 TransportManager *TransportManager::self()
00117 {
00118 if ( !sSelf ) {
00119 sSelf = new StaticTransportManager;
00120 sSelf->readConfig();
00121 }
00122 return sSelf;
00123 }
00124
00125 Transport *TransportManager::transportById( int id, bool def ) const
00126 {
00127 foreach ( Transport *t, d->transports ) {
00128 if ( t->id() == id ) {
00129 return t;
00130 }
00131 }
00132
00133 if ( def || ( id == 0 && d->defaultTransportId != id ) ) {
00134 return transportById( d->defaultTransportId, false );
00135 }
00136 return 0;
00137 }
00138
00139 Transport *TransportManager::transportByName( const QString &name, bool def ) const
00140 {
00141 foreach ( Transport *t, d->transports ) {
00142 if ( t->name() == name ) {
00143 return t;
00144 }
00145 }
00146 if ( def ) {
00147 return transportById( 0, false );
00148 }
00149 return 0;
00150 }
00151
00152 QList< Transport * > TransportManager::transports() const
00153 {
00154 return d->transports;
00155 }
00156
00157 Transport *TransportManager::createTransport() const
00158 {
00159 int id = createId();
00160 Transport *t = new Transport( QString::number( id ) );
00161 t->setId( id );
00162 return t;
00163 }
00164
00165 void TransportManager::addTransport( Transport *transport )
00166 {
00167 if ( d->transports.contains( transport ) ) {
00168 return;
00169 }
00170
00171 d->transports.append( transport );
00172 validateDefault();
00173 emitChangesCommitted();
00174 }
00175
00176 void TransportManager::schedule( TransportJob *job )
00177 {
00178 connect( job, SIGNAL(result(KJob*)), SLOT(jobResult(KJob*)) );
00179
00180
00181 if ( !job->transport()->isComplete() ) {
00182 kDebug() << "job waits for wallet:" << job;
00183 d->walletQueue << job;
00184 loadPasswordsAsync();
00185 return;
00186 }
00187
00188 job->start();
00189 }
00190
00191 void TransportManager::createDefaultTransport()
00192 {
00193 KEMailSettings kes;
00194 Transport *t = createTransport();
00195 t->setName( i18n( "Default Transport" ) );
00196 t->setHost( kes.getSetting( KEMailSettings::OutServer ) );
00197 if ( t->isValid() ) {
00198 t->writeConfig();
00199 addTransport( t );
00200 } else {
00201 kWarning() << "KEMailSettings does not contain a valid transport.";
00202 }
00203 }
00204
00205 TransportJob *TransportManager::createTransportJob( int transportId )
00206 {
00207 Transport *t = transportById( transportId, false );
00208 if ( !t ) {
00209 return 0;
00210 }
00211 switch ( t->type() ) {
00212 case Transport::EnumType::SMTP:
00213 return new SmtpJob( t->clone(), this );
00214 case Transport::EnumType::Sendmail:
00215 return new SendmailJob( t->clone(), this );
00216 }
00217 Q_ASSERT( false );
00218 return 0;
00219 }
00220
00221 TransportJob *TransportManager::createTransportJob( const QString &transport )
00222 {
00223 bool ok = false;
00224 Transport *t = 0;
00225
00226 int transportId = transport.toInt( &ok );
00227 if ( ok ) {
00228 t = transportById( transportId );
00229 }
00230
00231 if ( !t ) {
00232 t = transportByName( transport, false );
00233 }
00234
00235 if ( t ) {
00236 return createTransportJob( t->id() );
00237 }
00238
00239 return 0;
00240 }
00241
00242 bool TransportManager::isEmpty() const
00243 {
00244 return d->transports.isEmpty();
00245 }
00246
00247 QList<int> TransportManager::transportIds() const
00248 {
00249 QList<int> rv;
00250 foreach ( Transport *t, d->transports ) {
00251 rv << t->id();
00252 }
00253 return rv;
00254 }
00255
00256 QStringList TransportManager::transportNames() const
00257 {
00258 QStringList rv;
00259 foreach ( Transport *t, d->transports ) {
00260 rv << t->name();
00261 }
00262 return rv;
00263 }
00264
00265 QString TransportManager::defaultTransportName() const
00266 {
00267 Transport *t = transportById( d->defaultTransportId, false );
00268 if ( t ) {
00269 return t->name();
00270 }
00271 return QString();
00272 }
00273
00274 int TransportManager::defaultTransportId() const
00275 {
00276 return d->defaultTransportId;
00277 }
00278
00279 void TransportManager::setDefaultTransport( int id )
00280 {
00281 if ( id == d->defaultTransportId || !transportById( id, false ) ) {
00282 return;
00283 }
00284 d->defaultTransportId = id;
00285 writeConfig();
00286 }
00287
00288 void TransportManager::removeTransport( int id )
00289 {
00290 Transport *t = transportById( id, false );
00291 if ( !t ) {
00292 return;
00293 }
00294 emit transportRemoved( t->id(), t->name() );
00295 d->transports.removeAll( t );
00296 validateDefault();
00297 QString group = t->currentGroup();
00298 delete t;
00299 d->config->deleteGroup( group );
00300 writeConfig();
00301 }
00302
00303 void TransportManager::readConfig()
00304 {
00305 QList<Transport *> oldTransports = d->transports;
00306 d->transports.clear();
00307
00308 QRegExp re( QLatin1String( "^Transport (.+)$" ) );
00309 QStringList groups = d->config->groupList().filter( re );
00310 foreach ( const QString &s, groups ) {
00311 re.indexIn( s );
00312 Transport *t = 0;
00313
00314
00315 foreach ( Transport *old, oldTransports ) {
00316 if ( old->currentGroup() == QLatin1String( "Transport " ) + re.cap( 1 ) ) {
00317 kDebug() << "reloading existing transport:" << s;
00318 t = old;
00319 t->readConfig();
00320 oldTransports.removeAll( old );
00321 break;
00322 }
00323 }
00324
00325 if ( !t ) {
00326 t = new Transport( re.cap( 1 ) );
00327 }
00328 if ( t->id() <= 0 ) {
00329 t->setId( createId() );
00330 t->writeConfig();
00331 }
00332 d->transports.append( t );
00333 }
00334
00335 qDeleteAll( oldTransports );
00336 oldTransports.clear();
00337
00338
00339 KConfigGroup group( d->config, "General" );
00340 d->defaultTransportId = group.readEntry( "default-transport", 0 );
00341 if ( d->defaultTransportId == 0 ) {
00342
00343 QString name = group.readEntry( "default-transport", QString() );
00344 if ( !name.isEmpty() ) {
00345 Transport *t = transportByName( name, false );
00346 if ( t ) {
00347 d->defaultTransportId = t->id();
00348 writeConfig();
00349 }
00350 }
00351 }
00352 validateDefault();
00353 migrateToWallet();
00354 }
00355
00356 void TransportManager::writeConfig()
00357 {
00358 KConfigGroup group( d->config, "General" );
00359 group.writeEntry( "default-transport", d->defaultTransportId );
00360 d->config->sync();
00361 emitChangesCommitted();
00362 }
00363
00364 void TransportManager::emitChangesCommitted()
00365 {
00366 d->myOwnChange = true;
00367 d->appliedChange = false;
00368 emit transportsChanged();
00369 emit changesCommitted();
00370 }
00371
00372 void TransportManager::slotTransportsChanged()
00373 {
00374 if ( d->myOwnChange && d->appliedChange ) {
00375 d->myOwnChange = false;
00376 d->appliedChange = false;
00377 return;
00378 }
00379
00380 kDebug();
00381 d->config->reparseConfiguration();
00382
00383 readConfig();
00384 d->appliedChange = true;
00385 emit transportsChanged();
00386 }
00387
00388 int TransportManager::createId() const
00389 {
00390 QList<int> usedIds;
00391 foreach ( Transport *t, d->transports ) {
00392 usedIds << t->id();
00393 }
00394 usedIds << 0;
00395 int newId;
00396 do {
00397 newId = KRandom::random();
00398 } while ( usedIds.contains( newId ) );
00399 return newId;
00400 }
00401
00402 KWallet::Wallet * TransportManager::wallet()
00403 {
00404 if ( d->wallet && d->wallet->isOpen() ) {
00405 return d->wallet;
00406 }
00407
00408 if ( !Wallet::isEnabled() || d->walletOpenFailed ) {
00409 return 0;
00410 }
00411
00412 WId window = 0;
00413 if ( qApp->activeWindow() ) {
00414 window = qApp->activeWindow()->winId();
00415 } else if ( !QApplication::topLevelWidgets().isEmpty() ) {
00416 window = qApp->topLevelWidgets().first()->winId();
00417 }
00418
00419 delete d->wallet;
00420 d->wallet = Wallet::openWallet( Wallet::NetworkWallet(), window );
00421
00422 if ( !d->wallet ) {
00423 d->walletOpenFailed = true;
00424 return 0;
00425 }
00426
00427 prepareWallet();
00428 return d->wallet;
00429 }
00430
00431 void TransportManager::prepareWallet()
00432 {
00433 if ( !d->wallet ) {
00434 return;
00435 }
00436 if ( !d->wallet->hasFolder( WALLET_FOLDER ) ) {
00437 d->wallet->createFolder( WALLET_FOLDER );
00438 }
00439 d->wallet->setFolder( WALLET_FOLDER );
00440 }
00441
00442 void TransportManager::loadPasswords()
00443 {
00444 foreach ( Transport *t, d->transports ) {
00445 t->readPassword();
00446 }
00447
00448
00449 foreach ( TransportJob *job, d->walletQueue ) {
00450 job->start();
00451 }
00452 d->walletQueue.clear();
00453
00454 emit passwordsChanged();
00455 }
00456
00457 void TransportManager::loadPasswordsAsync()
00458 {
00459 kDebug();
00460
00461
00462 bool found = false;
00463 foreach ( Transport *t, d->transports ) {
00464 if ( !t->isComplete() ) {
00465 found = true;
00466 break;
00467 }
00468 }
00469 if ( !found ) {
00470 return;
00471 }
00472
00473
00474 if ( !d->wallet && !d->walletOpenFailed ) {
00475 WId window = 0;
00476 if ( qApp->activeWindow() ) {
00477 window = qApp->activeWindow()->winId();
00478 } else if ( !QApplication::topLevelWidgets().isEmpty() ) {
00479 window = qApp->topLevelWidgets().first()->winId();
00480 }
00481
00482 d->wallet = Wallet::openWallet( Wallet::NetworkWallet(), window,
00483 Wallet::Asynchronous );
00484 if ( d->wallet ) {
00485 connect( d->wallet, SIGNAL(walletOpened(bool)), SLOT(slotWalletOpened(bool)) );
00486 d->walletAsyncOpen = true;
00487 } else {
00488 d->walletOpenFailed = true;
00489 loadPasswords();
00490 }
00491 return;
00492 }
00493 if ( d->wallet && !d->walletAsyncOpen ) {
00494 loadPasswords();
00495 }
00496 }
00497
00498 void TransportManager::slotWalletOpened( bool success )
00499 {
00500 kDebug();
00501 d->walletAsyncOpen = false;
00502 if ( !success ) {
00503 d->walletOpenFailed = true;
00504 delete d->wallet;
00505 d->wallet = 0;
00506 } else {
00507 prepareWallet();
00508 }
00509 loadPasswords();
00510 }
00511
00512 void TransportManager::validateDefault()
00513 {
00514 if ( !transportById( d->defaultTransportId, false ) ) {
00515 if ( isEmpty() ) {
00516 d->defaultTransportId = -1;
00517 } else {
00518 d->defaultTransportId = d->transports.first()->id();
00519 writeConfig();
00520 }
00521 }
00522 }
00523
00524 void TransportManager::migrateToWallet()
00525 {
00526
00527 static bool firstRun = true;
00528 if ( !firstRun ) {
00529 return;
00530 }
00531 firstRun = false;
00532
00533
00534 if ( !d->isMainInstance ) {
00535 return;
00536 }
00537
00538
00539 QStringList names;
00540 foreach ( Transport *t, d->transports ) {
00541 if ( t->needsWalletMigration() ) {
00542 names << t->name();
00543 }
00544 }
00545 if ( names.isEmpty() ) {
00546 return;
00547 }
00548
00549
00550 int result = KMessageBox::questionYesNoList(
00551 0,
00552 i18n( "The following mail transports store their passwords in an "
00553 "unencrypted configuration file.\nFor security reasons, "
00554 "please consider migrating these passwords to KWallet, the "
00555 "KDE Wallet management tool,\nwhich stores sensitive data "
00556 "for you in a strongly encrypted file.\n"
00557 "Do you want to migrate your passwords to KWallet?" ),
00558 names, i18n( "Question" ),
00559 KGuiItem( i18n( "Migrate" ) ), KGuiItem( i18n( "Keep" ) ),
00560 QString::fromAscii( "WalletMigrate" ) );
00561 if ( result != KMessageBox::Yes ) {
00562 return;
00563 }
00564
00565
00566 foreach ( Transport *t, d->transports ) {
00567 if ( t->needsWalletMigration() ) {
00568 t->migrateToWallet();
00569 }
00570 }
00571 }
00572
00573 void TransportManager::dbusServiceOwnerChanged( const QString &service,
00574 const QString &oldOwner,
00575 const QString &newOwner )
00576 {
00577 Q_UNUSED( oldOwner );
00578 if ( service == DBUS_SERVICE_NAME && newOwner.isEmpty() ) {
00579 QDBusConnection::sessionBus().registerService( DBUS_SERVICE_NAME );
00580 }
00581 }
00582
00583 void TransportManager::jobResult( KJob *job )
00584 {
00585 d->walletQueue.removeAll( static_cast<TransportJob*>( job ) );
00586 }
00587
00588 #include "transportmanager.moc"