kpimidentities
identitymanager.cpp
00001 /* 00002 Copyright (c) 2002 Marc Mutz <mutz@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 // config keys: 00021 static const char configKeyDefaultIdentity[] = "Default Identity"; 00022 00023 #include "identitymanager.h" 00024 #include "identity.h" // for IdentityList::{export,import}Data 00025 00026 #include <kpimutils/email.h> // for static helper functions 00027 00028 #include <kemailsettings.h> // for IdentityEntry::fromControlCenter() 00029 #include <klocale.h> 00030 #include <kglobal.h> 00031 #include <kdebug.h> 00032 #include <kconfig.h> 00033 #include <kuser.h> 00034 #include <kconfiggroup.h> 00035 00036 #include <QList> 00037 #include <QRegExp> 00038 #include <QtDBus/QtDBus> 00039 00040 #include <assert.h> 00041 #include <krandom.h> 00042 00043 #include "identitymanageradaptor.h" 00044 00045 using namespace KPIMIdentities; 00046 00047 static QString newDBusObjectName() 00048 { 00049 static int s_count = 0; 00050 QString name( "/KPIMIDENTITIES_IdentityManager" ); 00051 if ( s_count++ ) { 00052 name += '_'; 00053 name += QString::number( s_count ); 00054 } 00055 return name; 00056 } 00057 00058 IdentityManager::IdentityManager( bool readonly, QObject *parent, 00059 const char *name ) 00060 : QObject( parent ) 00061 { 00062 setObjectName( name ); 00063 KGlobal::locale()->insertCatalog("libkpimidentities"); 00064 new IdentityManagerAdaptor( this ); 00065 QDBusConnection dbus = QDBusConnection::sessionBus(); 00066 const QString dbusPath = newDBusObjectName(); 00067 setProperty( "uniqueDBusPath", dbusPath ); 00068 const QString dbusInterface = "org.kde.pim.IdentityManager"; 00069 dbus.registerObject( dbusPath, this ); 00070 dbus.connect( QString(), QString(), dbusInterface, "identitiesChanged", this, 00071 SLOT( slotIdentitiesChanged( QString ) ) ); 00072 00073 mReadOnly = readonly; 00074 mConfig = new KConfig( "emailidentities" ); 00075 readConfig( mConfig ); 00076 if ( mIdentities.isEmpty() ) { 00077 kDebug( 5325 ) << "emailidentities is empty -> convert from kmailrc"; 00078 // No emailidentities file, or an empty one due to broken conversion 00079 // (kconf_update bug in kdelibs <= 3.2.2) 00080 // => convert it, i.e. read settings from kmailrc 00081 KConfig kmailConf( "kmailrc" ); 00082 readConfig( &kmailConf ); 00083 } 00084 // we need at least a default identity: 00085 if ( mIdentities.isEmpty() ) { 00086 kDebug( 5325 ) << "IdentityManager: No identity found. Creating default."; 00087 createDefaultIdentity(); 00088 commit(); 00089 } 00090 // Migration: people without settings in kemailsettings should get some 00091 if ( KEMailSettings().getSetting( KEMailSettings::EmailAddress ).isEmpty() ) { 00092 writeConfig(); 00093 } 00094 } 00095 00096 IdentityManager::~IdentityManager() 00097 { 00098 kWarning( hasPendingChanges(), 5325 ) 00099 << "IdentityManager: There were uncommitted changes!"; 00100 delete mConfig; 00101 } 00102 00103 QString IdentityManager::makeUnique( const QString &name ) const 00104 { 00105 int suffix = 1; 00106 QString result = name; 00107 while ( identities().contains( result ) ) { 00108 result = i18nc( "%1: name; %2: number appended to it to make it unique " 00109 "among a list of names", "%1 #%2", 00110 name, suffix ); 00111 suffix++; 00112 } 00113 return result; 00114 } 00115 00116 bool IdentityManager::isUnique( const QString &name ) const 00117 { 00118 return !identities().contains( name ); 00119 } 00120 00121 void IdentityManager::commit() 00122 { 00123 // early out: 00124 if ( !hasPendingChanges() || mReadOnly ) { 00125 return; 00126 } 00127 00128 QList<uint> seenUOIDs; 00129 for ( QList<Identity>::ConstIterator it = mIdentities.constBegin(); 00130 it != mIdentities.constEnd(); ++it ) { 00131 seenUOIDs << (*it).uoid(); 00132 } 00133 00134 QList<uint> changedUOIDs; 00135 // find added and changed identities: 00136 for ( QList<Identity>::ConstIterator it = mShadowIdentities.constBegin(); 00137 it != mShadowIdentities.constEnd(); ++it ) { 00138 int index = seenUOIDs.indexOf( (*it).uoid() ); 00139 if ( index != -1 ) { 00140 uint uoid = seenUOIDs.at( index ); 00141 const Identity &orig = identityForUoid( uoid ); // look up in mIdentities 00142 if ( *it != orig ) { 00143 // changed identity 00144 kDebug( 5325 ) << "emitting changed() for identity" << uoid; 00145 emit changed(*it); 00146 changedUOIDs << uoid; 00147 } 00148 seenUOIDs.removeAll( uoid ); 00149 } else { 00150 // new identity 00151 kDebug( 5325 ) << "emitting added() for identity" << (*it).uoid(); 00152 emit added(*it); 00153 } 00154 } 00155 00156 // what's left are deleted identities: 00157 for ( QList<uint>::ConstIterator it = seenUOIDs.constBegin(); 00158 it != seenUOIDs.constEnd(); ++it ) { 00159 kDebug( 5325 ) << "emitting deleted() for identity" << (*it); 00160 emit deleted(*it); 00161 } 00162 00163 mIdentities = mShadowIdentities; 00164 writeConfig(); 00165 00166 // now that mIdentities has all the new info, we can emit the added/changed 00167 // signals that ship a uoid. This is because the slots might use 00168 // identityForUoid(uoid)... 00169 for ( QList<uint>::ConstIterator it = changedUOIDs.constBegin(); 00170 it != changedUOIDs.constEnd(); ++it ) 00171 emit changed(*it); 00172 00173 emit changed(); // normal signal 00174 00175 // DBus signal for other IdentityManager instances 00176 const QString ourIdentifier = QString::fromLatin1( "%1/%2" ).arg( QDBusConnection::sessionBus().baseService() ) 00177 .arg( property( "uniqueDBusPath" ).toString() ); 00178 emit identitiesChanged( ourIdentifier ); 00179 } 00180 00181 void IdentityManager::rollback() 00182 { 00183 mShadowIdentities = mIdentities; 00184 } 00185 00186 bool IdentityManager::hasPendingChanges() const 00187 { 00188 return mIdentities != mShadowIdentities; 00189 } 00190 00191 QStringList IdentityManager::identities() const 00192 { 00193 QStringList result; 00194 for ( ConstIterator it = mIdentities.begin(); 00195 it != mIdentities.end(); ++it ) 00196 result << (*it).identityName(); 00197 return result; 00198 } 00199 00200 QStringList IdentityManager::shadowIdentities() const 00201 { 00202 QStringList result; 00203 for ( ConstIterator it = mShadowIdentities.begin(); 00204 it != mShadowIdentities.end(); ++it ) 00205 result << (*it).identityName(); 00206 return result; 00207 } 00208 00209 void IdentityManager::sort() 00210 { 00211 qSort( mShadowIdentities ); 00212 } 00213 00214 void IdentityManager::writeConfig() const 00215 { 00216 const QStringList identities = groupList( mConfig ); 00217 for ( QStringList::const_iterator group = identities.begin(); 00218 group != identities.end(); ++group ) 00219 mConfig->deleteGroup( *group ); 00220 int i = 0; 00221 for ( ConstIterator it = mIdentities.begin(); 00222 it != mIdentities.end(); ++it, ++i ) { 00223 KConfigGroup cg( mConfig, QString::fromLatin1( "Identity #%1" ).arg( i ) ); 00224 (*it).writeConfig( cg ); 00225 if ( (*it).isDefault() ) { 00226 // remember which one is default: 00227 KConfigGroup general( mConfig, "General" ); 00228 general.writeEntry( configKeyDefaultIdentity, (*it).uoid() ); 00229 00230 // Also write the default identity to emailsettings 00231 KEMailSettings es; 00232 es.setSetting( KEMailSettings::RealName, (*it).fullName() ); 00233 es.setSetting( KEMailSettings::EmailAddress, (*it).primaryEmailAddress() ); 00234 es.setSetting( KEMailSettings::Organization, (*it).organization() ); 00235 es.setSetting( KEMailSettings::ReplyToAddress, (*it).replyToAddr() ); 00236 } 00237 } 00238 mConfig->sync(); 00239 00240 } 00241 00242 void IdentityManager::readConfig( KConfig *config ) 00243 { 00244 mIdentities.clear(); 00245 00246 const QStringList identities = groupList( config ); 00247 if ( identities.isEmpty() ) { 00248 return; // nothing to be done... 00249 } 00250 00251 KConfigGroup general( config, "General" ); 00252 uint defaultIdentity = general.readEntry( configKeyDefaultIdentity, 0 ); 00253 bool haveDefault = false; 00254 00255 for ( QStringList::const_iterator group = identities.begin(); 00256 group != identities.end(); ++group ) { 00257 KConfigGroup configGroup( config, *group ); 00258 mIdentities << Identity(); 00259 mIdentities.last().readConfig( configGroup ); 00260 if ( !haveDefault && mIdentities.last().uoid() == defaultIdentity ) { 00261 haveDefault = true; 00262 mIdentities.last().setIsDefault( true ); 00263 } 00264 } 00265 if ( !haveDefault ) { 00266 kWarning( 5325 ) << "IdentityManager: There was no default identity." 00267 << "Marking first one as default."; 00268 mIdentities.first().setIsDefault( true ); 00269 } 00270 qSort( mIdentities ); 00271 00272 mShadowIdentities = mIdentities; 00273 } 00274 00275 QStringList IdentityManager::groupList( KConfig *config ) const 00276 { 00277 return config->groupList().filter( QRegExp( "^Identity #\\d+$" ) ); 00278 } 00279 00280 IdentityManager::ConstIterator IdentityManager::begin() const 00281 { 00282 return mIdentities.begin(); 00283 } 00284 00285 IdentityManager::ConstIterator IdentityManager::end() const 00286 { 00287 return mIdentities.end(); 00288 } 00289 00290 IdentityManager::Iterator IdentityManager::modifyBegin() 00291 { 00292 return mShadowIdentities.begin(); 00293 } 00294 00295 IdentityManager::Iterator IdentityManager::modifyEnd() 00296 { 00297 return mShadowIdentities.end(); 00298 } 00299 00300 const Identity &IdentityManager::identityForUoid( uint uoid ) const 00301 { 00302 for ( ConstIterator it = begin(); it != end(); ++it ) { 00303 if ( (*it).uoid() == uoid ) { 00304 return (*it); 00305 } 00306 } 00307 return Identity::null(); 00308 } 00309 00310 const Identity &IdentityManager::identityForUoidOrDefault( uint uoid ) const 00311 { 00312 const Identity &ident = identityForUoid( uoid ); 00313 if ( ident.isNull() ) { 00314 return defaultIdentity(); 00315 } else { 00316 return ident; 00317 } 00318 } 00319 00320 const Identity &IdentityManager::identityForAddress( 00321 const QString &addresses ) const 00322 { 00323 const QStringList addressList = KPIMUtils::splitAddressList( addresses ); 00324 foreach ( const QString &fullAddress, addressList ) { 00325 const QString addrSpec = KPIMUtils::extractEmailAddress( fullAddress ).toLower(); 00326 for ( ConstIterator it = begin(); it != end(); ++it ) { 00327 const Identity &identity = *it; 00328 if ( identity.matchesEmailAddress( addrSpec ) ) 00329 return identity; 00330 } 00331 } 00332 return Identity::null(); 00333 } 00334 00335 bool IdentityManager::thatIsMe( const QString &addressList ) const 00336 { 00337 return !identityForAddress( addressList ).isNull(); 00338 } 00339 00340 Identity &IdentityManager::modifyIdentityForName( const QString &name ) 00341 { 00342 for ( Iterator it = modifyBegin(); it != modifyEnd(); ++it ) { 00343 if ( (*it).identityName() == name ) { 00344 return (*it); 00345 } 00346 } 00347 00348 kWarning( 5325 ) << "IdentityManager::modifyIdentityForName() used as" 00349 << "newFromScratch() replacement!" 00350 << endl << " name == \"" << name << "\""; 00351 return newFromScratch( name ); 00352 } 00353 00354 Identity &IdentityManager::modifyIdentityForUoid( uint uoid ) 00355 { 00356 for ( Iterator it = modifyBegin(); it != modifyEnd(); ++it ) { 00357 if ( (*it).uoid() == uoid ) { 00358 return (*it); 00359 } 00360 } 00361 00362 kWarning( 5325 ) << "IdentityManager::identityForUoid() used as" 00363 << "newFromScratch() replacement!" 00364 << endl << " uoid == \"" << uoid << "\""; 00365 return newFromScratch( i18n( "Unnamed" ) ); 00366 } 00367 00368 const Identity &IdentityManager::defaultIdentity() const 00369 { 00370 for ( ConstIterator it = begin(); it != end(); ++it ) { 00371 if ( (*it).isDefault() ) { 00372 return (*it); 00373 } 00374 } 00375 00376 if ( mIdentities.isEmpty() ) 00377 kFatal( 5325 ) << "IdentityManager: No default identity found!"; 00378 else 00379 kWarning( 5325 ) << "IdentityManager: No default identity found!"; 00380 return *begin(); 00381 } 00382 00383 bool IdentityManager::setAsDefault( uint uoid ) 00384 { 00385 // First, check if the identity actually exists: 00386 bool found = false; 00387 for ( ConstIterator it = mShadowIdentities.constBegin(); 00388 it != mShadowIdentities.constEnd(); ++it ) 00389 if ( (*it).uoid() == uoid ) { 00390 found = true; 00391 break; 00392 } 00393 00394 if ( !found ) { 00395 return false; 00396 } 00397 00398 // Then, change the default as requested: 00399 for ( Iterator it = modifyBegin(); it != modifyEnd(); ++it ) { 00400 (*it).setIsDefault( (*it).uoid() == uoid ); 00401 } 00402 00403 // and re-sort: 00404 sort(); 00405 return true; 00406 } 00407 00408 bool IdentityManager::removeIdentity( const QString &name ) 00409 { 00410 if ( mShadowIdentities.size() <= 1 ) 00411 return false; 00412 00413 for ( Iterator it = modifyBegin(); it != modifyEnd(); ++it ) { 00414 if ( (*it).identityName() == name ) { 00415 bool removedWasDefault = (*it).isDefault(); 00416 mShadowIdentities.erase( it ); 00417 if ( removedWasDefault ) { 00418 mShadowIdentities.first().setIsDefault( true ); 00419 } 00420 return true; 00421 } 00422 } 00423 return false; 00424 } 00425 00426 bool IdentityManager::removeIdentityForced( const QString &name ) 00427 { 00428 for ( Iterator it = modifyBegin(); it != modifyEnd(); ++it ) { 00429 if ( (*it).identityName() == name ) { 00430 bool removedWasDefault = (*it).isDefault(); 00431 mShadowIdentities.erase( it ); 00432 if ( removedWasDefault && !mShadowIdentities.isEmpty() ) { 00433 mShadowIdentities.first().setIsDefault( true ); 00434 } 00435 return true; 00436 } 00437 } 00438 return false; 00439 } 00440 00441 Identity &IdentityManager::newFromScratch( const QString &name ) 00442 { 00443 return newFromExisting( Identity( name ) ); 00444 } 00445 00446 Identity &IdentityManager::newFromControlCenter( const QString &name ) 00447 { 00448 KEMailSettings es; 00449 es.setProfile( es.defaultProfileName() ); 00450 00451 return 00452 newFromExisting( Identity( name, 00453 es.getSetting( KEMailSettings::RealName ), 00454 es.getSetting( KEMailSettings::EmailAddress ), 00455 es.getSetting( KEMailSettings::Organization ), 00456 es.getSetting( KEMailSettings::ReplyToAddress ) ) ); 00457 } 00458 00459 Identity &IdentityManager::newFromExisting( const Identity &other, 00460 const QString &name ) 00461 { 00462 mShadowIdentities << other; 00463 Identity &result = mShadowIdentities.last(); 00464 result.setIsDefault( false ); // we don't want two default identities! 00465 result.setUoid( newUoid() ); // we don't want two identies w/ same UOID 00466 if ( !name.isNull() ) { 00467 result.setIdentityName( name ); 00468 } 00469 return result; 00470 } 00471 00472 void IdentityManager::createDefaultIdentity() 00473 { 00474 QString fullName, emailAddress; 00475 bool done = false; 00476 00477 // Check if the application has any settings 00478 createDefaultIdentity( fullName, emailAddress ); 00479 00480 // If not, then use the kcontrol settings 00481 if ( fullName.isEmpty() && emailAddress.isEmpty() ) { 00482 KEMailSettings emailSettings; 00483 fullName = emailSettings.getSetting( KEMailSettings::RealName ); 00484 emailAddress = emailSettings.getSetting( KEMailSettings::EmailAddress ); 00485 00486 if ( !fullName.isEmpty() && !emailAddress.isEmpty() ) { 00487 newFromControlCenter( i18nc( "use default address from control center", 00488 "Default" ) ); 00489 done = true; 00490 } else { 00491 // If KEmailSettings doesn't have name and address, generate something from KUser 00492 KUser user; 00493 if ( fullName.isEmpty() ) { 00494 fullName = user.property( KUser::FullName ).toString(); 00495 } 00496 if ( emailAddress.isEmpty() ) { 00497 emailAddress = user.loginName(); 00498 if ( !emailAddress.isEmpty() ) { 00499 KConfigGroup general( mConfig, "General" ); 00500 QString defaultdomain = general.readEntry( "Default domain" ); 00501 if ( !defaultdomain.isEmpty() ) { 00502 emailAddress += '@' + defaultdomain; 00503 } else { 00504 emailAddress.clear(); 00505 } 00506 } 00507 } 00508 } 00509 } 00510 00511 if ( !done ) { 00512 // Default identity name 00513 QString name( i18nc( "Default name for new email accounts/identities.", "Unnamed" ) ); 00514 00515 if ( !emailAddress.isEmpty() ) { 00516 // If we have an email address, create a default identity name from it 00517 QString idName = emailAddress; 00518 int pos = idName.indexOf( '@' ); 00519 if ( pos != -1 ) { 00520 name = idName.mid( pos + 1, -1 ); 00521 } 00522 00523 // Make the name a bit more human friendly 00524 name.replace( '.', ' ' ); 00525 pos = name.indexOf( ' ' ); 00526 if ( pos != 0 ) { 00527 name[pos + 1] = name[pos + 1].toUpper(); 00528 } 00529 name[0] = name[0].toUpper(); 00530 } else if ( !fullName.isEmpty() ) { 00531 // If we have a full name, create a default identity name from it 00532 name = fullName; 00533 } 00534 mShadowIdentities << Identity( name, fullName, emailAddress ); 00535 } 00536 00537 mShadowIdentities.last().setIsDefault( true ); 00538 mShadowIdentities.last().setUoid( newUoid() ); 00539 if ( mReadOnly ) { // commit won't do it in readonly mode 00540 mIdentities = mShadowIdentities; 00541 } 00542 } 00543 00544 int IdentityManager::newUoid() 00545 { 00546 int uoid; 00547 00548 // determine the UOIDs of all saved identities 00549 QList<uint> usedUOIDs; 00550 for ( QList<Identity>::ConstIterator it = mIdentities.constBegin(); 00551 it != mIdentities.constEnd(); ++it ) 00552 usedUOIDs << (*it).uoid(); 00553 00554 if ( hasPendingChanges() ) { 00555 // add UOIDs of all shadow identities. Yes, we will add a lot of duplicate 00556 // UOIDs, but avoiding duplicate UOIDs isn't worth the effort. 00557 for ( QList<Identity>::ConstIterator it = mShadowIdentities.constBegin(); 00558 it != mShadowIdentities.constEnd(); ++it ) { 00559 usedUOIDs << (*it).uoid(); 00560 } 00561 } 00562 00563 usedUOIDs << 0; // no UOID must be 0 because this value always refers to the 00564 // default identity 00565 00566 do { 00567 uoid = KRandom::random(); 00568 } while ( usedUOIDs.indexOf( uoid ) != -1 ); 00569 00570 return uoid; 00571 } 00572 00573 QStringList KPIMIdentities::IdentityManager::allEmails() const 00574 { 00575 QStringList lst; 00576 for ( ConstIterator it = begin(); it != end(); ++it ) { 00577 lst << (*it).primaryEmailAddress(); 00578 if ( !(*it).emailAliases().isEmpty() ) 00579 lst << (*it).emailAliases(); 00580 } 00581 return lst; 00582 } 00583 00584 void KPIMIdentities::IdentityManager::slotRollback() 00585 { 00586 rollback(); 00587 } 00588 00589 void KPIMIdentities::IdentityManager::slotIdentitiesChanged( const QString &id ) 00590 { 00591 kDebug( 5325 ) <<" KPIMIdentities::IdentityManager::slotIdentitiesChanged :" << id; 00592 const QString ourIdentifier = QString::fromLatin1( "%1/%2" ).arg( QDBusConnection::sessionBus().baseService() ) 00593 .arg( property( "uniqueDBusPath" ).toString() ); 00594 if ( id != ourIdentifier ) { 00595 mConfig->reparseConfiguration(); 00596 Q_ASSERT( !hasPendingChanges() ); 00597 readConfig( mConfig ); 00598 emit changed(); 00599 } 00600 } 00601 00602 #include "identitymanager.moc"