00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "transport.h"
00021 #include "legacydecrypt.h"
00022 #include "mailtransport_defs.h"
00023 #include "transportmanager.h"
00024 #include "transporttype_p.h"
00025
00026 #include <QTimer>
00027
00028 #include <KConfigGroup>
00029 #include <KDebug>
00030 #include <KLocalizedString>
00031 #include <KMessageBox>
00032 #include <KStringHandler>
00033 #include <KWallet/Wallet>
00034
00035 #include <akonadi/agentinstance.h>
00036 #include <akonadi/agentmanager.h>
00037
00038 using namespace MailTransport;
00039 using namespace KWallet;
00040
00045 class TransportPrivate
00046 {
00047 public:
00048 TransportType transportType;
00049 QString password;
00050 bool passwordLoaded;
00051 bool passwordDirty;
00052 bool storePasswordInFile;
00053 bool needsWalletMigration;
00054 QString oldName;
00055 };
00056
00057 Transport::Transport( const QString &cfgGroup ) :
00058 TransportBase( cfgGroup ), d( new TransportPrivate )
00059 {
00060 kDebug() << cfgGroup;
00061 d->passwordLoaded = false;
00062 d->passwordDirty = false;
00063 d->storePasswordInFile = false;
00064 d->needsWalletMigration = false;
00065 readConfig();
00066 }
00067
00068 Transport::~Transport()
00069 {
00070 delete d;
00071 }
00072
00073 bool Transport::isValid() const
00074 {
00075 return ( id() > 0 ) && !host().isEmpty() && port() <= 65536;
00076 }
00077
00078 QString Transport::password()
00079 {
00080 if ( !d->passwordLoaded && requiresAuthentication() && storePassword() &&
00081 d->password.isEmpty() ) {
00082 readPassword();
00083 }
00084 return d->password;
00085 }
00086
00087 void Transport::setPassword( const QString &passwd )
00088 {
00089 d->passwordLoaded = true;
00090 if ( d->password == passwd ) {
00091 return;
00092 }
00093 d->passwordDirty = true;
00094 d->password = passwd;
00095 }
00096
00097 void Transport::forceUniqueName()
00098 {
00099 QStringList existingNames;
00100 foreach ( Transport *t, TransportManager::self()->transports() ) {
00101 if ( t->id() != id() ) {
00102 existingNames << t->name();
00103 }
00104 }
00105 int suffix = 1;
00106 QString origName = name();
00107 while ( existingNames.contains( name() ) ) {
00108 setName( i18nc( "%1: name; %2: number appended to it to make "
00109 "it unique among a list of names", "%1 #%2", origName, suffix ) );
00110 ++suffix;
00111 }
00112
00113 }
00114
00115 void Transport::updatePasswordState()
00116 {
00117 Transport *original = TransportManager::self()->transportById( id(), false );
00118 if ( original == this ) {
00119 kWarning() << "Tried to update password state of non-cloned transport.";
00120 return;
00121 }
00122 if ( original ) {
00123 d->password = original->d->password;
00124 d->passwordLoaded = original->d->passwordLoaded;
00125 d->passwordDirty = original->d->passwordDirty;
00126 } else {
00127 kWarning() << "Transport with this ID not managed by transport manager.";
00128 }
00129 }
00130
00131 bool Transport::isComplete() const
00132 {
00133 return !requiresAuthentication() || !storePassword() || d->passwordLoaded;
00134 }
00135
00136 QString Transport::authenticationTypeString() const
00137 {
00138 return Transport::authenticationTypeString( authenticationType() );
00139 }
00140
00141 QString Transport::authenticationTypeString( int type )
00142 {
00143 switch ( type ) {
00144 case EnumAuthenticationType::LOGIN:
00145 return QLatin1String( "LOGIN" );
00146 case EnumAuthenticationType::PLAIN:
00147 return QLatin1String( "PLAIN" );
00148 case EnumAuthenticationType::CRAM_MD5:
00149 return QLatin1String( "CRAM-MD5" );
00150 case EnumAuthenticationType::DIGEST_MD5:
00151 return QLatin1String( "DIGEST-MD5" );
00152 case EnumAuthenticationType::NTLM:
00153 return QLatin1String( "NTLM" );
00154 case EnumAuthenticationType::GSSAPI:
00155 return QLatin1String( "GSSAPI" );
00156 case EnumAuthenticationType::CLEAR:
00157 return i18nc( "Authentication method", "Clear text" );
00158 case EnumAuthenticationType::APOP:
00159 return QLatin1String( "APOP" );
00160 case EnumAuthenticationType::ANONYMOUS:
00161 return i18nc( "Authentication method", "Anonymous" );
00162 }
00163 Q_ASSERT( false );
00164 return QString();
00165 }
00166
00167 void Transport::usrReadConfig()
00168 {
00169 TransportBase::usrReadConfig();
00170
00171 setHost( host().trimmed() );
00172
00173 if ( d->oldName.isEmpty() ) {
00174 d->oldName = name();
00175 }
00176
00177
00178 {
00179 using namespace Akonadi;
00180 d->transportType = TransportType();
00181 d->transportType.d->mType = type();
00182 kDebug() << "type" << type();
00183 if ( type() == EnumType::Akonadi ) {
00184 const AgentInstance instance = AgentManager::self()->instance( host() );
00185 if ( !instance.isValid() ) {
00186 kWarning() << "Akonadi transport with invalid resource instance.";
00187 }
00188 d->transportType.d->mAgentType = instance.type();
00189 kDebug() << "agent type" << instance.type().name() << "id" << instance.type().identifier();
00190 }
00191
00192
00193 const TransportType::List &types = TransportManager::self()->types();
00194 int index = types.indexOf( d->transportType );
00195 if ( index != -1 ) {
00196 d->transportType = types[ index ];
00197 } else {
00198 kWarning() << "Type unknown to manager.";
00199 d->transportType.d->mName = i18nc( "An unknown transport type", "Unknown" );
00200 }
00201 }
00202
00203
00204 if ( !storePassword() || d->passwordLoaded ) {
00205 return;
00206 }
00207
00208
00209 KConfigGroup group( config(), currentGroup() );
00210 if ( group.hasKey( "password" ) ) {
00211 d->password = KStringHandler::obscure( group.readEntry( "password" ) );
00212 } else if ( group.hasKey( "password-kmail" ) ) {
00213 d->password = Legacy::decryptKMail( group.readEntry( "password-kmail" ) );
00214 } else if ( group.hasKey( "password-knode" ) ) {
00215 d->password = Legacy::decryptKNode( group.readEntry( "password-knode" ) );
00216 }
00217
00218 if ( !d->password.isEmpty() ) {
00219 d->passwordLoaded = true;
00220 if ( Wallet::isEnabled() ) {
00221 d->needsWalletMigration = true;
00222 } else {
00223 d->storePasswordInFile = true;
00224 }
00225 } else {
00226
00227 if ( Wallet::isOpen( Wallet::NetworkWallet() ) ) {
00228
00229
00230
00231
00232 QTimer::singleShot( 0, this, SLOT(readPassword()) );
00233 }
00234 }
00235 }
00236
00237 void Transport::usrWriteConfig()
00238 {
00239 if ( requiresAuthentication() && storePassword() && d->passwordDirty ) {
00240 Wallet *wallet = TransportManager::self()->wallet();
00241 if ( !wallet || wallet->writePassword( QString::number( id() ), d->password ) != 0 ) {
00242
00243 if ( d->storePasswordInFile || KMessageBox::warningYesNo(
00244 0,
00245 i18n( "KWallet is not available. It is strongly recommended to use "
00246 "KWallet for managing your passwords.\n"
00247 "However, the password can be stored in the configuration "
00248 "file instead. The password is stored in an obfuscated format, "
00249 "but should not be considered secure from decryption efforts "
00250 "if access to the configuration file is obtained.\n"
00251 "Do you want to store the password for server '%1' in the "
00252 "configuration file?", name() ),
00253 i18n( "KWallet Not Available" ),
00254 KGuiItem( i18n( "Store Password" ) ),
00255 KGuiItem( i18n( "Do Not Store Password" ) ) ) == KMessageBox::Yes ) {
00256
00257 KConfigGroup group( config(), currentGroup() );
00258 group.writeEntry( "password", KStringHandler::obscure( d->password ) );
00259 d->storePasswordInFile = true;
00260 }
00261 }
00262 d->passwordDirty = false;
00263 }
00264
00265 TransportBase::usrWriteConfig();
00266 TransportManager::self()->emitChangesCommitted();
00267 if ( name() != d->oldName ) {
00268 emit TransportManager::self()->transportRenamed( id(), d->oldName, name() );
00269 d->oldName = name();
00270 }
00271 }
00272
00273 void Transport::readPassword()
00274 {
00275
00276 if ( !requiresAuthentication() ) {
00277 return;
00278 }
00279 d->passwordLoaded = true;
00280
00281
00282 if ( Wallet::folderDoesNotExist( Wallet::NetworkWallet(), WALLET_FOLDER ) ||
00283 Wallet::keyDoesNotExist( Wallet::NetworkWallet(), WALLET_FOLDER,
00284 QString::number( id() ) ) ) {
00285
00286 if ( Wallet::folderDoesNotExist( Wallet::NetworkWallet(), KMAIL_WALLET_FOLDER ) ||
00287 Wallet::keyDoesNotExist( Wallet::NetworkWallet(), KMAIL_WALLET_FOLDER,
00288 QString::fromLatin1( "transport-%1" ).arg( id() ) ) ) {
00289 return;
00290 }
00291 kDebug() << "migrating password from kmail wallet";
00292 KWallet::Wallet *wallet = TransportManager::self()->wallet();
00293 if ( wallet ) {
00294 wallet->setFolder( KMAIL_WALLET_FOLDER );
00295 wallet->readPassword( QString::fromLatin1( "transport-%1" ).arg( id() ), d->password );
00296 wallet->removeEntry( QString::fromLatin1( "transport-%1" ).arg( id() ) );
00297 wallet->setFolder( WALLET_FOLDER );
00298 d->passwordDirty = true;
00299 writeConfig();
00300 }
00301 return;
00302 }
00303
00304
00305 KWallet::Wallet *wallet = TransportManager::self()->wallet();
00306 if ( wallet ) {
00307 wallet->readPassword( QString::number( id() ), d->password );
00308 }
00309 }
00310
00311 bool Transport::needsWalletMigration() const
00312 {
00313 return d->needsWalletMigration;
00314 }
00315
00316 void Transport::migrateToWallet()
00317 {
00318 kDebug() << "migrating" << id() << "to wallet";
00319 d->needsWalletMigration = false;
00320 KConfigGroup group( config(), currentGroup() );
00321 group.deleteEntry( "password" );
00322 group.deleteEntry( "password-kmail" );
00323 group.deleteEntry( "password-knode" );
00324 d->passwordDirty = true;
00325 d->storePasswordInFile = false;
00326 writeConfig();
00327 }
00328
00329 Transport *Transport::clone() const
00330 {
00331 QString id = currentGroup().mid( 10 );
00332 return new Transport( id );
00333 }
00334
00335 TransportType Transport::transportType() const
00336 {
00337 if ( !d->transportType.isValid() ) {
00338 kWarning() << "Invalid transport type.";
00339 }
00340 return d->transportType;
00341 }
00342
00343 void Transport::setTransportType( const TransportType &type )
00344 {
00345 Q_ASSERT( type.isValid() );
00346 d->transportType = type;
00347 setType( type.type() );
00348 }
00349
00350 #include "transport.moc"