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