identitymanager.cpp
00001 /* -*- mode: C++; c-file-style: "gnu" -*- 00002 identitymanager.cpp 00003 00004 This file is part of KMail, the KDE mail client. 00005 Copyright (c) 2002 Marc Mutz <mutz@kde.org> 00006 00007 KMail is free software; you can redistribute it and/or modify it 00008 under the terms of the GNU General Public License, version 2, as 00009 published by the Free Software Foundation. 00010 00011 KMail is distributed in the hope that it will be useful, but 00012 WITHOUT ANY WARRANTY; without even the implied warranty of 00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 General Public License for more details. 00015 00016 You should have received a copy of the GNU General Public License 00017 along with this program; if not, write to the Free Software 00018 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00019 00020 In addition, as a special exception, the copyright holders give 00021 permission to link the code of this program with any edition of 00022 the TQt library by Trolltech AS, Norway (or with modified versions 00023 of TQt that use the same license as TQt), and distribute linked 00024 combinations including the two. You must obey the GNU General 00025 Public License in all respects for all of the code used other than 00026 TQt. If you modify this file, you may extend this exception to 00027 your version of the file, but you are not obligated to do so. If 00028 you do not wish to do so, delete this exception statement from 00029 your version. 00030 */ 00031 00032 // config keys: 00033 static const char configKeyDefaultIdentity[] = "Default Identity"; 00034 00035 #ifdef HAVE_CONFIG_H 00036 #include <config.h> 00037 #endif 00038 00039 #include "identitymanager.h" 00040 00041 #include "identity.h" // for IdentityList::{export,import}Data 00042 #include <libemailfunctions/email.h> // for static helper functions 00043 00044 #include <kemailsettings.h> // for IdentityEntry::fromControlCenter() 00045 #include <kapplication.h> 00046 #include <klocale.h> 00047 #include <kdebug.h> 00048 #include <kconfig.h> 00049 #include <kuser.h> 00050 #include <dcopclient.h> 00051 00052 #include <tqregexp.h> 00053 00054 #include <assert.h> 00055 00056 using namespace KPIM; 00057 00058 static TQCString newDCOPObjectName() 00059 { 00060 static int s_count = 0; 00061 TQCString name( "KPIM::IdentityManager" ); 00062 if ( s_count++ ) { 00063 name += '-'; 00064 name += TQCString().setNum( s_count ); 00065 } 00066 return name; 00067 } 00068 00069 IdentityManager::IdentityManager( bool readonly, TQObject * parent, const char * name ) 00070 : ConfigManager( parent, name ), DCOPObject( newDCOPObjectName() ) 00071 { 00072 mReadOnly = readonly; 00073 mConfig = new KConfig( "emailidentities", readonly ); 00074 readConfig(mConfig); 00075 if ( mIdentities.isEmpty() ) { 00076 kdDebug(5006) << "emailidentities is empty -> convert from kmailrc" << endl; 00077 // No emailidentities file, or an empty one due to broken conversion (kconf_update bug in kdelibs <= 3.2.2) 00078 // => convert it, i.e. read settings from kmailrc 00079 KConfig kmailConf( "kmailrc", true ); 00080 readConfig( &kmailConf ); 00081 } 00082 // we need at least a default identity: 00083 if ( mIdentities.isEmpty() ) { 00084 kdDebug( 5006 ) << "IdentityManager: No identity found. Creating default." << endl; 00085 createDefaultIdentity(); 00086 commit(); 00087 } 00088 // Migration: people without settings in kemailsettings should get some 00089 if ( KEMailSettings().getSetting( KEMailSettings::EmailAddress ).isEmpty() ) { 00090 writeConfig(); 00091 } 00092 00093 // The emitter is always called KPIM::IdentityManager even if we are not 00094 if ( !connectDCOPSignal( 0, "KPIM::IdentityManager", "identitiesChanged(TQCString,TQCString)", 00095 "slotIdentitiesChanged(TQCString,TQCString)", false ) ) 00096 kdError(5650) << "IdentityManager: connection to identitiesChanged failed" << endl; 00097 } 00098 00099 IdentityManager::~IdentityManager() 00100 { 00101 kdWarning( hasPendingChanges(), 5006 ) 00102 << "IdentityManager: There were uncommitted changes!" << endl; 00103 delete mConfig; 00104 } 00105 00106 void IdentityManager::commit() 00107 { 00108 // early out: 00109 if ( !hasPendingChanges() || mReadOnly ) return; 00110 00111 TQValueList<uint> seenUOIDs; 00112 for ( TQValueList<Identity>::ConstIterator it = mIdentities.begin() ; 00113 it != mIdentities.end() ; ++it ) 00114 seenUOIDs << (*it).uoid(); 00115 00116 TQValueList<uint> changedUOIDs; 00117 // find added and changed identities: 00118 for ( TQValueList<Identity>::ConstIterator it = mShadowIdentities.begin() ; 00119 it != mShadowIdentities.end() ; ++it ) { 00120 TQValueList<uint>::Iterator uoid = seenUOIDs.find( (*it).uoid() ); 00121 if ( uoid != seenUOIDs.end() ) { 00122 const Identity & orig = identityForUoid( *uoid ); // look it up in mIdentities 00123 if ( *it != orig ) { 00124 // changed identity 00125 kdDebug( 5006 ) << "emitting changed() for identity " << *uoid << endl; 00126 emit changed( *it ); 00127 changedUOIDs << *uoid; 00128 } 00129 seenUOIDs.remove( uoid ); 00130 } else { 00131 // new identity 00132 kdDebug( 5006 ) << "emitting added() for identity " << (*it).uoid() << endl; 00133 emit added( *it ); 00134 } 00135 } 00136 00137 // what's left are deleted identities: 00138 for ( TQValueList<uint>::ConstIterator it = seenUOIDs.begin() ; 00139 it != seenUOIDs.end() ; ++it ) { 00140 kdDebug( 5006 ) << "emitting deleted() for identity " << (*it) << endl; 00141 emit deleted( *it ); 00142 } 00143 00144 mIdentities = mShadowIdentities; 00145 writeConfig(); 00146 00147 // now that mIdentities has all the new info, we can emit the added/changed 00148 // signals that ship a uoid. This is because the slots might use identityForUoid(uoid)... 00149 for ( TQValueList<uint>::ConstIterator it = changedUOIDs.begin() ; 00150 it != changedUOIDs.end() ; ++it ) 00151 emit changed( *it ); 00152 00153 emit ConfigManager::changed(); // normal signal 00154 00155 // DCOP signal for other IdentityManager instances 00156 // The emitter is always set to KPIM::IdentityManager, so that the connect works 00157 // This is why we can't use k_dcop_signals here, but need to use emitDCOPSignal 00158 TQByteArray data; TQDataStream arg( data, IO_WriteOnly ); 00159 arg << kapp->dcopClient()->appId(); 00160 arg << DCOPObject::objId(); // the real objId, for checking in slotIdentitiesChanged 00161 kapp->dcopClient()->emitDCOPSignal( "KPIM::IdentityManager", "identitiesChanged(TQCString,TQCString)", data ); 00162 } 00163 00164 void IdentityManager::rollback() 00165 { 00166 mShadowIdentities = mIdentities; 00167 } 00168 00169 bool IdentityManager::hasPendingChanges() const 00170 { 00171 return mIdentities != mShadowIdentities; 00172 } 00173 00174 TQStringList IdentityManager::identities() const 00175 { 00176 TQStringList result; 00177 for ( ConstIterator it = mIdentities.begin() ; 00178 it != mIdentities.end() ; ++it ) 00179 result << (*it).identityName(); 00180 return result; 00181 } 00182 00183 TQStringList IdentityManager::shadowIdentities() const 00184 { 00185 TQStringList result; 00186 for ( ConstIterator it = mShadowIdentities.begin() ; 00187 it != mShadowIdentities.end() ; ++it ) 00188 result << (*it).identityName(); 00189 return result; 00190 } 00191 00192 void IdentityManager::sort() { 00193 qHeapSort( mShadowIdentities ); 00194 } 00195 00196 void IdentityManager::writeConfig() const { 00197 TQStringList identities = groupList(mConfig); 00198 for ( TQStringList::Iterator group = identities.begin() ; 00199 group != identities.end() ; ++group ) 00200 mConfig->deleteGroup( *group ); 00201 int i = 0; 00202 for ( ConstIterator it = mIdentities.begin() ; 00203 it != mIdentities.end() ; ++it, ++i ) { 00204 KConfigGroup cg( mConfig, TQString::fromLatin1("Identity #%1").arg(i) ); 00205 (*it).writeConfig( &cg ); 00206 if ( (*it).isDefault() ) { 00207 // remember which one is default: 00208 KConfigGroup general( mConfig, "General" ); 00209 general.writeEntry( configKeyDefaultIdentity, (*it).uoid() ); 00210 00211 // Also write the default identity to emailsettings 00212 KEMailSettings es; 00213 es.setSetting( KEMailSettings::RealName, (*it).fullName() ); 00214 es.setSetting( KEMailSettings::EmailAddress, (*it).primaryEmailAddress() ); 00215 es.setSetting( KEMailSettings::Organization, (*it).organization() ); 00216 es.setSetting( KEMailSettings::ReplyToAddress, (*it).replyToAddr() ); 00217 } 00218 } 00219 mConfig->sync(); 00220 00221 } 00222 00223 void IdentityManager::readConfig(KConfigBase* config) { 00224 mIdentities.clear(); 00225 00226 TQStringList identities = groupList(config); 00227 if ( identities.isEmpty() ) return; // nothing to be done... 00228 00229 KConfigGroup general( config, "General" ); 00230 uint defaultIdentity = general.readUnsignedNumEntry( configKeyDefaultIdentity ); 00231 bool haveDefault = false; 00232 00233 for ( TQStringList::Iterator group = identities.begin() ; 00234 group != identities.end() ; ++group ) { 00235 KConfigGroup configGroup( config, *group ); 00236 mIdentities << Identity(); 00237 mIdentities.last().readConfig( &configGroup ); 00238 if ( !haveDefault && mIdentities.last().uoid() == defaultIdentity ) { 00239 haveDefault = true; 00240 mIdentities.last().setIsDefault( true ); 00241 } 00242 } 00243 if ( !haveDefault ) { 00244 kdWarning( 5006 ) << "IdentityManager: There was no default identity. Marking first one as default." << endl; 00245 mIdentities.first().setIsDefault( true ); 00246 } 00247 qHeapSort( mIdentities ); 00248 00249 mShadowIdentities = mIdentities; 00250 } 00251 00252 TQStringList IdentityManager::groupList(KConfigBase* config) const { 00253 return config->groupList().grep( TQRegExp("^Identity #\\d+$") ); 00254 } 00255 00256 IdentityManager::ConstIterator IdentityManager::begin() const { 00257 return mIdentities.begin(); 00258 } 00259 00260 IdentityManager::ConstIterator IdentityManager::end() const { 00261 return mIdentities.end(); 00262 } 00263 00264 IdentityManager::Iterator IdentityManager::modifyBegin() { 00265 return mShadowIdentities.begin(); 00266 } 00267 00268 IdentityManager::Iterator IdentityManager::modifyEnd() { 00269 return mShadowIdentities.end(); 00270 } 00271 00272 const Identity & IdentityManager::identityForName( const TQString & name ) const 00273 { 00274 kdWarning( 5006 ) 00275 << "deprecated method IdentityManager::identityForName() called!" << endl; 00276 for ( ConstIterator it = begin() ; it != end() ; ++it ) 00277 if ( (*it).identityName() == name ) return (*it); 00278 return Identity::null(); 00279 } 00280 00281 const Identity & IdentityManager::identityForUoid( uint uoid ) const { 00282 for ( ConstIterator it = begin() ; it != end() ; ++it ) 00283 if ( (*it).uoid() == uoid ) return (*it); 00284 return Identity::null(); 00285 } 00286 00287 const Identity & IdentityManager::identityForNameOrDefault( const TQString & name ) const 00288 { 00289 const Identity & ident = identityForName( name ); 00290 if ( ident.isNull() ) 00291 return defaultIdentity(); 00292 else 00293 return ident; 00294 } 00295 00296 const Identity & IdentityManager::identityForUoidOrDefault( uint uoid ) const 00297 { 00298 const Identity & ident = identityForUoid( uoid ); 00299 if ( ident.isNull() ) 00300 return defaultIdentity(); 00301 else 00302 return ident; 00303 } 00304 00305 const Identity & IdentityManager::identityForAddress( const TQString & addresses ) const 00306 { 00307 const TQStringList addressList = KPIM::splitEmailAddrList( addresses ); 00308 for( TQStringList::ConstIterator addrIt = addressList.begin(); 00309 addrIt != addressList.end(); ++addrIt ) { 00310 const TQString addr = KPIM::getEmailAddress( *addrIt ).lower(); 00311 for ( ConstIterator it = begin() ; it != end() ; ++it ) { 00312 const Identity & id = *it; 00313 if ( id.matchesEmailAddress( addr ) ) { 00314 return id; 00315 } 00316 } 00317 } 00318 return Identity::null(); 00319 } 00320 00321 bool IdentityManager::thatIsMe( const TQString & addressList ) const { 00322 return !identityForAddress( addressList ).isNull(); 00323 } 00324 00325 Identity & IdentityManager::modifyIdentityForName( const TQString & name ) 00326 { 00327 for ( Iterator it = modifyBegin() ; it != modifyEnd() ; ++it ) 00328 if ( (*it).identityName() == name ) return (*it); 00329 kdWarning( 5006 ) << "IdentityManager::identityForName() used as newFromScratch() replacement!" 00330 << "\n name == \"" << name << "\"" << endl; 00331 return newFromScratch( name ); 00332 } 00333 00334 Identity & IdentityManager::modifyIdentityForUoid( uint uoid ) 00335 { 00336 for ( Iterator it = modifyBegin() ; it != modifyEnd() ; ++it ) 00337 if ( (*it).uoid() == uoid ) return (*it); 00338 kdWarning( 5006 ) << "IdentityManager::identityForUoid() used as newFromScratch() replacement!" 00339 << "\n uoid == \"" << uoid << "\"" << endl; 00340 return newFromScratch( i18n("Unnamed") ); 00341 } 00342 00343 const Identity & IdentityManager::defaultIdentity() const { 00344 for ( ConstIterator it = begin() ; it != end() ; ++it ) 00345 if ( (*it).isDefault() ) return (*it); 00346 (mIdentities.isEmpty() ? kdFatal( 5006 ) : kdWarning( 5006 ) ) 00347 << "IdentityManager: No default identity found!" << endl; 00348 return *begin(); 00349 } 00350 00351 bool IdentityManager::setAsDefault( const TQString & name ) { 00352 // First, check if the identity actually exists: 00353 TQStringList names = shadowIdentities(); 00354 if ( names.find( name ) == names.end() ) return false; 00355 // Then, change the default as requested: 00356 for ( Iterator it = modifyBegin() ; it != modifyEnd() ; ++it ) 00357 (*it).setIsDefault( (*it).identityName() == name ); 00358 // and re-sort: 00359 sort(); 00360 return true; 00361 } 00362 00363 bool IdentityManager::setAsDefault( uint uoid ) { 00364 // First, check if the identity actually exists: 00365 bool found = false; 00366 for ( ConstIterator it = mShadowIdentities.begin() ; 00367 it != mShadowIdentities.end() ; ++it ) 00368 if ( (*it).uoid() == uoid ) { 00369 found = true; 00370 break; 00371 } 00372 if ( !found ) return false; 00373 00374 // Then, change the default as requested: 00375 for ( Iterator it = modifyBegin() ; it != modifyEnd() ; ++it ) 00376 (*it).setIsDefault( (*it).uoid() == uoid ); 00377 // and re-sort: 00378 sort(); 00379 return true; 00380 } 00381 00382 bool IdentityManager::removeIdentity( const TQString & name ) { 00383 for ( Iterator it = modifyBegin() ; it != modifyEnd() ; ++it ) 00384 if ( (*it).identityName() == name ) { 00385 bool removedWasDefault = (*it).isDefault(); 00386 mShadowIdentities.remove( it ); 00387 if ( removedWasDefault ) 00388 mShadowIdentities.first().setIsDefault( true ); 00389 return true; 00390 } 00391 return false; 00392 } 00393 00394 Identity & IdentityManager::newFromScratch( const TQString & name ) { 00395 return newFromExisting( Identity( name ) ); 00396 } 00397 00398 Identity & IdentityManager::newFromControlCenter( const TQString & name ) { 00399 KEMailSettings es; 00400 es.setProfile( es.defaultProfileName() ); 00401 00402 return newFromExisting( Identity( name, 00403 es.getSetting( KEMailSettings::RealName ), 00404 es.getSetting( KEMailSettings::EmailAddress ), 00405 es.getSetting( KEMailSettings::Organization ), 00406 es.getSetting( KEMailSettings::ReplyToAddress ) 00407 ) ); 00408 } 00409 00410 Identity & IdentityManager::newFromExisting( const Identity & other, 00411 const TQString & name ) { 00412 mShadowIdentities << other; 00413 Identity & result = mShadowIdentities.last(); 00414 result.setIsDefault( false ); // we don't want two default identities! 00415 result.setUoid( newUoid() ); // we don't want two identies w/ same UOID 00416 if ( !name.isNull() ) 00417 result.setIdentityName( name ); 00418 return result; 00419 } 00420 00421 void IdentityManager::createDefaultIdentity() { 00422 TQString fullName, emailAddress; 00423 bool done = false; 00424 00425 // Check if the application has any settings 00426 createDefaultIdentity( fullName, emailAddress ); 00427 00428 // If not, then use the kcontrol settings 00429 if ( fullName.isEmpty() && emailAddress.isEmpty() ) { 00430 KEMailSettings emailSettings; 00431 fullName = emailSettings.getSetting( KEMailSettings::RealName ); 00432 emailAddress = emailSettings.getSetting( KEMailSettings::EmailAddress ); 00433 00434 if ( !fullName.isEmpty() && !emailAddress.isEmpty() ) { 00435 newFromControlCenter( i18n("Default") ); 00436 done = true; 00437 } else { 00438 // If KEmailSettings doesn't have name and address, generate something from KUser 00439 KUser user; 00440 if ( fullName.isEmpty() ) 00441 fullName = user.fullName(); 00442 if ( emailAddress.isEmpty() ) { 00443 emailAddress = user.loginName(); 00444 if ( !emailAddress.isEmpty() ) { 00445 KConfigGroup general( mConfig, "General" ); 00446 TQString defaultdomain = general.readEntry( "Default domain" ); 00447 if( !defaultdomain.isEmpty() ) { 00448 emailAddress += '@' + defaultdomain; 00449 } 00450 else { 00451 emailAddress = TQString(); 00452 } 00453 } 00454 } 00455 } 00456 } 00457 00458 if ( !done ) 00459 mShadowIdentities << Identity( i18n("Default"), fullName, emailAddress ); 00460 00461 mShadowIdentities.last().setIsDefault( true ); 00462 mShadowIdentities.last().setUoid( newUoid() ); 00463 if ( mReadOnly ) // commit won't do it in readonly mode 00464 mIdentities = mShadowIdentities; 00465 } 00466 00467 int IdentityManager::newUoid() 00468 { 00469 int uoid; 00470 00471 // determine the UOIDs of all saved identities 00472 TQValueList<uint> usedUOIDs; 00473 for ( TQValueList<Identity>::ConstIterator it = mIdentities.begin() ; 00474 it != mIdentities.end() ; ++it ) 00475 usedUOIDs << (*it).uoid(); 00476 00477 if ( hasPendingChanges() ) { 00478 // add UOIDs of all shadow identities. Yes, we will add a lot of duplicate 00479 // UOIDs, but avoiding duplicate UOIDs isn't worth the effort. 00480 for ( TQValueList<Identity>::ConstIterator it = mShadowIdentities.begin() ; 00481 it != mShadowIdentities.end() ; ++it ) { 00482 usedUOIDs << (*it).uoid(); 00483 } 00484 } 00485 00486 usedUOIDs << 0; // no UOID must be 0 because this value always refers to the 00487 // default identity 00488 00489 do { 00490 uoid = kapp->random(); 00491 } while ( usedUOIDs.find( uoid ) != usedUOIDs.end() ); 00492 00493 return uoid; 00494 } 00495 00496 TQStringList KPIM::IdentityManager::allEmails() const 00497 { 00498 TQStringList lst; 00499 for ( ConstIterator it = begin() ; it != end() ; ++it ) { 00500 lst << (*it).primaryEmailAddress(); 00501 } 00502 return lst; 00503 } 00504 00505 void KPIM::IdentityManager::slotIdentitiesChanged( TQCString appId, TQCString objId ) 00506 { 00507 // From standalone kmail to standalone korganizer, the appId will differ 00508 // From kontact the appId will match, so we need to test the objId 00509 if ( kapp->dcopClient()->appId() != appId || DCOPObject::objId() != objId ) { 00510 mConfig->reparseConfiguration(); 00511 Q_ASSERT( !hasPendingChanges() ); 00512 readConfig( mConfig ); 00513 } 00514 } 00515 00516 #include "identitymanager.moc"