libkpimidentities

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"