You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
517 lines
17 KiB
517 lines
17 KiB
/* -*- mode: C++; c-file-style: "gnu" -*-
|
|
identitymanager.cpp
|
|
|
|
This file is part of KMail, the KDE mail client.
|
|
Copyright (c) 2002 Marc Mutz <mutz@kde.org>
|
|
|
|
KMail is free software; you can redistribute it and/or modify it
|
|
under the terms of the GNU General Public License, version 2, as
|
|
published by the Free Software Foundation.
|
|
|
|
KMail is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
In addition, as a special exception, the copyright holders give
|
|
permission to link the code of this program with any edition of
|
|
the TQt library by Trolltech AS, Norway (or with modified versions
|
|
of TQt that use the same license as TQt), and distribute linked
|
|
combinations including the two. You must obey the GNU General
|
|
Public License in all respects for all of the code used other than
|
|
TQt. If you modify this file, you may extend this exception to
|
|
your version of the file, but you are not obligated to do so. If
|
|
you do not wish to do so, delete this exception statement from
|
|
your version.
|
|
*/
|
|
|
|
// config keys:
|
|
static const char configKeyDefaultIdentity[] = "Default Identity";
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include "identitymanager.h"
|
|
|
|
#include "identity.h" // for IdentityList::{export,import}Data
|
|
#include <libemailfunctions/email.h> // for static helper functions
|
|
|
|
#include <kemailsettings.h> // for IdentityEntry::fromControlCenter()
|
|
#include <kapplication.h>
|
|
#include <klocale.h>
|
|
#include <kdebug.h>
|
|
#include <kconfig.h>
|
|
#include <kuser.h>
|
|
#include <dcopclient.h>
|
|
|
|
#include <tqregexp.h>
|
|
|
|
#include <assert.h>
|
|
|
|
using namespace KPIM;
|
|
|
|
static TQCString newDCOPObjectName()
|
|
{
|
|
static int s_count = 0;
|
|
TQCString name( "KPIM::IdentityManager" );
|
|
if ( s_count++ ) {
|
|
name += '-';
|
|
name += TQCString().setNum( s_count );
|
|
}
|
|
return name;
|
|
}
|
|
|
|
IdentityManager::IdentityManager( bool readonly, TQObject * parent, const char * name )
|
|
: ConfigManager( parent, name ), DCOPObject( newDCOPObjectName() )
|
|
{
|
|
mReadOnly = readonly;
|
|
mConfig = new KConfig( "emailidentities", readonly );
|
|
readConfig(mConfig);
|
|
if ( mIdentities.isEmpty() ) {
|
|
kdDebug(5006) << "emailidentities is empty -> convert from kmailrc" << endl;
|
|
// No emailidentities file, or an empty one due to broken conversion (kconf_update bug in kdelibs <= 3.2.2)
|
|
// => convert it, i.e. read settings from kmailrc
|
|
KConfig kmailConf( "kmailrc", true );
|
|
readConfig( &kmailConf );
|
|
}
|
|
// we need at least a default identity:
|
|
if ( mIdentities.isEmpty() ) {
|
|
kdDebug( 5006 ) << "IdentityManager: No identity found. Creating default." << endl;
|
|
createDefaultIdentity();
|
|
commit();
|
|
}
|
|
// Migration: people without settings in kemailsettings should get some
|
|
if ( KEMailSettings().getSetting( KEMailSettings::EmailAddress ).isEmpty() ) {
|
|
writeConfig();
|
|
}
|
|
|
|
// The emitter is always called KPIM::IdentityManager even if we are not
|
|
if ( !connectDCOPSignal( 0, "KPIM::IdentityManager", "identitiesChanged(TQCString,TQCString)",
|
|
"slotIdentitiesChanged(TQCString,TQCString)", false ) )
|
|
kdError(5650) << "IdentityManager: connection to identitiesChanged failed" << endl;
|
|
}
|
|
|
|
IdentityManager::~IdentityManager()
|
|
{
|
|
kdWarning( hasPendingChanges(), 5006 )
|
|
<< "IdentityManager: There were uncommitted changes!" << endl;
|
|
delete mConfig;
|
|
}
|
|
|
|
void IdentityManager::commit()
|
|
{
|
|
// early out:
|
|
if ( !hasPendingChanges() || mReadOnly ) return;
|
|
|
|
TQValueList<uint> seenUOIDs;
|
|
for ( TQValueList<Identity>::ConstIterator it = mIdentities.begin() ;
|
|
it != mIdentities.end() ; ++it )
|
|
seenUOIDs << (*it).uoid();
|
|
|
|
TQValueList<uint> changedUOIDs;
|
|
// find added and changed identities:
|
|
for ( TQValueList<Identity>::ConstIterator it = mShadowIdentities.begin() ;
|
|
it != mShadowIdentities.end() ; ++it ) {
|
|
TQValueList<uint>::Iterator uoid = seenUOIDs.find( (*it).uoid() );
|
|
if ( uoid != seenUOIDs.end() ) {
|
|
const Identity & orig = identityForUoid( *uoid ); // look it up in mIdentities
|
|
if ( *it != orig ) {
|
|
// changed identity
|
|
kdDebug( 5006 ) << "emitting changed() for identity " << *uoid << endl;
|
|
emit changed( *it );
|
|
changedUOIDs << *uoid;
|
|
}
|
|
seenUOIDs.remove( uoid );
|
|
} else {
|
|
// new identity
|
|
kdDebug( 5006 ) << "emitting added() for identity " << (*it).uoid() << endl;
|
|
emit added( *it );
|
|
}
|
|
}
|
|
|
|
// what's left are deleted identities:
|
|
for ( TQValueList<uint>::ConstIterator it = seenUOIDs.begin() ;
|
|
it != seenUOIDs.end() ; ++it ) {
|
|
kdDebug( 5006 ) << "emitting deleted() for identity " << (*it) << endl;
|
|
emit deleted( *it );
|
|
}
|
|
|
|
mIdentities = mShadowIdentities;
|
|
writeConfig();
|
|
|
|
// now that mIdentities has all the new info, we can emit the added/changed
|
|
// signals that ship a uoid. This is because the slots might use identityForUoid(uoid)...
|
|
for ( TQValueList<uint>::ConstIterator it = changedUOIDs.begin() ;
|
|
it != changedUOIDs.end() ; ++it )
|
|
emit changed( *it );
|
|
|
|
emit ConfigManager::changed(); // normal signal
|
|
|
|
// DCOP signal for other IdentityManager instances
|
|
// The emitter is always set to KPIM::IdentityManager, so that the connect works
|
|
// This is why we can't use k_dcop_signals here, but need to use emitDCOPSignal
|
|
TQByteArray data; TQDataStream arg( data, IO_WriteOnly );
|
|
arg << kapp->dcopClient()->appId();
|
|
arg << DCOPObject::objId(); // the real objId, for checking in slotIdentitiesChanged
|
|
kapp->dcopClient()->emitDCOPSignal( "KPIM::IdentityManager", "identitiesChanged(TQCString,TQCString)", data );
|
|
}
|
|
|
|
void IdentityManager::rollback()
|
|
{
|
|
mShadowIdentities = mIdentities;
|
|
}
|
|
|
|
bool IdentityManager::hasPendingChanges() const
|
|
{
|
|
return mIdentities != mShadowIdentities;
|
|
}
|
|
|
|
TQStringList IdentityManager::identities() const
|
|
{
|
|
TQStringList result;
|
|
for ( ConstIterator it = mIdentities.begin() ;
|
|
it != mIdentities.end() ; ++it )
|
|
result << (*it).identityName();
|
|
return result;
|
|
}
|
|
|
|
TQStringList IdentityManager::shadowIdentities() const
|
|
{
|
|
TQStringList result;
|
|
for ( ConstIterator it = mShadowIdentities.begin() ;
|
|
it != mShadowIdentities.end() ; ++it )
|
|
result << (*it).identityName();
|
|
return result;
|
|
}
|
|
|
|
void IdentityManager::sort() {
|
|
qHeapSort( mShadowIdentities );
|
|
}
|
|
|
|
void IdentityManager::writeConfig() const {
|
|
TQStringList identities = groupList(mConfig);
|
|
for ( TQStringList::Iterator group = identities.begin() ;
|
|
group != identities.end() ; ++group )
|
|
mConfig->deleteGroup( *group );
|
|
int i = 0;
|
|
for ( ConstIterator it = mIdentities.begin() ;
|
|
it != mIdentities.end() ; ++it, ++i ) {
|
|
KConfigGroup cg( mConfig, TQString::tqfromLatin1("Identity #%1").tqarg(i) );
|
|
(*it).writeConfig( &cg );
|
|
if ( (*it).isDefault() ) {
|
|
// remember which one is default:
|
|
KConfigGroup general( mConfig, "General" );
|
|
general.writeEntry( configKeyDefaultIdentity, (*it).uoid() );
|
|
|
|
// Also write the default identity to emailsettings
|
|
KEMailSettings es;
|
|
es.setSetting( KEMailSettings::RealName, (*it).fullName() );
|
|
es.setSetting( KEMailSettings::EmailAddress, (*it).primaryEmailAddress() );
|
|
es.setSetting( KEMailSettings::Organization, (*it).organization() );
|
|
es.setSetting( KEMailSettings::ReplyToAddress, (*it).replyToAddr() );
|
|
}
|
|
}
|
|
mConfig->sync();
|
|
|
|
}
|
|
|
|
void IdentityManager::readConfig(KConfigBase* config) {
|
|
mIdentities.clear();
|
|
|
|
TQStringList identities = groupList(config);
|
|
if ( identities.isEmpty() ) return; // nothing to be done...
|
|
|
|
KConfigGroup general( config, "General" );
|
|
uint defaultIdentity = general.readUnsignedNumEntry( configKeyDefaultIdentity );
|
|
bool haveDefault = false;
|
|
|
|
for ( TQStringList::Iterator group = identities.begin() ;
|
|
group != identities.end() ; ++group ) {
|
|
KConfigGroup configGroup( config, *group );
|
|
mIdentities << Identity();
|
|
mIdentities.last().readConfig( &configGroup );
|
|
if ( !haveDefault && mIdentities.last().uoid() == defaultIdentity ) {
|
|
haveDefault = true;
|
|
mIdentities.last().setIsDefault( true );
|
|
}
|
|
}
|
|
if ( !haveDefault ) {
|
|
kdWarning( 5006 ) << "IdentityManager: There was no default identity. Marking first one as default." << endl;
|
|
mIdentities.first().setIsDefault( true );
|
|
}
|
|
qHeapSort( mIdentities );
|
|
|
|
mShadowIdentities = mIdentities;
|
|
}
|
|
|
|
TQStringList IdentityManager::groupList(KConfigBase* config) const {
|
|
return config->groupList().grep( TQRegExp("^Identity #\\d+$") );
|
|
}
|
|
|
|
IdentityManager::ConstIterator IdentityManager::begin() const {
|
|
return mIdentities.begin();
|
|
}
|
|
|
|
IdentityManager::ConstIterator IdentityManager::end() const {
|
|
return mIdentities.end();
|
|
}
|
|
|
|
IdentityManager::Iterator IdentityManager::modifyBegin() {
|
|
return mShadowIdentities.begin();
|
|
}
|
|
|
|
IdentityManager::Iterator IdentityManager::modifyEnd() {
|
|
return mShadowIdentities.end();
|
|
}
|
|
|
|
const Identity & IdentityManager::identityForName( const TQString & name ) const
|
|
{
|
|
kdWarning( 5006 )
|
|
<< "deprecated method IdentityManager::identityForName() called!" << endl;
|
|
for ( ConstIterator it = begin() ; it != end() ; ++it )
|
|
if ( (*it).identityName() == name ) return (*it);
|
|
return Identity::null();
|
|
}
|
|
|
|
const Identity & IdentityManager::identityForUoid( uint uoid ) const {
|
|
for ( ConstIterator it = begin() ; it != end() ; ++it )
|
|
if ( (*it).uoid() == uoid ) return (*it);
|
|
return Identity::null();
|
|
}
|
|
|
|
const Identity & IdentityManager::identityForNameOrDefault( const TQString & name ) const
|
|
{
|
|
const Identity & ident = identityForName( name );
|
|
if ( ident.isNull() )
|
|
return defaultIdentity();
|
|
else
|
|
return ident;
|
|
}
|
|
|
|
const Identity & IdentityManager::identityForUoidOrDefault( uint uoid ) const
|
|
{
|
|
const Identity & ident = identityForUoid( uoid );
|
|
if ( ident.isNull() )
|
|
return defaultIdentity();
|
|
else
|
|
return ident;
|
|
}
|
|
|
|
const Identity & IdentityManager::identityForAddress( const TQString & addresses ) const
|
|
{
|
|
const TQStringList addressList = KPIM::splitEmailAddrList( addresses );
|
|
for( TQStringList::ConstIterator addrIt = addressList.begin();
|
|
addrIt != addressList.end(); ++addrIt ) {
|
|
const TQString addr = KPIM::getEmailAddress( *addrIt ).lower();
|
|
for ( ConstIterator it = begin() ; it != end() ; ++it ) {
|
|
const Identity & id = *it;
|
|
if ( id.matchesEmailAddress( addr ) ) {
|
|
return id;
|
|
}
|
|
}
|
|
}
|
|
return Identity::null();
|
|
}
|
|
|
|
bool IdentityManager::thatIsMe( const TQString & addressList ) const {
|
|
return !identityForAddress( addressList ).isNull();
|
|
}
|
|
|
|
Identity & IdentityManager::modifyIdentityForName( const TQString & name )
|
|
{
|
|
for ( Iterator it = modifyBegin() ; it != modifyEnd() ; ++it )
|
|
if ( (*it).identityName() == name ) return (*it);
|
|
kdWarning( 5006 ) << "IdentityManager::identityForName() used as newFromScratch() replacement!"
|
|
<< "\n name == \"" << name << "\"" << endl;
|
|
return newFromScratch( name );
|
|
}
|
|
|
|
Identity & IdentityManager::modifyIdentityForUoid( uint uoid )
|
|
{
|
|
for ( Iterator it = modifyBegin() ; it != modifyEnd() ; ++it )
|
|
if ( (*it).uoid() == uoid ) return (*it);
|
|
kdWarning( 5006 ) << "IdentityManager::identityForUoid() used as newFromScratch() replacement!"
|
|
<< "\n uoid == \"" << uoid << "\"" << endl;
|
|
return newFromScratch( i18n("Unnamed") );
|
|
}
|
|
|
|
const Identity & IdentityManager::defaultIdentity() const {
|
|
for ( ConstIterator it = begin() ; it != end() ; ++it )
|
|
if ( (*it).isDefault() ) return (*it);
|
|
(mIdentities.isEmpty() ? kdFatal( 5006 ) : kdWarning( 5006 ) )
|
|
<< "IdentityManager: No default identity found!" << endl;
|
|
return *begin();
|
|
}
|
|
|
|
bool IdentityManager::setAsDefault( const TQString & name ) {
|
|
// First, check if the identity actually exists:
|
|
TQStringList names = shadowIdentities();
|
|
if ( names.find( name ) == names.end() ) return false;
|
|
// Then, change the default as requested:
|
|
for ( Iterator it = modifyBegin() ; it != modifyEnd() ; ++it )
|
|
(*it).setIsDefault( (*it).identityName() == name );
|
|
// and re-sort:
|
|
sort();
|
|
return true;
|
|
}
|
|
|
|
bool IdentityManager::setAsDefault( uint uoid ) {
|
|
// First, check if the identity actually exists:
|
|
bool found = false;
|
|
for ( ConstIterator it = mShadowIdentities.begin() ;
|
|
it != mShadowIdentities.end() ; ++it )
|
|
if ( (*it).uoid() == uoid ) {
|
|
found = true;
|
|
break;
|
|
}
|
|
if ( !found ) return false;
|
|
|
|
// Then, change the default as requested:
|
|
for ( Iterator it = modifyBegin() ; it != modifyEnd() ; ++it )
|
|
(*it).setIsDefault( (*it).uoid() == uoid );
|
|
// and re-sort:
|
|
sort();
|
|
return true;
|
|
}
|
|
|
|
bool IdentityManager::removeIdentity( const TQString & name ) {
|
|
for ( Iterator it = modifyBegin() ; it != modifyEnd() ; ++it )
|
|
if ( (*it).identityName() == name ) {
|
|
bool removedWasDefault = (*it).isDefault();
|
|
mShadowIdentities.remove( it );
|
|
if ( removedWasDefault )
|
|
mShadowIdentities.first().setIsDefault( true );
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Identity & IdentityManager::newFromScratch( const TQString & name ) {
|
|
return newFromExisting( Identity( name ) );
|
|
}
|
|
|
|
Identity & IdentityManager::newFromControlCenter( const TQString & name ) {
|
|
KEMailSettings es;
|
|
es.setProfile( es.defaultProfileName() );
|
|
|
|
return newFromExisting( Identity( name,
|
|
es.getSetting( KEMailSettings::RealName ),
|
|
es.getSetting( KEMailSettings::EmailAddress ),
|
|
es.getSetting( KEMailSettings::Organization ),
|
|
es.getSetting( KEMailSettings::ReplyToAddress )
|
|
) );
|
|
}
|
|
|
|
Identity & IdentityManager::newFromExisting( const Identity & other,
|
|
const TQString & name ) {
|
|
mShadowIdentities << other;
|
|
Identity & result = mShadowIdentities.last();
|
|
result.setIsDefault( false ); // we don't want two default identities!
|
|
result.setUoid( newUoid() ); // we don't want two identies w/ same UOID
|
|
if ( !name.isNull() )
|
|
result.setIdentityName( name );
|
|
return result;
|
|
}
|
|
|
|
void IdentityManager::createDefaultIdentity() {
|
|
TQString fullName, emailAddress;
|
|
bool done = false;
|
|
|
|
// Check if the application has any settings
|
|
createDefaultIdentity( fullName, emailAddress );
|
|
|
|
// If not, then use the kcontrol settings
|
|
if ( fullName.isEmpty() && emailAddress.isEmpty() ) {
|
|
KEMailSettings emailSettings;
|
|
fullName = emailSettings.getSetting( KEMailSettings::RealName );
|
|
emailAddress = emailSettings.getSetting( KEMailSettings::EmailAddress );
|
|
|
|
if ( !fullName.isEmpty() && !emailAddress.isEmpty() ) {
|
|
newFromControlCenter( i18n("Default") );
|
|
done = true;
|
|
} else {
|
|
// If KEmailSettings doesn't have name and address, generate something from KUser
|
|
KUser user;
|
|
if ( fullName.isEmpty() )
|
|
fullName = user.fullName();
|
|
if ( emailAddress.isEmpty() ) {
|
|
emailAddress = user.loginName();
|
|
if ( !emailAddress.isEmpty() ) {
|
|
KConfigGroup general( mConfig, "General" );
|
|
TQString defaultdomain = general.readEntry( "Default domain" );
|
|
if( !defaultdomain.isEmpty() ) {
|
|
emailAddress += '@' + defaultdomain;
|
|
}
|
|
else {
|
|
emailAddress = TQString();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !done )
|
|
mShadowIdentities << Identity( i18n("Default"), fullName, emailAddress );
|
|
|
|
mShadowIdentities.last().setIsDefault( true );
|
|
mShadowIdentities.last().setUoid( newUoid() );
|
|
if ( mReadOnly ) // commit won't do it in readonly mode
|
|
mIdentities = mShadowIdentities;
|
|
}
|
|
|
|
int IdentityManager::newUoid()
|
|
{
|
|
int uoid;
|
|
|
|
// determine the UOIDs of all saved identities
|
|
TQValueList<uint> usedUOIDs;
|
|
for ( TQValueList<Identity>::ConstIterator it = mIdentities.begin() ;
|
|
it != mIdentities.end() ; ++it )
|
|
usedUOIDs << (*it).uoid();
|
|
|
|
if ( hasPendingChanges() ) {
|
|
// add UOIDs of all shadow identities. Yes, we will add a lot of duplicate
|
|
// UOIDs, but avoiding duplicate UOIDs isn't worth the effort.
|
|
for ( TQValueList<Identity>::ConstIterator it = mShadowIdentities.begin() ;
|
|
it != mShadowIdentities.end() ; ++it ) {
|
|
usedUOIDs << (*it).uoid();
|
|
}
|
|
}
|
|
|
|
usedUOIDs << 0; // no UOID must be 0 because this value always refers to the
|
|
// default identity
|
|
|
|
do {
|
|
uoid = kapp->random();
|
|
} while ( usedUOIDs.find( uoid ) != usedUOIDs.end() );
|
|
|
|
return uoid;
|
|
}
|
|
|
|
TQStringList KPIM::IdentityManager::allEmails() const
|
|
{
|
|
TQStringList lst;
|
|
for ( ConstIterator it = begin() ; it != end() ; ++it ) {
|
|
lst << (*it).primaryEmailAddress();
|
|
}
|
|
return lst;
|
|
}
|
|
|
|
void KPIM::IdentityManager::slotIdentitiesChanged( TQCString appId, TQCString objId )
|
|
{
|
|
// From standalone kmail to standalone korganizer, the appId will differ
|
|
// From kontact the appId will match, so we need to test the objId
|
|
if ( kapp->dcopClient()->appId() != appId || DCOPObject::objId() != objId ) {
|
|
mConfig->reparseConfiguration();
|
|
Q_ASSERT( !hasPendingChanges() );
|
|
readConfig( mConfig );
|
|
}
|
|
}
|
|
|
|
#include "identitymanager.moc"
|