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 Q_ASSERT( false );
00234 return 0;
00235 }
00236
00237 bool TransportManager::isEmpty() const
00238 {
00239 return d->transports.isEmpty();
00240 }
00241
00242 QList<int> TransportManager::transportIds() const
00243 {
00244 QList<int> rv;
00245 foreach ( Transport *t, d->transports ) {
00246 rv << t->id();
00247 }
00248 return rv;
00249 }
00250
00251 QStringList TransportManager::transportNames() const
00252 {
00253 QStringList rv;
00254 foreach ( Transport *t, d->transports ) {
00255 rv << t->name();
00256 }
00257 return rv;
00258 }
00259
00260 QString TransportManager::defaultTransportName() const
00261 {
00262 Transport *t = transportById( d->defaultTransportId, false );
00263 if ( t ) {
00264 return t->name();
00265 }
00266 return QString();
00267 }
00268
00269 int TransportManager::defaultTransportId() const
00270 {
00271 return d->defaultTransportId;
00272 }
00273
00274 void TransportManager::setDefaultTransport( int id )
00275 {
00276 if ( id == d->defaultTransportId || !transportById( id, false ) ) {
00277 return;
00278 }
00279 d->defaultTransportId = id;
00280 writeConfig();
00281 }
00282
00283 void TransportManager::removeTransport( int id )
00284 {
00285 Transport *t = transportById( id, false );
00286 if ( !t ) {
00287 return;
00288 }
00289 emit transportRemoved( t->id(), t->name() );
00290 d->transports.removeAll( t );
00291 validateDefault();
00292 QString group = t->currentGroup();
00293 delete t;
00294 d->config->deleteGroup( group );
00295 writeConfig();
00296 }
00297
00298 void TransportManager::readConfig()
00299 {
00300 QList<Transport *> oldTransports = d->transports;
00301 d->transports.clear();
00302
00303 QRegExp re( QLatin1String( "^Transport (.+)$" ) );
00304 QStringList groups = d->config->groupList().filter( re );
00305 foreach ( QString s, groups ) {
00306 re.indexIn( s );
00307 Transport *t = 0;
00308
00309
00310 foreach ( Transport *old, oldTransports ) {
00311 if ( old->currentGroup() == QLatin1String( "Transport " ) + re.cap( 1 ) ) {
00312 kDebug() << "reloading existing transport:" << s;
00313 t = old;
00314 t->readConfig();
00315 oldTransports.removeAll( old );
00316 break;
00317 }
00318 }
00319
00320 if ( !t ) {
00321 t = new Transport( re.cap( 1 ) );
00322 }
00323 if ( t->id() <= 0 ) {
00324 t->setId( createId() );
00325 t->writeConfig();
00326 }
00327 d->transports.append( t );
00328 }
00329
00330 qDeleteAll( oldTransports );
00331 oldTransports.clear();
00332
00333
00334 KConfigGroup group( d->config, "General" );
00335 d->defaultTransportId = group.readEntry( "default-transport", 0 );
00336 if ( d->defaultTransportId == 0 ) {
00337
00338 QString name = group.readEntry( "default-transport", QString() );
00339 if ( !name.isEmpty() ) {
00340 Transport *t = transportByName( name, false );
00341 if ( t ) {
00342 d->defaultTransportId = t->id();
00343 writeConfig();
00344 }
00345 }
00346 }
00347 validateDefault();
00348 migrateToWallet();
00349 }
00350
00351 void TransportManager::writeConfig()
00352 {
00353 KConfigGroup group( d->config, "General" );
00354 group.writeEntry( "default-transport", d->defaultTransportId );
00355 d->config->sync();
00356 emitChangesCommitted();
00357 }
00358
00359 void TransportManager::emitChangesCommitted()
00360 {
00361 d->myOwnChange = true;
00362 emit transportsChanged();
00363 emit changesCommitted();
00364 }
00365
00366 void TransportManager::slotTransportsChanged()
00367 {
00368 if ( d->myOwnChange ) {
00369 d->myOwnChange = false;
00370 return;
00371 }
00372
00373 kDebug();
00374 d->config->reparseConfiguration();
00375
00376 readConfig();
00377 emit transportsChanged();
00378 }
00379
00380 int TransportManager::createId() const
00381 {
00382 QList<int> usedIds;
00383 foreach ( Transport *t, d->transports ) {
00384 usedIds << t->id();
00385 }
00386 usedIds << 0;
00387 int newId;
00388 do {
00389 newId = KRandom::random();
00390 } while ( usedIds.contains( newId ) );
00391 return newId;
00392 }
00393
00394 KWallet::Wallet * TransportManager::wallet()
00395 {
00396 if ( d->wallet && d->wallet->isOpen() ) {
00397 return d->wallet;
00398 }
00399
00400 if ( !Wallet::isEnabled() || d->walletOpenFailed ) {
00401 return 0;
00402 }
00403
00404 WId window = 0;
00405 if ( qApp->activeWindow() ) {
00406 window = qApp->activeWindow()->winId();
00407 } else if ( !QApplication::topLevelWidgets().isEmpty() ) {
00408 window = qApp->topLevelWidgets().first()->winId();
00409 }
00410
00411 delete d->wallet;
00412 d->wallet = Wallet::openWallet( Wallet::NetworkWallet(), window );
00413
00414 if ( !d->wallet ) {
00415 d->walletOpenFailed = true;
00416 return 0;
00417 }
00418
00419 prepareWallet();
00420 return d->wallet;
00421 }
00422
00423 void TransportManager::prepareWallet()
00424 {
00425 if ( !d->wallet ) {
00426 return;
00427 }
00428 if ( !d->wallet->hasFolder( WALLET_FOLDER ) ) {
00429 d->wallet->createFolder( WALLET_FOLDER );
00430 }
00431 d->wallet->setFolder( WALLET_FOLDER );
00432 }
00433
00434 void TransportManager::loadPasswords()
00435 {
00436 foreach ( Transport *t, d->transports ) {
00437 t->readPassword();
00438 }
00439
00440
00441 foreach ( TransportJob *job, d->walletQueue ) {
00442 job->start();
00443 }
00444 d->walletQueue.clear();
00445
00446 emit passwordsChanged();
00447 }
00448
00449 void TransportManager::loadPasswordsAsync()
00450 {
00451 kDebug();
00452
00453
00454 bool found = false;
00455 foreach ( Transport *t, d->transports ) {
00456 if ( !t->isComplete() ) {
00457 found = true;
00458 break;
00459 }
00460 }
00461 if ( !found ) {
00462 return;
00463 }
00464
00465
00466 if ( !d->wallet && !d->walletOpenFailed ) {
00467 WId window = 0;
00468 if ( qApp->activeWindow() ) {
00469 window = qApp->activeWindow()->winId();
00470 } else if ( !QApplication::topLevelWidgets().isEmpty() ) {
00471 window = qApp->topLevelWidgets().first()->winId();
00472 }
00473
00474 d->wallet = Wallet::openWallet( Wallet::NetworkWallet(), window,
00475 Wallet::Asynchronous );
00476 if ( d->wallet ) {
00477 connect( d->wallet, SIGNAL(walletOpened(bool)), SLOT(slotWalletOpened(bool)) );
00478 d->walletAsyncOpen = true;
00479 } else {
00480 d->walletOpenFailed = true;
00481 loadPasswords();
00482 }
00483 return;
00484 }
00485 if ( d->wallet && !d->walletAsyncOpen ) {
00486 loadPasswords();
00487 }
00488 }
00489
00490 void TransportManager::slotWalletOpened( bool success )
00491 {
00492 kDebug();
00493 d->walletAsyncOpen = false;
00494 if ( !success ) {
00495 d->walletOpenFailed = true;
00496 delete d->wallet;
00497 d->wallet = 0;
00498 } else {
00499 prepareWallet();
00500 }
00501 loadPasswords();
00502 }
00503
00504 void TransportManager::validateDefault()
00505 {
00506 if ( !transportById( d->defaultTransportId, false ) ) {
00507 if ( isEmpty() ) {
00508 d->defaultTransportId = -1;
00509 } else {
00510 d->defaultTransportId = d->transports.first()->id();
00511 writeConfig();
00512 }
00513 }
00514 }
00515
00516 void TransportManager::migrateToWallet()
00517 {
00518
00519 static bool firstRun = true;
00520 if ( !firstRun ) {
00521 return;
00522 }
00523 firstRun = false;
00524
00525
00526 if ( !d->isMainInstance ) {
00527 return;
00528 }
00529
00530
00531 QStringList names;
00532 foreach ( Transport *t, d->transports ) {
00533 if ( t->needsWalletMigration() ) {
00534 names << t->name();
00535 }
00536 }
00537 if ( names.isEmpty() ) {
00538 return;
00539 }
00540
00541
00542 int result = KMessageBox::questionYesNoList(
00543 0,
00544 i18n( "The following mail transports store passwords in the configuration "
00545 "file instead in KWallet.\nIt is recommended to use KWallet for "
00546 "password storage for security reasons.\n"
00547 "Do you want to migrate your passwords to KWallet?" ),
00548 names, i18n( "Question" ),
00549 KGuiItem( i18n( "Migrate" ) ), KGuiItem( i18n( "Keep" ) ),
00550 QString::fromAscii( "WalletMigrate" ) );
00551 if ( result != KMessageBox::Yes ) {
00552 return;
00553 }
00554
00555
00556 foreach ( Transport *t, d->transports ) {
00557 if ( t->needsWalletMigration() ) {
00558 t->migrateToWallet();
00559 }
00560 }
00561 }
00562
00563 void TransportManager::dbusServiceOwnerChanged( const QString &service,
00564 const QString &oldOwner,
00565 const QString &newOwner )
00566 {
00567 Q_UNUSED( oldOwner );
00568 if ( service == DBUS_SERVICE_NAME && newOwner.isEmpty() ) {
00569 QDBusConnection::sessionBus().registerService( DBUS_SERVICE_NAME );
00570 }
00571 }
00572
00573 void TransportManager::jobResult( KJob *job )
00574 {
00575 d->walletQueue.removeAll( static_cast<TransportJob*>( job ) );
00576 }
00577
00578 #include "transportmanager.moc"