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.
1096 lines
37 KiB
1096 lines
37 KiB
/*
|
|
kopetecontactlist.cpp - Kopete's Contact List backend
|
|
|
|
Copyright (c) 2005 by Michael Larouche <michael.larouche@kdemail.net>
|
|
Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
|
|
Copyright (c) 2002-2004 by Olivier Goffart <ogoffart @ kde.org>
|
|
Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
|
|
|
|
Copyright (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
|
|
|
|
*************************************************************************
|
|
* *
|
|
* This library is free software; you can redistribute it and/or *
|
|
* modify it under the terms of the GNU Lesser General Public *
|
|
* License as published by the Free Software Foundation; either *
|
|
* version 2 of the License, or (at your option) any later version. *
|
|
* *
|
|
*************************************************************************
|
|
*/
|
|
|
|
#include "kopetecontactlist.h"
|
|
|
|
#include <tqdir.h>
|
|
#include <tqregexp.h>
|
|
#include <tqtimer.h>
|
|
|
|
#include <tdeapplication.h>
|
|
#include <tdeabc/stdaddressbook.h>
|
|
#include <kdebug.h>
|
|
#include <ksavefile.h>
|
|
#include <kstandarddirs.h>
|
|
#include <kopeteconfig.h>
|
|
#include <tdeglobal.h>
|
|
#include "kopetemetacontact.h"
|
|
#include "kopetecontact.h"
|
|
#include "kopetechatsession.h"
|
|
//#include "kopetemessage.h"
|
|
#include "kopetepluginmanager.h"
|
|
#include "kopeteprotocol.h"
|
|
#include "kopeteaccount.h"
|
|
#include "kopeteaccountmanager.h"
|
|
#include "kopetegroup.h"
|
|
#include "kopetepicture.h"
|
|
|
|
|
|
namespace Kopete
|
|
{
|
|
|
|
class ContactList::Private
|
|
{public:
|
|
/** Flag: do not save the contactlist until she is completely loaded */
|
|
bool loaded ;
|
|
|
|
TQPtrList<MetaContact> contacts;
|
|
TQPtrList<Group> groups;
|
|
TQPtrList<MetaContact> selectedMetaContacts;
|
|
TQPtrList<Group> selectedGroups;
|
|
|
|
TQTimer *saveTimer;
|
|
|
|
MetaContact *myself;
|
|
|
|
/** Flag: does the user uses the global identity */
|
|
bool useGlobalIdentity;
|
|
|
|
/**
|
|
* Current contact list version * 10 ( i.e. '10' is version '1.0' )
|
|
*/
|
|
static const uint ContactListVersion = 10;
|
|
};
|
|
|
|
ContactList *ContactList::s_self = 0L;
|
|
|
|
ContactList *ContactList::self()
|
|
{
|
|
if( !s_self )
|
|
s_self = new ContactList;
|
|
|
|
return s_self;
|
|
}
|
|
|
|
ContactList::ContactList()
|
|
: TQObject( kapp, "KopeteContactList" )
|
|
{
|
|
d=new Private;
|
|
|
|
//the myself metacontact can't be created now, because it will use
|
|
//ContactList::self() as parent which will call this constructor -> infinite loop
|
|
d->myself=0L;
|
|
|
|
//no contactlist loaded yet, don't save them
|
|
d->loaded=false;
|
|
|
|
// automatically save on changes to the list
|
|
d->saveTimer = new TQTimer( this, "saveTimer" );
|
|
connect( d->saveTimer, TQT_SIGNAL( timeout() ), TQT_SLOT ( save() ) );
|
|
|
|
connect( this, TQT_SIGNAL( metaContactAdded( Kopete::MetaContact * ) ), TQT_SLOT( slotSaveLater() ) );
|
|
connect( this, TQT_SIGNAL( metaContactRemoved( Kopete::MetaContact * ) ), TQT_SLOT( slotSaveLater() ) );
|
|
connect( this, TQT_SIGNAL( groupAdded( Kopete::Group * ) ), TQT_SLOT( slotSaveLater() ) );
|
|
connect( this, TQT_SIGNAL( groupRemoved( Kopete::Group * ) ), TQT_SLOT( slotSaveLater() ) );
|
|
connect( this, TQT_SIGNAL( groupRenamed( Kopete::Group *, const TQString & ) ), TQT_SLOT( slotSaveLater() ) );
|
|
}
|
|
|
|
ContactList::~ContactList()
|
|
{
|
|
delete d->myself;
|
|
delete d;
|
|
}
|
|
|
|
TQPtrList<MetaContact> ContactList::metaContacts() const
|
|
{
|
|
return d->contacts;
|
|
}
|
|
|
|
|
|
TQPtrList<Group> ContactList::groups() const
|
|
{
|
|
return d->groups;
|
|
}
|
|
|
|
|
|
MetaContact *ContactList::metaContact( const TQString &metaContactId ) const
|
|
{
|
|
TQPtrListIterator<MetaContact> it( d->contacts );
|
|
|
|
for( ; it.current(); ++it )
|
|
{
|
|
if( it.current()->metaContactId() == metaContactId )
|
|
return it.current();
|
|
}
|
|
|
|
return 0L;
|
|
}
|
|
|
|
|
|
Group * ContactList::group(unsigned int groupId) const
|
|
{
|
|
Group *groupIterator;
|
|
for ( groupIterator = d->groups.first(); groupIterator; groupIterator = d->groups.next() )
|
|
{
|
|
if( groupIterator->groupId()==groupId )
|
|
return groupIterator;
|
|
}
|
|
return 0L;
|
|
}
|
|
|
|
|
|
Contact *ContactList::findContact( const TQString &protocolId,
|
|
const TQString &accountId, const TQString &contactId ) const
|
|
{
|
|
//Browsing metacontacts is too slow, better to uses the Dict of the account.
|
|
Account *i=AccountManager::self()->findAccount(protocolId,accountId);
|
|
if(!i)
|
|
{
|
|
kdDebug( 14010 ) << k_funcinfo << "Account not found" << endl;
|
|
return 0L;
|
|
}
|
|
return i->contacts()[contactId];
|
|
}
|
|
|
|
|
|
MetaContact *ContactList::findMetaContactByDisplayName( const TQString &displayName ) const
|
|
{
|
|
TQPtrListIterator<MetaContact> it( d->contacts );
|
|
for( ; it.current(); ++it )
|
|
{
|
|
// kdDebug(14010) << "Display Name: " << it.current()->displayName() << "\n";
|
|
if( it.current()->displayName() == displayName ) {
|
|
return it.current();
|
|
}
|
|
}
|
|
|
|
return 0L;
|
|
}
|
|
|
|
MetaContact* ContactList::findMetaContactByContactId( const TQString &contactId ) const
|
|
{
|
|
TQPtrList<Account> acts=AccountManager::self()->accounts();
|
|
TQPtrListIterator<Account> it( acts );
|
|
for ( ; it.current(); ++it )
|
|
{
|
|
Contact *c=(*it)->contacts()[contactId];
|
|
if(c && c->metaContact())
|
|
return c->metaContact();
|
|
}
|
|
return 0L;
|
|
}
|
|
|
|
Group * ContactList::findGroup(const TQString& displayName, int type)
|
|
{
|
|
if( type == Group::Temporary )
|
|
return Group::temporary();
|
|
|
|
Group *groupIterator;
|
|
for ( groupIterator = d->groups.first(); groupIterator; groupIterator = d->groups.next() )
|
|
{
|
|
if( groupIterator->type() == type && groupIterator->displayName() == displayName )
|
|
return groupIterator;
|
|
}
|
|
|
|
Group *newGroup = new Group( displayName, (Group::GroupType)type );
|
|
addGroup( newGroup );
|
|
return newGroup;
|
|
}
|
|
|
|
|
|
TQPtrList<MetaContact> ContactList::selectedMetaContacts() const
|
|
{
|
|
return d->selectedMetaContacts;
|
|
}
|
|
|
|
TQPtrList<Group> ContactList::selectedGroups() const
|
|
{
|
|
return d->selectedGroups;
|
|
}
|
|
|
|
|
|
void ContactList::addMetaContact( MetaContact *mc )
|
|
{
|
|
if ( d->contacts.contains( mc ) )
|
|
return;
|
|
|
|
d->contacts.append( mc );
|
|
|
|
emit metaContactAdded( mc );
|
|
connect( mc, TQT_SIGNAL( persistentDataChanged( ) ), TQT_SLOT( slotSaveLater() ) );
|
|
connect( mc, TQT_SIGNAL( addedToGroup( Kopete::MetaContact *, Kopete::Group * ) ), TQT_SIGNAL( metaContactAddedToGroup( Kopete::MetaContact *, Kopete::Group * ) ) );
|
|
connect( mc, TQT_SIGNAL( removedFromGroup( Kopete::MetaContact *, Kopete::Group * ) ), TQT_SIGNAL( metaContactRemovedFromGroup( Kopete::MetaContact *, Kopete::Group * ) ) );
|
|
}
|
|
|
|
|
|
void ContactList::removeMetaContact(MetaContact *m)
|
|
{
|
|
if ( !d->contacts.contains(m) )
|
|
{
|
|
kdDebug(14010) << k_funcinfo << "Trying to remove a not listed MetaContact." << endl;
|
|
return;
|
|
}
|
|
|
|
if ( d->selectedMetaContacts.contains( m ) )
|
|
{
|
|
d->selectedMetaContacts.remove( m );
|
|
setSelectedItems( d->selectedMetaContacts, d->selectedGroups );
|
|
}
|
|
|
|
//removes subcontact from server here and now.
|
|
TQPtrList<Contact> cts=m->contacts();
|
|
for( Contact *c = cts.first(); c; c = cts.next() )
|
|
{
|
|
c->deleteContact();
|
|
}
|
|
|
|
d->contacts.remove( m );
|
|
emit metaContactRemoved( m );
|
|
m->deleteLater();
|
|
}
|
|
|
|
|
|
void ContactList::addGroup( Group * g )
|
|
{
|
|
if(!d->groups.contains(g) )
|
|
{
|
|
d->groups.append( g );
|
|
emit groupAdded( g );
|
|
connect( g , TQT_SIGNAL ( displayNameChanged(Kopete::Group* , const TQString & )) , this , TQT_SIGNAL ( groupRenamed(Kopete::Group* , const TQString & )) ) ;
|
|
}
|
|
}
|
|
|
|
void ContactList::removeGroup( Group *g )
|
|
{
|
|
if ( d->selectedGroups.contains( g ) )
|
|
{
|
|
d->selectedGroups.remove( g );
|
|
setSelectedItems( d->selectedMetaContacts, d->selectedGroups );
|
|
}
|
|
|
|
d->groups.remove( g );
|
|
emit groupRemoved( g );
|
|
g->deleteLater();
|
|
}
|
|
|
|
|
|
void ContactList::setSelectedItems(TQPtrList<MetaContact> metaContacts , TQPtrList<Group> groups)
|
|
{
|
|
kdDebug( 14010 ) << k_funcinfo << metaContacts.count() << " metacontacts, " << groups.count() << " groups selected" << endl;
|
|
d->selectedMetaContacts=metaContacts;
|
|
d->selectedGroups=groups;
|
|
|
|
emit metaContactSelected( groups.isEmpty() && metaContacts.count()==1 );
|
|
emit selectionChanged();
|
|
}
|
|
|
|
MetaContact* ContactList::myself()
|
|
{
|
|
if(!d->myself)
|
|
d->myself=new MetaContact();
|
|
return d->myself;
|
|
}
|
|
|
|
void ContactList::loadGlobalIdentity()
|
|
{
|
|
// Apply the global identity
|
|
if(Kopete::Config::enableGlobalIdentity())
|
|
{
|
|
// Disconnect to make sure it will not cause duplicate calls.
|
|
disconnect(myself(), TQT_SIGNAL(displayNameChanged(const TQString&, const TQString&)), this, TQT_SLOT(slotDisplayNameChanged()));
|
|
disconnect(myself(), TQT_SIGNAL(photoChanged()), this, TQT_SLOT(slotPhotoChanged()));
|
|
|
|
connect(myself(), TQT_SIGNAL(displayNameChanged(const TQString&, const TQString&)), this, TQT_SLOT(slotDisplayNameChanged()));
|
|
connect(myself(), TQT_SIGNAL(photoChanged()), this, TQT_SLOT(slotPhotoChanged()));
|
|
|
|
// Ensure that the myself metaContactId is always the KABC whoAmI
|
|
TDEABC::Addressee a = TDEABC::StdAddressBook::self()->whoAmI();
|
|
if(!a.isEmpty() && a.uid() != myself()->metaContactId())
|
|
{
|
|
myself()->setMetaContactId(a.uid());
|
|
}
|
|
|
|
// Apply the global identity
|
|
// Maybe one of the myself contact from a account has a different displayName/photo at startup.
|
|
slotDisplayNameChanged();
|
|
slotPhotoChanged();
|
|
}
|
|
else
|
|
{
|
|
disconnect(myself(), TQT_SIGNAL(displayNameChanged(const TQString&, const TQString&)), this, TQT_SLOT(slotDisplayNameChanged()));
|
|
disconnect(myself(), TQT_SIGNAL(photoChanged()), this, TQT_SLOT(slotPhotoChanged()));
|
|
}
|
|
}
|
|
|
|
void ContactList::slotDisplayNameChanged()
|
|
{
|
|
static bool mutex=false;
|
|
if(mutex)
|
|
{
|
|
kdDebug (14010) << k_funcinfo << " mutex blocked" << endl ;
|
|
return;
|
|
}
|
|
mutex=true;
|
|
|
|
kdDebug( 14010 ) << k_funcinfo << myself()->displayName() << endl;
|
|
|
|
emit globalIdentityChanged(Kopete::Global::Properties::self()->nickName().key(), myself()->displayName());
|
|
mutex=false;
|
|
}
|
|
|
|
void ContactList::slotPhotoChanged()
|
|
{
|
|
static bool mutex=false;
|
|
if(mutex)
|
|
{
|
|
kdDebug (14010) << k_funcinfo << " mutex blocked" << endl ;
|
|
return;
|
|
}
|
|
mutex=true;
|
|
kdDebug( 14010 ) << k_funcinfo << myself()->picture().path() << endl;
|
|
|
|
emit globalIdentityChanged(Kopete::Global::Properties::self()->photo().key(), myself()->picture().path());
|
|
mutex=false;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
void ContactList::load()
|
|
{
|
|
loadXML();
|
|
// Apply the global identity when all the protocols plugins are loaded.
|
|
connect(PluginManager::self(), TQT_SIGNAL(allPluginsLoaded()), this, TQT_SLOT(loadGlobalIdentity()));
|
|
}
|
|
|
|
void ContactList::loadXML()
|
|
{
|
|
// don't save when we're in the middle of this...
|
|
d->loaded = false;
|
|
|
|
TQString filename = locateLocal( "appdata", TQString::fromLatin1( "contactlist.xml" ) );
|
|
if( filename.isEmpty() )
|
|
{
|
|
d->loaded=true;
|
|
return ;
|
|
}
|
|
|
|
TQDomDocument contactList( TQString::fromLatin1( "kopete-contact-list" ) );
|
|
|
|
TQFile contactListFile( filename );
|
|
contactListFile.open( IO_ReadOnly );
|
|
contactList.setContent( &contactListFile );
|
|
|
|
TQDomElement list = contactList.documentElement();
|
|
|
|
TQString versionString = list.attribute( TQString::fromLatin1( "version" ), TQString() );
|
|
uint version = 0;
|
|
if( TQRegExp( TQString::fromLatin1( "[0-9]+\\.[0-9]" ) ).exactMatch( versionString ) )
|
|
version = versionString.replace( TQString::fromLatin1( "." ), TQString() ).toUInt();
|
|
|
|
if( version < Private::ContactListVersion )
|
|
{
|
|
// The version string is invalid, or we're using an older version.
|
|
// Convert first and reparse the file afterwards
|
|
kdDebug( 14010 ) << k_funcinfo << "Contact list version " << version
|
|
<< " is older than current version " << Private::ContactListVersion
|
|
<< ". Converting first." << endl;
|
|
|
|
contactListFile.close();
|
|
|
|
convertContactList( filename, version, Private::ContactListVersion );
|
|
|
|
contactList = TQDomDocument ( TQString::fromLatin1( "kopete-contact-list" ) );
|
|
|
|
contactListFile.open( IO_ReadOnly );
|
|
contactList.setContent( &contactListFile );
|
|
|
|
list = contactList.documentElement();
|
|
}
|
|
|
|
addGroup( Kopete::Group::topLevel() );
|
|
|
|
TQDomElement element = list.firstChild().toElement();
|
|
while( !element.isNull() )
|
|
{
|
|
if( element.tagName() == TQString::fromLatin1("meta-contact") )
|
|
{
|
|
//TODO: id isn't used
|
|
//TQString id = element.attribute( "id", TQString() );
|
|
Kopete::MetaContact *metaContact = new Kopete::MetaContact();
|
|
if ( !metaContact->fromXML( element ) )
|
|
{
|
|
delete metaContact;
|
|
metaContact = 0;
|
|
}
|
|
else
|
|
{
|
|
Kopete::ContactList::self()->addMetaContact(
|
|
metaContact );
|
|
}
|
|
}
|
|
else if( element.tagName() == TQString::fromLatin1("kopete-group") )
|
|
{
|
|
Kopete::Group *group = new Kopete::Group();
|
|
if( !group->fromXML( element ) )
|
|
{
|
|
delete group;
|
|
group = 0;
|
|
}
|
|
else
|
|
{
|
|
Kopete::ContactList::self()->addGroup( group );
|
|
}
|
|
}
|
|
// Only load myself metacontact information when Global Identity is enabled.
|
|
else if( element.tagName() == TQString::fromLatin1("myself-meta-contact") && Kopete::Config::enableGlobalIdentity() )
|
|
{
|
|
if( !myself()->fromXML( element ) )
|
|
{
|
|
delete d->myself;
|
|
d->myself = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
kdWarning(14010) << "Kopete::ContactList::loadXML: "
|
|
<< "Unknown element '" << element.tagName()
|
|
<< "' in contact list!" << endl;
|
|
}
|
|
element = element.nextSibling().toElement();
|
|
}
|
|
contactListFile.close();
|
|
d->loaded=true;
|
|
}
|
|
|
|
void ContactList::convertContactList( const TQString &fileName, uint /* fromVersion */, uint /* toVersion */ )
|
|
{
|
|
// For now, ignore fromVersion and toVersion. These are meant for future
|
|
// changes to allow incremental (multi-pass) conversion so we don't have
|
|
// to rewrite the whole conversion code for each change.
|
|
|
|
TQDomDocument contactList( TQString::fromLatin1( "messaging-contact-list" ) );
|
|
TQFile contactListFile( fileName );
|
|
contactListFile.open( IO_ReadOnly );
|
|
contactList.setContent( &contactListFile );
|
|
|
|
TQDomElement oldList = contactList.documentElement();
|
|
|
|
TQDomDocument newList( TQString::fromLatin1( "kopete-contact-list" ) );
|
|
newList.appendChild( newList.createProcessingInstruction( TQString::fromLatin1( "xml" ), TQString::fromLatin1( "version=\"1.0\"" ) ) );
|
|
|
|
TQDomElement newRoot = newList.createElement( TQString::fromLatin1( "kopete-contact-list" ) );
|
|
newList.appendChild( newRoot );
|
|
newRoot.setAttribute( TQString::fromLatin1( "version" ), TQString::fromLatin1( "1.0" ) );
|
|
|
|
TQDomNode oldNode = oldList.firstChild();
|
|
while( !oldNode.isNull() )
|
|
{
|
|
TQDomElement oldElement = oldNode.toElement();
|
|
if( !oldElement.isNull() )
|
|
{
|
|
if( oldElement.tagName() == TQString::fromLatin1("meta-contact") )
|
|
{
|
|
// Ignore ID, it is not used in the current list anyway
|
|
TQDomElement newMetaContact = newList.createElement( TQString::fromLatin1( "meta-contact" ) );
|
|
newRoot.appendChild( newMetaContact );
|
|
|
|
// Plugin data is stored completely different, and requires
|
|
// some bookkeeping to convert properly
|
|
TQMap<TQString, TQDomElement> pluginData;
|
|
TQStringList icqData;
|
|
TQStringList gaduData;
|
|
|
|
// ICQ and Gadu can only be converted properly if the address book fields
|
|
// are already parsed. Therefore, scan for those first and add the rest
|
|
// afterwards
|
|
TQDomNode oldContactNode = oldNode.firstChild();
|
|
while( !oldContactNode.isNull() )
|
|
{
|
|
TQDomElement oldContactElement = oldContactNode.toElement();
|
|
if( !oldContactElement.isNull() && oldContactElement.tagName() == TQString::fromLatin1("address-book-field") )
|
|
{
|
|
// Convert address book fields.
|
|
// Jabber will be called "xmpp", Aim/Toc and Aim/Oscar both will
|
|
// be called "aim". AIM, IRC, Oscar and SMS don't use address
|
|
// book fields yet; Gadu and ICQ can be converted as-is.
|
|
// As Yahoo is unfinished we won't try to convert at all.
|
|
TQString id = oldContactElement.attribute( TQString::fromLatin1( "id" ), TQString() );
|
|
TQString data = oldContactElement.text();
|
|
|
|
TQString app, key, val;
|
|
TQString separator = TQString::fromLatin1( "," );
|
|
if( id == TQString::fromLatin1( "messaging/gadu" ) )
|
|
separator = TQString::fromLatin1( "\n" );
|
|
else if( id == TQString::fromLatin1( "messaging/icq" ) )
|
|
separator = TQString::fromLatin1( ";" );
|
|
else if( id == TQString::fromLatin1( "messaging/jabber" ) )
|
|
id = TQString::fromLatin1( "messaging/xmpp" );
|
|
|
|
if( id == TQString::fromLatin1( "messaging/gadu" ) || id == TQString::fromLatin1( "messaging/icq" ) ||
|
|
id == TQString::fromLatin1( "messaging/winpopup" ) || id == TQString::fromLatin1( "messaging/xmpp" ) )
|
|
{
|
|
app = id;
|
|
key = TQString::fromLatin1( "All" );
|
|
val = data.replace( separator, TQChar( 0xE000 ) );
|
|
}
|
|
|
|
if( !app.isEmpty() )
|
|
{
|
|
TQDomElement addressBookField = newList.createElement( TQString::fromLatin1( "address-book-field" ) );
|
|
newMetaContact.appendChild( addressBookField );
|
|
|
|
addressBookField.setAttribute( TQString::fromLatin1( "app" ), app );
|
|
addressBookField.setAttribute( TQString::fromLatin1( "key" ), key );
|
|
|
|
addressBookField.appendChild( newList.createTextNode( val ) );
|
|
|
|
// ICQ didn't store the contactId locally, only in the address
|
|
// book fields, so we need to be able to access it later
|
|
if( id == TQString::fromLatin1( "messaging/icq" ) )
|
|
icqData = TQStringList::split( TQChar( 0xE000 ), val );
|
|
else if( id == TQString::fromLatin1("messaging/gadu") )
|
|
gaduData = TQStringList::split( TQChar( 0xE000 ), val );
|
|
}
|
|
}
|
|
oldContactNode = oldContactNode.nextSibling();
|
|
}
|
|
|
|
// Now, convert the other elements
|
|
oldContactNode = oldNode.firstChild();
|
|
while( !oldContactNode.isNull() )
|
|
{
|
|
TQDomElement oldContactElement = oldContactNode.toElement();
|
|
if( !oldContactElement.isNull() )
|
|
{
|
|
if( oldContactElement.tagName() == TQString::fromLatin1("display-name") )
|
|
{
|
|
TQDomElement displayName = newList.createElement( TQString::fromLatin1( "display-name" ) );
|
|
displayName.appendChild( newList.createTextNode( oldContactElement.text() ) );
|
|
newMetaContact.appendChild( displayName );
|
|
}
|
|
else if( oldContactElement.tagName() == TQString::fromLatin1("groups") )
|
|
{
|
|
TQDomElement groups = newList.createElement( TQString::fromLatin1( "groups" ) );
|
|
newMetaContact.appendChild( groups );
|
|
|
|
TQDomNode oldGroup = oldContactElement.firstChild();
|
|
while( !oldGroup.isNull() )
|
|
{
|
|
TQDomElement oldGroupElement = oldGroup.toElement();
|
|
if ( oldGroupElement.tagName() == TQString::fromLatin1("group") )
|
|
{
|
|
TQDomElement group = newList.createElement( TQString::fromLatin1( "group" ) );
|
|
group.appendChild( newList.createTextNode( oldGroupElement.text() ) );
|
|
groups.appendChild( group );
|
|
}
|
|
else if ( oldGroupElement.tagName() == TQString::fromLatin1("top-level") )
|
|
{
|
|
TQDomElement group = newList.createElement( TQString::fromLatin1( "top-level" ) );
|
|
groups.appendChild( group );
|
|
}
|
|
|
|
oldGroup = oldGroup.nextSibling();
|
|
}
|
|
}
|
|
else if( oldContactElement.tagName() == TQString::fromLatin1( "plugin-data" ) )
|
|
{
|
|
// Convert the plugin data
|
|
TQString id = oldContactElement.attribute( TQString::fromLatin1( "plugin-id" ), TQString() );
|
|
TQString data = oldContactElement.text();
|
|
|
|
bool convertOldAim = false;
|
|
uint fieldCount = 1;
|
|
TQString addressBookLabel;
|
|
if( id == TQString::fromLatin1("IRCProtocol") )
|
|
{
|
|
fieldCount = 3;
|
|
addressBookLabel = TQString::fromLatin1("irc");
|
|
}
|
|
else if( id == TQString::fromLatin1("OscarProtocol") )
|
|
{
|
|
fieldCount = 2;
|
|
addressBookLabel = TQString::fromLatin1("aim");
|
|
}
|
|
else if( id == TQString::fromLatin1("AIMProtocol") )
|
|
{
|
|
id = TQString::fromLatin1("OscarProtocol");
|
|
convertOldAim = true;
|
|
addressBookLabel = TQString::fromLatin1("aim");
|
|
}
|
|
else if( id == TQString::fromLatin1("ICTQProtocol") || id == TQString::fromLatin1("WPProtocol") || id == TQString::fromLatin1("GaduProtocol") )
|
|
{
|
|
fieldCount = 1;
|
|
}
|
|
else if( id == TQString::fromLatin1("JabberProtocol") )
|
|
{
|
|
fieldCount = 4;
|
|
}
|
|
else if( id == TQString::fromLatin1("SMSProtocol") )
|
|
{
|
|
// SMS used a variable serializing using a dot as delimiter.
|
|
// The minimal count is three though (id, name, delimiter).
|
|
fieldCount = 2;
|
|
addressBookLabel = TQString::fromLatin1("sms");
|
|
}
|
|
|
|
if( pluginData[ id ].isNull() )
|
|
{
|
|
pluginData[ id ] = newList.createElement( TQString::fromLatin1( "plugin-data" ) );
|
|
pluginData[ id ].setAttribute( TQString::fromLatin1( "plugin-id" ), id );
|
|
newMetaContact.appendChild( pluginData[ id ] );
|
|
}
|
|
|
|
// Do the actual conversion
|
|
if ( id == TQString::fromLatin1( "OscarProtocol" ) ||
|
|
id == TQString::fromLatin1( "AIMProtocol" ) || id == TQString::fromLatin1( "IRCProtocol" ) ||
|
|
id == TQString::fromLatin1( "ICTQProtocol" ) || id == TQString::fromLatin1( "JabberProtocol" ) ||
|
|
id == TQString::fromLatin1( "SMSProtocol" ) || id == TQString::fromLatin1( "WPProtocol" ) ||
|
|
id == TQString::fromLatin1( "GaduProtocol" ) )
|
|
{
|
|
TQStringList strList = TQStringList::split( TQString::fromLatin1( "||" ), data );
|
|
|
|
// Unescape '||'
|
|
for( TQStringList::iterator it = strList.begin(); it != strList.end(); ++it )
|
|
{
|
|
( *it ).replace( TQString::fromLatin1( "\\|;" ), TQString::fromLatin1( "|" ) ).
|
|
replace( TQString::fromLatin1( "\\\\" ), TQString::fromLatin1( "\\" ) );
|
|
}
|
|
|
|
uint idx = 0;
|
|
while( idx < strList.size() )
|
|
{
|
|
TQDomElement dataField;
|
|
|
|
dataField = newList.createElement( TQString::fromLatin1( "plugin-data-field" ) );
|
|
pluginData[ id ].appendChild( dataField );
|
|
dataField.setAttribute( TQString::fromLatin1( "key" ), TQString::fromLatin1( "contactId" ) );
|
|
if( id == TQString::fromLatin1("ICTQProtocol") )
|
|
dataField.appendChild( newList.createTextNode( icqData[ idx ] ) );
|
|
else if( id == TQString::fromLatin1("GaduProtocol") )
|
|
dataField.appendChild( newList.createTextNode( gaduData[ idx ] ) );
|
|
else if( id == TQString::fromLatin1("JabberProtocol") )
|
|
dataField.appendChild( newList.createTextNode( strList[ idx + 1 ] ) );
|
|
else
|
|
dataField.appendChild( newList.createTextNode( strList[ idx ] ) );
|
|
|
|
dataField = newList.createElement( TQString::fromLatin1( "plugin-data-field" ) );
|
|
pluginData[ id ].appendChild( dataField );
|
|
dataField.setAttribute( TQString::fromLatin1( "key" ), TQString::fromLatin1( "displayName" ) );
|
|
if( convertOldAim || id == TQString::fromLatin1("ICTQProtocol") || id == TQString::fromLatin1("WPProtocol") || id == TQString::fromLatin1("GaduProtocol") )
|
|
dataField.appendChild( newList.createTextNode( strList[ idx ] ) );
|
|
else if( id == TQString::fromLatin1("JabberProtocol") )
|
|
dataField.appendChild( newList.createTextNode( strList[ idx + 2 ] ) );
|
|
else
|
|
dataField.appendChild( newList.createTextNode( strList[ idx + 1 ] ) );
|
|
|
|
if( id == TQString::fromLatin1("IRCProtocol") )
|
|
{
|
|
dataField = newList.createElement( TQString::fromLatin1( "plugin-data-field" ) );
|
|
pluginData[ id ].appendChild( dataField );
|
|
dataField.setAttribute( TQString::fromLatin1( "key" ), TQString::fromLatin1( "serverName" ) );
|
|
dataField.appendChild( newList.createTextNode( strList[ idx + 2 ] ) );
|
|
}
|
|
else if( id == TQString::fromLatin1("JabberProtocol") )
|
|
{
|
|
dataField = newList.createElement( TQString::fromLatin1( "plugin-data-field" ) );
|
|
pluginData[ id ].appendChild( dataField );
|
|
dataField.setAttribute( TQString::fromLatin1( "key" ), TQString::fromLatin1( "accountId" ) );
|
|
dataField.appendChild( newList.createTextNode( strList[ idx ] ) );
|
|
|
|
dataField = newList.createElement( TQString::fromLatin1( "plugin-data-field" ) );
|
|
pluginData[ id ].appendChild( dataField );
|
|
dataField.setAttribute( TQString::fromLatin1( "key" ), TQString::fromLatin1( "groups" ) );
|
|
dataField.appendChild( newList.createTextNode( strList[ idx + 3 ] ) );
|
|
}
|
|
else if( id == TQString::fromLatin1( "SMSProtocol" ) &&
|
|
( idx + 2 < strList.size() ) && strList[ idx + 2 ] != TQString::fromLatin1( "." ) )
|
|
{
|
|
dataField = newList.createElement( TQString::fromLatin1( "plugin-data-field" ) );
|
|
pluginData[ id ].appendChild( dataField );
|
|
dataField.setAttribute( TQString::fromLatin1( "key" ), TQString::fromLatin1( "serviceName" ) );
|
|
dataField.appendChild( newList.createTextNode( strList[ idx + 2 ] ) );
|
|
|
|
dataField = newList.createElement( TQString::fromLatin1( "plugin-data-field" ) );
|
|
pluginData[ id ].appendChild( dataField );
|
|
dataField.setAttribute( TQString::fromLatin1( "key" ), TQString::fromLatin1( "servicePrefs" ) );
|
|
dataField.appendChild( newList.createTextNode( strList[ idx + 3 ] ) );
|
|
|
|
// Add extra fields
|
|
idx += 2;
|
|
}
|
|
|
|
// AIM, IRC, Oscar and SMS didn't store address book fields up
|
|
// to now, so create one
|
|
if( id != TQString::fromLatin1("ICTQProtocol") && id != TQString::fromLatin1("JabberProtocol") && id != TQString::fromLatin1("WPProtocol") && id != TQString::fromLatin1("GaduProtocol") )
|
|
{
|
|
TQDomElement addressBookField = newList.createElement( TQString::fromLatin1( "address-book-field" ) );
|
|
newMetaContact.appendChild( addressBookField );
|
|
|
|
addressBookField.setAttribute( TQString::fromLatin1( "app" ),
|
|
TQString::fromLatin1( "messaging/" ) + addressBookLabel );
|
|
addressBookField.setAttribute( TQString::fromLatin1( "key" ), TQString::fromLatin1( "All" ) );
|
|
addressBookField.appendChild( newList.createTextNode( strList[ idx ] ) );
|
|
}
|
|
|
|
idx += fieldCount;
|
|
}
|
|
}
|
|
else if( id == TQString::fromLatin1("ContactNotesPlugin") || id == TQString::fromLatin1("CryptographyPlugin") || id == TQString::fromLatin1("TranslatorPlugin") )
|
|
{
|
|
TQDomElement dataField = newList.createElement( TQString::fromLatin1( "plugin-data-field" ) );
|
|
pluginData[ id ].appendChild( dataField );
|
|
if( id == TQString::fromLatin1("ContactNotesPlugin") )
|
|
dataField.setAttribute( TQString::fromLatin1( "key" ), TQString::fromLatin1( "notes" ) );
|
|
else if( id == TQString::fromLatin1("CryptographyPlugin") )
|
|
dataField.setAttribute( TQString::fromLatin1( "key" ), TQString::fromLatin1( "gpgKey" ) );
|
|
else if( id == TQString::fromLatin1("TranslatorPlugin") )
|
|
dataField.setAttribute( TQString::fromLatin1( "key" ), TQString::fromLatin1( "languageKey" ) );
|
|
|
|
dataField.appendChild( newList.createTextNode( data ) );
|
|
}
|
|
}
|
|
}
|
|
oldContactNode = oldContactNode.nextSibling();
|
|
}
|
|
}
|
|
else if( oldElement.tagName() == TQString::fromLatin1("kopete-group") )
|
|
{
|
|
TQDomElement newGroup = newList.createElement( TQString::fromLatin1( "kopete-group" ) );
|
|
newRoot.appendChild( newGroup );
|
|
|
|
TQDomNode oldGroupNode = oldNode.firstChild();
|
|
while( !oldGroupNode.isNull() )
|
|
{
|
|
TQDomElement oldGroupElement = oldGroupNode.toElement();
|
|
|
|
if( oldGroupElement.tagName() == TQString::fromLatin1("display-name") )
|
|
{
|
|
TQDomElement displayName = newList.createElement( TQString::fromLatin1( "display-name" ) );
|
|
displayName.appendChild( newList.createTextNode( oldGroupElement.text() ) );
|
|
newGroup.appendChild( displayName );
|
|
}
|
|
if( oldGroupElement.tagName() == TQString::fromLatin1("type") )
|
|
{
|
|
if( oldGroupElement.text() == TQString::fromLatin1("Temporary") )
|
|
newGroup.setAttribute( TQString::fromLatin1( "type" ), TQString::fromLatin1( "temporary" ) );
|
|
else if( oldGroupElement.text() == TQString::fromLatin1( "TopLevel" ) )
|
|
newGroup.setAttribute( TQString::fromLatin1( "type" ), TQString::fromLatin1( "top-level" ) );
|
|
else
|
|
newGroup.setAttribute( TQString::fromLatin1( "type" ), TQString::fromLatin1( "standard" ) );
|
|
}
|
|
if( oldGroupElement.tagName() == TQString::fromLatin1("view") )
|
|
{
|
|
if( oldGroupElement.text() == TQString::fromLatin1("collapsed") )
|
|
newGroup.setAttribute( TQString::fromLatin1( "view" ), TQString::fromLatin1( "collapsed" ) );
|
|
else
|
|
newGroup.setAttribute( TQString::fromLatin1( "view" ), TQString::fromLatin1( "expanded" ) );
|
|
}
|
|
else if( oldGroupElement.tagName() == TQString::fromLatin1("plugin-data") )
|
|
{
|
|
// Per-group plugin data
|
|
// FIXME: This needs updating too, ideally, convert this in a later
|
|
// contactlist.xml version
|
|
TQDomElement groupPluginData = newList.createElement( TQString::fromLatin1( "plugin-data" ) );
|
|
newGroup.appendChild( groupPluginData );
|
|
|
|
groupPluginData.setAttribute( TQString::fromLatin1( "plugin-id" ),
|
|
oldGroupElement.attribute( TQString::fromLatin1( "plugin-id" ), TQString() ) );
|
|
groupPluginData.appendChild( newList.createTextNode( oldGroupElement.text() ) );
|
|
}
|
|
|
|
oldGroupNode = oldGroupNode.nextSibling();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
kdWarning( 14010 ) << k_funcinfo << "Unknown element '" << oldElement.tagName()
|
|
<< "' in contact list!" << endl;
|
|
}
|
|
}
|
|
oldNode = oldNode.nextSibling();
|
|
}
|
|
|
|
// Close the file, and save the new file
|
|
contactListFile.close();
|
|
|
|
TQDir().rename( fileName, fileName + TQString::fromLatin1( ".bak" ) );
|
|
|
|
// kdDebug( 14010 ) << k_funcinfo << "XML output:\n" << newList.toString( 2 ) << endl;
|
|
|
|
contactListFile.open( IO_WriteOnly );
|
|
TQTextStream stream( &contactListFile );
|
|
stream.setEncoding( TQTextStream::UnicodeUTF8 );
|
|
stream << newList.toString( 2 );
|
|
|
|
contactListFile.flush();
|
|
contactListFile.close();
|
|
}
|
|
|
|
void Kopete::ContactList::save()
|
|
{
|
|
saveXML();
|
|
}
|
|
|
|
void Kopete::ContactList::saveXML()
|
|
{
|
|
if(!d->loaded)
|
|
{
|
|
kdDebug(14010) << "Kopete::ContactList::saveXML: contactlist not loaded, abort saving" << endl;
|
|
return;
|
|
}
|
|
|
|
TQString contactListFileName = locateLocal( "appdata", TQString::fromLatin1( "contactlist.xml" ) );
|
|
KSaveFile contactListFile( contactListFileName );
|
|
if( contactListFile.status() == 0 )
|
|
{
|
|
TQTextStream *stream = contactListFile.textStream();
|
|
stream->setEncoding( TQTextStream::UnicodeUTF8 );
|
|
toXML().save( *stream, 4 );
|
|
|
|
if ( contactListFile.close() )
|
|
{
|
|
// cancel any scheduled saves
|
|
d->saveTimer->stop();
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
kdDebug(14010) << "Kopete::ContactList::saveXML: failed to write contactlist, error code is: " << contactListFile.status() << endl;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
kdWarning(14010) << "Kopete::ContactList::saveXML: Couldn't open contact list file "
|
|
<< contactListFileName << ". Contact list not saved." << endl;
|
|
}
|
|
|
|
// if we got here, saving the contact list failed. retry every minute until it works.
|
|
d->saveTimer->start( 60000, true /* single-shot: will get restarted by us next time if it's still failing */ );
|
|
}
|
|
|
|
const TQDomDocument ContactList::toXML()
|
|
{
|
|
TQDomDocument doc;
|
|
doc.appendChild( doc.createElement( TQString::fromLatin1("kopete-contact-list") ) );
|
|
doc.documentElement().setAttribute( TQString::fromLatin1("version"), TQString::fromLatin1("1.0"));
|
|
|
|
// Save group information. ie: Open/Closed, pehaps later icons? Who knows.
|
|
for( Kopete::Group *g = d->groups.first(); g; g = d->groups.next() )
|
|
doc.documentElement().appendChild( doc.importNode( g->toXML(), true ) );
|
|
|
|
// Save metacontact information.
|
|
for( Kopete::MetaContact *m = d->contacts.first(); m; m = d->contacts.next() )
|
|
if( !m->isTemporary() )
|
|
doc.documentElement().appendChild( doc.importNode( m->toXML(), true ) );
|
|
|
|
// Save myself metacontact information
|
|
if( Kopete::Config::enableGlobalIdentity() )
|
|
{
|
|
TQDomElement myselfElement = myself()->toXML(true); // Save minimal information.
|
|
myselfElement.setTagName( TQString::fromLatin1("myself-meta-contact") );
|
|
doc.documentElement().appendChild( doc.importNode( myselfElement, true ) );
|
|
}
|
|
|
|
return doc;
|
|
}
|
|
|
|
TQStringList ContactList::contacts() const
|
|
{
|
|
TQStringList contacts;
|
|
TQPtrListIterator<Kopete::MetaContact> it( d->contacts );
|
|
for( ; it.current(); ++it )
|
|
{
|
|
contacts.append( it.current()->displayName() );
|
|
}
|
|
return contacts;
|
|
}
|
|
|
|
TQStringList ContactList::contactStatuses() const
|
|
{
|
|
TQStringList meta_contacts;
|
|
TQPtrListIterator<Kopete::MetaContact> it( d->contacts );
|
|
for( ; it.current(); ++it )
|
|
{
|
|
meta_contacts.append( TQString::fromLatin1( "%1 (%2)" ).
|
|
arg( it.current()->displayName(), it.current()->statusString() ));
|
|
}
|
|
return meta_contacts;
|
|
}
|
|
|
|
TQStringList ContactList::reachableContacts() const
|
|
{
|
|
TQStringList contacts;
|
|
TQPtrListIterator<Kopete::MetaContact> it( d->contacts );
|
|
for( ; it.current(); ++it )
|
|
{
|
|
if ( it.current()->isReachable() )
|
|
contacts.append( it.current()->displayName() );
|
|
}
|
|
return contacts;
|
|
}
|
|
|
|
TQPtrList<Contact> ContactList::onlineContacts() const
|
|
{
|
|
TQPtrList<Kopete::Contact> result;
|
|
TQPtrListIterator<Kopete::MetaContact> it( d->contacts );
|
|
for( ; it.current(); ++it )
|
|
{
|
|
if ( it.current()->isOnline() )
|
|
{
|
|
TQPtrList<Kopete::Contact> contacts = it.current()->contacts();
|
|
TQPtrListIterator<Kopete::Contact> cit( contacts );
|
|
for( ; cit.current(); ++cit )
|
|
{
|
|
if ( cit.current()->isOnline() )
|
|
result.append( cit.current() );
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
TQPtrList<Kopete::MetaContact> Kopete::ContactList::onlineMetaContacts() const
|
|
{
|
|
TQPtrList<Kopete::MetaContact> result;
|
|
TQPtrListIterator<Kopete::MetaContact> it( d->contacts );
|
|
for( ; it.current(); ++it )
|
|
{
|
|
if ( it.current()->isOnline() )
|
|
result.append( it.current() );
|
|
}
|
|
return result;
|
|
}
|
|
|
|
TQPtrList<Kopete::MetaContact> Kopete::ContactList::onlineMetaContacts( const TQString &protocolId ) const
|
|
{
|
|
TQPtrList<Kopete::MetaContact> result;
|
|
TQPtrListIterator<Kopete::MetaContact> it( d->contacts );
|
|
for( ; it.current(); ++it )
|
|
{
|
|
// FIXME: This loop is not very efficient :(
|
|
if ( it.current()->isOnline() )
|
|
{
|
|
TQPtrList<Kopete::Contact> contacts = it.current()->contacts();
|
|
TQPtrListIterator<Kopete::Contact> cit( contacts );
|
|
for( ; cit.current(); ++cit )
|
|
{
|
|
if( cit.current()->isOnline() && cit.current()->protocol()->pluginId() == protocolId )
|
|
result.append( it.current() );
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
TQPtrList<Kopete::Contact> Kopete::ContactList::onlineContacts( const TQString &protocolId ) const
|
|
{
|
|
TQPtrList<Kopete::Contact> result;
|
|
TQPtrListIterator<Kopete::MetaContact> it( d->contacts );
|
|
for( ; it.current(); ++it )
|
|
{
|
|
// FIXME: This loop is not very efficient :(
|
|
if ( it.current()->isOnline() )
|
|
{
|
|
TQPtrList<Kopete::Contact> contacts = it.current()->contacts();
|
|
TQPtrListIterator<Kopete::Contact> cit( contacts );
|
|
for( ; cit.current(); ++cit )
|
|
{
|
|
if( cit.current()->isOnline() && cit.current()->protocol()->pluginId() == protocolId )
|
|
result.append( cit.current() );
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
TQStringList Kopete::ContactList::fileTransferContacts() const
|
|
{
|
|
TQStringList contacts;
|
|
TQPtrListIterator<Kopete::MetaContact> it( d->contacts );
|
|
for( ; it.current(); ++it )
|
|
{
|
|
if ( it.current()->canAcceptFiles() )
|
|
contacts.append( it.current()->displayName() );
|
|
}
|
|
return contacts;
|
|
}
|
|
|
|
void Kopete::ContactList::sendFile( const TQString &displayName, const KURL &sourceURL,
|
|
const TQString &altFileName, const long unsigned int fileSize)
|
|
{
|
|
// kdDebug(14010) << "Send To Display Name: " << displayName << "\n";
|
|
|
|
Kopete::MetaContact *c = findMetaContactByDisplayName( displayName );
|
|
if( c )
|
|
c->sendFile( sourceURL, altFileName, fileSize );
|
|
}
|
|
|
|
void Kopete::ContactList::messageContact( const TQString &contactId, const TQString &messageText )
|
|
{
|
|
Kopete::MetaContact *mc = findMetaContactByContactId( contactId );
|
|
if (!mc) return;
|
|
|
|
Kopete::Contact *c = mc->execute(); //We need to know which contact was chosen as the preferred in order to message it
|
|
if (!c) return;
|
|
|
|
Kopete::Message msg(c->account()->myself(), c, messageText, Kopete::Message::Outbound);
|
|
c->manager(Contact::CanCreate)->sendMessage(msg);
|
|
|
|
}
|
|
|
|
|
|
TQStringList Kopete::ContactList::contactFileProtocols(const TQString &displayName)
|
|
{
|
|
// kdDebug(14010) << "Get contacts for: " << displayName << "\n";
|
|
TQStringList protocols;
|
|
|
|
Kopete::MetaContact *c = findMetaContactByDisplayName( displayName );
|
|
if( c )
|
|
{
|
|
TQPtrList<Kopete::Contact> mContacts = c->contacts();
|
|
kdDebug(14010) << mContacts.count() << endl;
|
|
TQPtrListIterator<Kopete::Contact> jt( mContacts );
|
|
for ( ; jt.current(); ++jt )
|
|
{
|
|
kdDebug(14010) << "1" << jt.current()->protocol()->pluginId() << endl;
|
|
if( jt.current()->canAcceptFiles() ) {
|
|
kdDebug(14010) << jt.current()->protocol()->pluginId() << endl;
|
|
protocols.append ( jt.current()->protocol()->pluginId() );
|
|
}
|
|
}
|
|
return protocols;
|
|
}
|
|
return TQStringList();
|
|
}
|
|
|
|
|
|
void ContactList::slotSaveLater()
|
|
{
|
|
// if we already have a save scheduled, it will be cancelled. either way,
|
|
// start a timer to save the contact list a bit later.
|
|
d->saveTimer->start( 17100 /* 17,1 seconds */, true /* single-shot */ );
|
|
}
|
|
|
|
void ContactList::slotKABCChanged()
|
|
{
|
|
// TODO: react to changes in KABC, replacing this function, post 3.4 (Will)
|
|
// call syncWithKABC on each metacontact to check if its associated tdeabc entry has changed.
|
|
/* for ( MetaContact * mc = d->contacts.first(); mc; mc = d->contacts.next() )
|
|
|
|
mc->syncWithKABC();*/
|
|
}
|
|
|
|
|
|
} //END namespace Kopete
|
|
|
|
#include "kopetecontactlist.moc"
|
|
|
|
// vim: set noet ts=4 sts=4 sw=4:
|
|
|