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 QList<Identity>::ConstIterator end = mIdentities.constEnd(); 00130 for ( QList<Identity>::ConstIterator it = mIdentities.constBegin(); 00131 it != end; ++it ) { 00132 seenUOIDs << (*it).uoid(); 00133 } 00134 00135 QList<uint> changedUOIDs; 00136 // find added and changed identities: 00137 for ( QList<Identity>::ConstIterator it = mShadowIdentities.constBegin(); 00138 it != mShadowIdentities.constEnd(); ++it ) { 00139 int index = seenUOIDs.indexOf( (*it).uoid() ); 00140 if ( index != -1 ) { 00141 uint uoid = seenUOIDs.at( index ); 00142 const Identity &orig = identityForUoid( uoid ); // look up in mIdentities 00143 if ( *it != orig ) { 00144 // changed identity 00145 kDebug( 5325 ) << "emitting changed() for identity" << uoid; 00146 emit changed(*it); 00147 changedUOIDs << uoid; 00148 } 00149 seenUOIDs.removeAll( uoid ); 00150 } else { 00151 // new identity 00152 kDebug( 5325 ) << "emitting added() for identity" << (*it).uoid(); 00153 emit added(*it); 00154 } 00155 } 00156 00157 // what's left are deleted identities: 00158 for ( QList<uint>::ConstIterator it = seenUOIDs.constBegin(); 00159 it != seenUOIDs.constEnd(); ++it ) { 00160 kDebug( 5325 ) << "emitting deleted() for identity" << (*it); 00161 emit deleted(*it); 00162 } 00163 00164 mIdentities = mShadowIdentities; 00165 writeConfig(); 00166 00167 // now that mIdentities has all the new info, we can emit the added/changed 00168 // signals that ship a uoid. This is because the slots might use 00169 // identityForUoid(uoid)... 00170 QList<uint>::ConstIterator changedEnd( changedUOIDs.constEnd() ); 00171 for ( QList<uint>::ConstIterator it = changedUOIDs.constBegin(); 00172 it != changedEnd; ++it ) 00173 emit changed(*it); 00174 00175 emit changed(); // normal signal 00176 00177 // DBus signal for other IdentityManager instances 00178 const QString ourIdentifier = QString::fromLatin1( "%1/%2" ). 00179 arg( QDBusConnection::sessionBus().baseService() ). 00180 arg( property( "uniqueDBusPath" ).toString() ); 00181 emit identitiesChanged( ourIdentifier ); 00182 } 00183 00184 void IdentityManager::rollback() 00185 { 00186 mShadowIdentities = mIdentities; 00187 } 00188 00189 bool IdentityManager::hasPendingChanges() const 00190 { 00191 return mIdentities != mShadowIdentities; 00192 } 00193 00194 QStringList IdentityManager::identities() const 00195 { 00196 QStringList result; 00197 ConstIterator end = mIdentities.constEnd(); 00198 for ( ConstIterator it = mIdentities.constBegin(); 00199 it != end; ++it ) 00200 result << (*it).identityName(); 00201 return result; 00202 } 00203 00204 QStringList IdentityManager::shadowIdentities() const 00205 { 00206 QStringList result; 00207 ConstIterator end = mShadowIdentities.constEnd(); 00208 for ( ConstIterator it = mShadowIdentities.constBegin(); 00209 it != end; ++it ) 00210 result << (*it).identityName(); 00211 return result; 00212 } 00213 00214 void IdentityManager::sort() 00215 { 00216 qSort( mShadowIdentities ); 00217 } 00218 00219 void IdentityManager::writeConfig() const 00220 { 00221 const QStringList identities = groupList( mConfig ); 00222 QStringList::const_iterator groupEnd = identities.constEnd(); 00223 for ( QStringList::const_iterator group = identities.constBegin(); 00224 group != groupEnd; ++group ) 00225 mConfig->deleteGroup( *group ); 00226 int i = 0; 00227 ConstIterator end = mIdentities.constEnd(); 00228 for ( ConstIterator it = mIdentities.constBegin(); 00229 it != end; ++it, ++i ) { 00230 KConfigGroup cg( mConfig, QString::fromLatin1( "Identity #%1" ).arg( i ) ); 00231 (*it).writeConfig( cg ); 00232 if ( (*it).isDefault() ) { 00233 // remember which one is default: 00234 KConfigGroup general( mConfig, "General" ); 00235 general.writeEntry( configKeyDefaultIdentity, (*it).uoid() ); 00236 00237 // Also write the default identity to emailsettings 00238 KEMailSettings es; 00239 es.setSetting( KEMailSettings::RealName, (*it).fullName() ); 00240 es.setSetting( KEMailSettings::EmailAddress, (*it).primaryEmailAddress() ); 00241 es.setSetting( KEMailSettings::Organization, (*it).organization() ); 00242 es.setSetting( KEMailSettings::ReplyToAddress, (*it).replyToAddr() ); 00243 } 00244 } 00245 mConfig->sync(); 00246 00247 } 00248 00249 void IdentityManager::readConfig( KConfig *config ) 00250 { 00251 mIdentities.clear(); 00252 00253 const QStringList identities = groupList( config ); 00254 if ( identities.isEmpty() ) { 00255 return; // nothing to be done... 00256 } 00257 00258 KConfigGroup general( config, "General" ); 00259 uint defaultIdentity = general.readEntry( configKeyDefaultIdentity, 0 ); 00260 bool haveDefault = false; 00261 QStringList::const_iterator groupEnd = identities.constEnd(); 00262 for ( QStringList::const_iterator group = identities.constBegin(); 00263 group != groupEnd; ++group ) { 00264 KConfigGroup configGroup( config, *group ); 00265 mIdentities << Identity(); 00266 mIdentities.last().readConfig( configGroup ); 00267 if ( !haveDefault && mIdentities.last().uoid() == defaultIdentity ) { 00268 haveDefault = true; 00269 mIdentities.last().setIsDefault( true ); 00270 } 00271 } 00272 00273 if ( !haveDefault ) { 00274 kWarning( 5325 ) << "IdentityManager: There was no default identity." 00275 << "Marking first one as default."; 00276 mIdentities.first().setIsDefault( true ); 00277 } 00278 qSort( mIdentities ); 00279 00280 mShadowIdentities = mIdentities; 00281 } 00282 00283 QStringList IdentityManager::groupList( KConfig *config ) const 00284 { 00285 return config->groupList().filter( QRegExp( "^Identity #\\d+$" ) ); 00286 } 00287 00288 IdentityManager::ConstIterator IdentityManager::begin() const 00289 { 00290 return mIdentities.begin(); 00291 } 00292 00293 IdentityManager::ConstIterator IdentityManager::end() const 00294 { 00295 return mIdentities.end(); 00296 } 00297 00298 IdentityManager::Iterator IdentityManager::modifyBegin() 00299 { 00300 return mShadowIdentities.begin(); 00301 } 00302 00303 IdentityManager::Iterator IdentityManager::modifyEnd() 00304 { 00305 return mShadowIdentities.end(); 00306 } 00307 00308 const Identity &IdentityManager::identityForUoid( uint uoid ) const 00309 { 00310 for ( ConstIterator it = begin(); it != end(); ++it ) { 00311 if ( (*it).uoid() == uoid ) { 00312 return (*it); 00313 } 00314 } 00315 return Identity::null(); 00316 } 00317 00318 const Identity &IdentityManager::identityForUoidOrDefault( uint uoid ) const 00319 { 00320 const Identity &ident = identityForUoid( uoid ); 00321 if ( ident.isNull() ) { 00322 return defaultIdentity(); 00323 } else { 00324 return ident; 00325 } 00326 } 00327 00328 const Identity &IdentityManager::identityForAddress( 00329 const QString &addresses ) const 00330 { 00331 const QStringList addressList = KPIMUtils::splitAddressList( addresses ); 00332 foreach ( const QString &fullAddress, addressList ) { 00333 const QString addrSpec = KPIMUtils::extractEmailAddress( fullAddress ).toLower(); 00334 for ( ConstIterator it = begin(); it != end(); ++it ) { 00335 const Identity &identity = *it; 00336 if ( identity.matchesEmailAddress( addrSpec ) ) { 00337 return identity; 00338 } 00339 } 00340 } 00341 return Identity::null(); 00342 } 00343 00344 bool IdentityManager::thatIsMe( const QString &addressList ) const 00345 { 00346 return !identityForAddress( addressList ).isNull(); 00347 } 00348 00349 Identity &IdentityManager::modifyIdentityForName( const QString &name ) 00350 { 00351 for ( Iterator it = modifyBegin(); it != modifyEnd(); ++it ) { 00352 if ( (*it).identityName() == name ) { 00353 return (*it); 00354 } 00355 } 00356 00357 kWarning( 5325 ) << "IdentityManager::modifyIdentityForName() used as" 00358 << "newFromScratch() replacement!" 00359 << endl << " name == \"" << name << "\""; 00360 return newFromScratch( name ); 00361 } 00362 00363 Identity &IdentityManager::modifyIdentityForUoid( uint uoid ) 00364 { 00365 for ( Iterator it = modifyBegin(); it != modifyEnd(); ++it ) { 00366 if ( (*it).uoid() == uoid ) { 00367 return (*it); 00368 } 00369 } 00370 00371 kWarning( 5325 ) << "IdentityManager::identityForUoid() used as" 00372 << "newFromScratch() replacement!" 00373 << endl << " uoid == \"" << uoid << "\""; 00374 return newFromScratch( i18n( "Unnamed" ) ); 00375 } 00376 00377 const Identity &IdentityManager::defaultIdentity() const 00378 { 00379 for ( ConstIterator it = begin(); it != end(); ++it ) { 00380 if ( (*it).isDefault() ) { 00381 return (*it); 00382 } 00383 } 00384 00385 if ( mIdentities.isEmpty() ) { 00386 kFatal( 5325 ) << "IdentityManager: No default identity found!"; 00387 } else { 00388 kWarning( 5325 ) << "IdentityManager: No default identity found!"; 00389 } 00390 return *begin(); 00391 } 00392 00393 bool IdentityManager::setAsDefault( uint uoid ) 00394 { 00395 // First, check if the identity actually exists: 00396 bool found = false; 00397 for ( ConstIterator it = mShadowIdentities.constBegin(); 00398 it != mShadowIdentities.constEnd(); ++it ) 00399 if ( (*it).uoid() == uoid ) { 00400 found = true; 00401 break; 00402 } 00403 00404 if ( !found ) { 00405 return false; 00406 } 00407 00408 // Then, change the default as requested: 00409 for ( Iterator it = modifyBegin(); it != modifyEnd(); ++it ) { 00410 (*it).setIsDefault( (*it).uoid() == uoid ); 00411 } 00412 00413 // and re-sort: 00414 sort(); 00415 return true; 00416 } 00417 00418 bool IdentityManager::removeIdentity( const QString &name ) 00419 { 00420 if ( mShadowIdentities.size() <= 1 ) { 00421 return false; 00422 } 00423 00424 for ( Iterator it = modifyBegin(); it != modifyEnd(); ++it ) { 00425 if ( (*it).identityName() == name ) { 00426 bool removedWasDefault = (*it).isDefault(); 00427 mShadowIdentities.erase( it ); 00428 if ( removedWasDefault && !mShadowIdentities.isEmpty() ) { 00429 mShadowIdentities.first().setIsDefault( true ); 00430 } 00431 return true; 00432 } 00433 } 00434 return false; 00435 } 00436 00437 bool IdentityManager::removeIdentityForced( const QString &name ) 00438 { 00439 for ( Iterator it = modifyBegin(); it != modifyEnd(); ++it ) { 00440 if ( (*it).identityName() == name ) { 00441 bool removedWasDefault = (*it).isDefault(); 00442 mShadowIdentities.erase( it ); 00443 if ( removedWasDefault && !mShadowIdentities.isEmpty() ) { 00444 mShadowIdentities.first().setIsDefault( true ); 00445 } 00446 return true; 00447 } 00448 } 00449 return false; 00450 } 00451 00452 Identity &IdentityManager::newFromScratch( const QString &name ) 00453 { 00454 return newFromExisting( Identity( name ) ); 00455 } 00456 00457 Identity &IdentityManager::newFromControlCenter( const QString &name ) 00458 { 00459 KEMailSettings es; 00460 es.setProfile( es.defaultProfileName() ); 00461 00462 return 00463 newFromExisting( Identity( name, 00464 es.getSetting( KEMailSettings::RealName ), 00465 es.getSetting( KEMailSettings::EmailAddress ), 00466 es.getSetting( KEMailSettings::Organization ), 00467 es.getSetting( KEMailSettings::ReplyToAddress ) ) ); 00468 } 00469 00470 Identity &IdentityManager::newFromExisting( const Identity &other, const QString &name ) 00471 { 00472 mShadowIdentities << other; 00473 Identity &result = mShadowIdentities.last(); 00474 result.setIsDefault( false ); // we don't want two default identities! 00475 result.setUoid( newUoid() ); // we don't want two identies w/ same UOID 00476 if ( !name.isNull() ) { 00477 result.setIdentityName( name ); 00478 } 00479 return result; 00480 } 00481 00482 void IdentityManager::createDefaultIdentity() 00483 { 00484 QString fullName, emailAddress; 00485 bool done = false; 00486 00487 // Check if the application has any settings 00488 createDefaultIdentity( fullName, emailAddress ); 00489 00490 // If not, then use the kcontrol settings 00491 if ( fullName.isEmpty() && emailAddress.isEmpty() ) { 00492 KEMailSettings emailSettings; 00493 fullName = emailSettings.getSetting( KEMailSettings::RealName ); 00494 emailAddress = emailSettings.getSetting( KEMailSettings::EmailAddress ); 00495 00496 if ( !fullName.isEmpty() && !emailAddress.isEmpty() ) { 00497 newFromControlCenter( i18nc( "use default address from control center", 00498 "Default" ) ); 00499 done = true; 00500 } else { 00501 // If KEmailSettings doesn't have name and address, generate something from KUser 00502 KUser user; 00503 if ( fullName.isEmpty() ) { 00504 fullName = user.property( KUser::FullName ).toString(); 00505 } 00506 if ( emailAddress.isEmpty() ) { 00507 emailAddress = user.loginName(); 00508 if ( !emailAddress.isEmpty() ) { 00509 KConfigGroup general( mConfig, "General" ); 00510 QString defaultdomain = general.readEntry( "Default domain" ); 00511 if ( !defaultdomain.isEmpty() ) { 00512 emailAddress += '@' + defaultdomain; 00513 } else { 00514 emailAddress.clear(); 00515 } 00516 } 00517 } 00518 } 00519 } 00520 00521 if ( !done ) { 00522 // Default identity name 00523 QString name( i18nc( "Default name for new email accounts/identities.", "Unnamed" ) ); 00524 00525 if ( !emailAddress.isEmpty() ) { 00526 // If we have an email address, create a default identity name from it 00527 QString idName = emailAddress; 00528 int pos = idName.indexOf( '@' ); 00529 if ( pos != -1 ) { 00530 name = idName.mid( pos + 1, -1 ); 00531 } 00532 00533 // Make the name a bit more human friendly 00534 name.replace( '.', ' ' ); 00535 pos = name.indexOf( ' ' ); 00536 if ( pos != 0 ) { 00537 name[pos + 1] = name[pos + 1].toUpper(); 00538 } 00539 name[0] = name[0].toUpper(); 00540 } else if ( !fullName.isEmpty() ) { 00541 // If we have a full name, create a default identity name from it 00542 name = fullName; 00543 } 00544 mShadowIdentities << Identity( name, fullName, emailAddress ); 00545 } 00546 00547 mShadowIdentities.last().setIsDefault( true ); 00548 mShadowIdentities.last().setUoid( newUoid() ); 00549 if ( mReadOnly ) { // commit won't do it in readonly mode 00550 mIdentities = mShadowIdentities; 00551 } 00552 } 00553 00554 int IdentityManager::newUoid() 00555 { 00556 int uoid; 00557 00558 // determine the UOIDs of all saved identities 00559 QList<uint> usedUOIDs; 00560 for ( QList<Identity>::ConstIterator it = mIdentities.constBegin(); 00561 it != mIdentities.constEnd(); ++it ) 00562 usedUOIDs << (*it).uoid(); 00563 00564 if ( hasPendingChanges() ) { 00565 // add UOIDs of all shadow identities. Yes, we will add a lot of duplicate 00566 // UOIDs, but avoiding duplicate UOIDs isn't worth the effort. 00567 for ( QList<Identity>::ConstIterator it = mShadowIdentities.constBegin(); 00568 it != mShadowIdentities.constEnd(); ++it ) { 00569 usedUOIDs << (*it).uoid(); 00570 } 00571 } 00572 00573 usedUOIDs << 0; // no UOID must be 0 because this value always refers to the 00574 // default identity 00575 00576 do { 00577 uoid = KRandom::random(); 00578 } while ( usedUOIDs.indexOf( uoid ) != -1 ); 00579 00580 return uoid; 00581 } 00582 00583 QStringList KPIMIdentities::IdentityManager::allEmails() const 00584 { 00585 QStringList lst; 00586 for ( ConstIterator it = begin(); it != end(); ++it ) { 00587 lst << (*it).primaryEmailAddress(); 00588 if ( !(*it).emailAliases().isEmpty() ) { 00589 lst << (*it).emailAliases(); 00590 } 00591 } 00592 return lst; 00593 } 00594 00595 void KPIMIdentities::IdentityManager::slotRollback() 00596 { 00597 rollback(); 00598 } 00599 00600 void KPIMIdentities::IdentityManager::slotIdentitiesChanged( const QString &id ) 00601 { 00602 kDebug( 5325 ) <<" KPIMIdentities::IdentityManager::slotIdentitiesChanged :" << id; 00603 const QString ourIdentifier = QString::fromLatin1( "%1/%2" ). 00604 arg( QDBusConnection::sessionBus().baseService() ). 00605 arg( property( "uniqueDBusPath" ).toString() ); 00606 if ( id != ourIdentifier ) { 00607 mConfig->reparseConfiguration(); 00608 Q_ASSERT( !hasPendingChanges() ); 00609 readConfig( mConfig ); 00610 emit changed(); 00611 } 00612 } 00613 00614 #include "identitymanager.moc"