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 Qt library by Trolltech AS, Norway (or with modified versions
00023     of Qt that use the same license as Qt), 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     Qt.  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 <qregexp.h>
00053 
00054 #include <assert.h>
00055 
00056 using namespace KPIM;
00057 
00058 static QCString newDCOPObjectName()
00059 {
00060     static int s_count = 0;
00061     QCString name( "KPIM::IdentityManager" );
00062     if ( s_count++ ) {
00063       name += '-';
00064       name += QCString().setNum( s_count );
00065     }
00066     return name;
00067 }
00068 
00069 IdentityManager::IdentityManager( bool readonly, QObject * 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(QCString,QCString)",
00095                            "slotIdentitiesChanged(QCString,QCString)", 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   QValueList<uint> seenUOIDs;
00112   for ( QValueList<Identity>::ConstIterator it = mIdentities.begin() ;
00113     it != mIdentities.end() ; ++it )
00114     seenUOIDs << (*it).uoid();
00115 
00116   QValueList<uint> changedUOIDs;
00117   // find added and changed identities:
00118   for ( QValueList<Identity>::ConstIterator it = mShadowIdentities.begin() ;
00119     it != mShadowIdentities.end() ; ++it ) {
00120     QValueList<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 ( QValueList<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 ( QValueList<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   QByteArray data; QDataStream 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(QCString,QCString)", 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 QStringList IdentityManager::identities() const
00175 {
00176   QStringList result;
00177   for ( ConstIterator it = mIdentities.begin() ;
00178     it != mIdentities.end() ; ++it )
00179     result << (*it).identityName();
00180   return result;
00181 }
00182 
00183 QStringList IdentityManager::shadowIdentities() const
00184 {
00185   QStringList 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   QStringList identities = groupList(mConfig);
00198   for ( QStringList::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, QString::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).emailAddr() );
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   QStringList 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 ( QStringList::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 QStringList IdentityManager::groupList(KConfigBase* config) const {
00253   return config->groupList().grep( QRegExp("^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 QString & 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 QString & 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 QString & addresses ) const
00306 {
00307   QStringList addressList = KPIM::splitEmailAddrList( addresses );
00308   for ( ConstIterator it = begin() ; it != end() ; ++it ) {
00309     for( QStringList::ConstIterator addrIt = addressList.begin();
00310          addrIt != addressList.end(); ++addrIt ) {
00311       // I use QString::utf8() instead of QString::latin1() because I want
00312       // a QCString and not a char*. It doesn't matter because emailAddr()
00313       // returns a 7-bit string.
00314       if( (*it).emailAddr().lower() ==
00315           KPIM::getEmailAddress( *addrIt ).lower() ) {
00316         return (*it);
00317       }
00318     }
00319   }
00320   return Identity::null();
00321 }
00322 
00323 bool IdentityManager::thatIsMe( const QString & addressList ) const {
00324   return !identityForAddress( addressList ).isNull();
00325 }
00326 
00327 Identity & IdentityManager::modifyIdentityForName( const QString & name )
00328 {
00329   for ( Iterator it = modifyBegin() ; it != modifyEnd() ; ++it )
00330     if ( (*it).identityName() == name ) return (*it);
00331   kdWarning( 5006 ) << "IdentityManager::identityForName() used as newFromScratch() replacement!"
00332             << "\n  name == \"" << name << "\"" << endl;
00333   return newFromScratch( name );
00334 }
00335 
00336 Identity & IdentityManager::modifyIdentityForUoid( uint uoid )
00337 {
00338   for ( Iterator it = modifyBegin() ; it != modifyEnd() ; ++it )
00339     if ( (*it).uoid() == uoid ) return (*it);
00340   kdWarning( 5006 ) << "IdentityManager::identityForUoid() used as newFromScratch() replacement!"
00341             << "\n  uoid == \"" << uoid << "\"" << endl;
00342   return newFromScratch( i18n("Unnamed") );
00343 }
00344 
00345 const Identity & IdentityManager::defaultIdentity() const {
00346   for ( ConstIterator it = begin() ; it != end() ; ++it )
00347     if ( (*it).isDefault() ) return (*it);
00348   (mIdentities.isEmpty() ? kdFatal( 5006 ) : kdWarning( 5006 ) )
00349     << "IdentityManager: No default identity found!" << endl;
00350   return *begin();
00351 }
00352 
00353 bool IdentityManager::setAsDefault( const QString & name ) {
00354   // First, check if the identity actually exists:
00355   QStringList names = shadowIdentities();
00356   if ( names.find( name ) == names.end() ) return false;
00357   // Then, change the default as requested:
00358   for ( Iterator it = modifyBegin() ; it != modifyEnd() ; ++it )
00359     (*it).setIsDefault( (*it).identityName() == name );
00360   // and re-sort:
00361   sort();
00362   return true;
00363 }
00364 
00365 bool IdentityManager::setAsDefault( uint uoid ) {
00366   // First, check if the identity actually exists:
00367   bool found = false;
00368   for ( ConstIterator it = mShadowIdentities.begin() ;
00369     it != mShadowIdentities.end() ; ++it )
00370     if ( (*it).uoid() == uoid ) {
00371       found = true;
00372       break;
00373     }
00374   if ( !found ) return false;
00375 
00376   // Then, change the default as requested:
00377   for ( Iterator it = modifyBegin() ; it != modifyEnd() ; ++it )
00378     (*it).setIsDefault( (*it).uoid() == uoid );
00379   // and re-sort:
00380   sort();
00381   return true;
00382 }
00383 
00384 bool IdentityManager::removeIdentity( const QString & name ) {
00385   for ( Iterator it = modifyBegin() ; it != modifyEnd() ; ++it )
00386     if ( (*it).identityName() == name ) {
00387       bool removedWasDefault = (*it).isDefault();
00388       mShadowIdentities.remove( it );
00389       if ( removedWasDefault )
00390     mShadowIdentities.first().setIsDefault( true );
00391       return true;
00392     }
00393   return false;
00394 }
00395 
00396 Identity & IdentityManager::newFromScratch( const QString & name ) {
00397   return newFromExisting( Identity( name ) );
00398 }
00399 
00400 Identity & IdentityManager::newFromControlCenter( const QString & name ) {
00401   KEMailSettings es;
00402   es.setProfile( es.defaultProfileName() );
00403 
00404   return newFromExisting( Identity( name,
00405                    es.getSetting( KEMailSettings::RealName ),
00406                    es.getSetting( KEMailSettings::EmailAddress ),
00407                    es.getSetting( KEMailSettings::Organization ),
00408                    es.getSetting( KEMailSettings::ReplyToAddress )
00409                    ) );
00410 }
00411 
00412 Identity & IdentityManager::newFromExisting( const Identity & other,
00413                            const QString & name ) {
00414   mShadowIdentities << other;
00415   Identity & result = mShadowIdentities.last();
00416   result.setIsDefault( false ); // we don't want two default identities!
00417   result.setUoid( newUoid() ); // we don't want two identies w/ same UOID
00418   if ( !name.isNull() )
00419     result.setIdentityName( name );
00420   return result;
00421 }
00422 
00423 void IdentityManager::createDefaultIdentity() {
00424   QString fullName, emailAddress;
00425   bool done = false;
00426 
00427   // Check if the application has any settings
00428   createDefaultIdentity( fullName, emailAddress );
00429 
00430   // If not, then use the kcontrol settings
00431   if ( fullName.isEmpty() && emailAddress.isEmpty() ) {
00432     KEMailSettings emailSettings;
00433     fullName = emailSettings.getSetting( KEMailSettings::RealName );
00434     emailAddress = emailSettings.getSetting( KEMailSettings::EmailAddress );
00435 
00436     if ( !fullName.isEmpty() && !emailAddress.isEmpty() ) {
00437       newFromControlCenter( i18n("Default") );
00438       done = true;
00439     } else {
00440       // If KEmailSettings doesn't have name and address, generate something from KUser
00441       KUser user;
00442       if ( fullName.isEmpty() )
00443         fullName = user.fullName();
00444       if ( emailAddress.isEmpty() ) {
00445         emailAddress = user.loginName();
00446         if ( !emailAddress.isEmpty() ) {
00447           KConfigGroup general( mConfig, "General" );
00448           QString defaultdomain = general.readEntry( "Default domain" );
00449           if( !defaultdomain.isEmpty() ) {
00450             emailAddress += '@' + defaultdomain;
00451           }
00452           else {
00453             emailAddress = QString::null;
00454           }
00455         }
00456       }
00457     }
00458   }
00459 
00460   if ( !done )
00461     mShadowIdentities << Identity( i18n("Default"), fullName, emailAddress );
00462 
00463   mShadowIdentities.last().setIsDefault( true );
00464   mShadowIdentities.last().setUoid( newUoid() );
00465   if ( mReadOnly ) // commit won't do it in readonly mode
00466     mIdentities = mShadowIdentities;
00467 }
00468 
00469 int IdentityManager::newUoid()
00470 {
00471   int uoid;
00472 
00473   // determine the UOIDs of all saved identities
00474   QValueList<uint> usedUOIDs;
00475   for ( QValueList<Identity>::ConstIterator it = mIdentities.begin() ;
00476     it != mIdentities.end() ; ++it )
00477     usedUOIDs << (*it).uoid();
00478 
00479   if ( hasPendingChanges() ) {
00480     // add UOIDs of all shadow identities. Yes, we will add a lot of duplicate
00481     // UOIDs, but avoiding duplicate UOIDs isn't worth the effort.
00482     for ( QValueList<Identity>::ConstIterator it = mShadowIdentities.begin() ;
00483           it != mShadowIdentities.end() ; ++it ) {
00484       usedUOIDs << (*it).uoid();
00485     }
00486   }
00487 
00488   usedUOIDs << 0; // no UOID must be 0 because this value always refers to the
00489                   // default identity
00490 
00491   do {
00492     uoid = kapp->random();
00493   } while ( usedUOIDs.find( uoid ) != usedUOIDs.end() );
00494 
00495   return uoid;
00496 }
00497 
00498 QStringList KPIM::IdentityManager::allEmails() const
00499 {
00500   QStringList lst;
00501   for ( ConstIterator it = begin() ; it != end() ; ++it ) {
00502     lst << (*it).emailAddr();
00503   }
00504   return lst;
00505 }
00506 
00507 void KPIM::IdentityManager::slotIdentitiesChanged( QCString appId, QCString objId )
00508 {
00509   // From standalone kmail to standalone korganizer, the appId will differ
00510   // From kontact the appId will match, so we need to test the objId
00511   if ( kapp->dcopClient()->appId() != appId || DCOPObject::objId() != objId ) {
00512     mConfig->reparseConfiguration();
00513     Q_ASSERT( !hasPendingChanges() );
00514     readConfig( mConfig );
00515   }
00516 }
00517 
00518 #include "identitymanager.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys