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