mailtransport
transport.cpp
00001 /* 00002 Copyright (c) 2006 - 2007 Volker Krause <vkrause@kde.org> 00003 00004 This library is free software; you can redistribute it and/or modify it 00005 under the terms of the GNU Library General Public License as published by 00006 the Free Software Foundation; either version 2 of the License, or (at your 00007 option) any later version. 00008 00009 This library is distributed in the hope that it will be useful, but WITHOUT 00010 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 00011 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public 00012 License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to the 00016 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 00017 02110-1301, USA. 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 // Set TransportType. 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 // Now we have the type and possibly agentType. Get the name, description 00192 // etc. from TransportManager. 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 // we have everything we need 00204 if ( !storePassword() || d->passwordLoaded ) { 00205 return; 00206 } 00207 00208 // try to find a password in the config file otherwise 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 // read password if wallet is open, defer otherwise 00227 if ( Wallet::isOpen( Wallet::NetworkWallet() ) ) { 00228 // Don't read the password right away because this can lead 00229 // to reentrancy problems in KDBusServiceStarter when an application 00230 // run in Kontact creates the transports (due to a QEventLoop in the 00231 // synchronous KWallet openWallet call). 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 // wallet saving failed, ask if we should store in the config file instead 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 // write to config file 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 // no need to load a password if the account doesn't require auth 00276 if ( !requiresAuthentication() ) { 00277 return; 00278 } 00279 d->passwordLoaded = true; 00280 00281 // check whether there is a chance to find our password at all 00282 if ( Wallet::folderDoesNotExist( Wallet::NetworkWallet(), WALLET_FOLDER ) || 00283 Wallet::keyDoesNotExist( Wallet::NetworkWallet(), WALLET_FOLDER, 00284 QString::number( id() ) ) ) { 00285 // try migrating password from kmail 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 // finally try to open the wallet and read the password 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"