|
|
|
/*
|
|
|
|
kopeteprotocol.cpp - Kopete Protocol
|
|
|
|
|
|
|
|
Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
|
|
|
|
Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
|
|
|
|
Copyright (c) 2002-2004 by Olivier Goffart <ogoffart @tiscalinet.be>
|
|
|
|
|
|
|
|
Kopete (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 "kopeteprotocol.h"
|
|
|
|
|
|
|
|
#include <kdebug.h>
|
|
|
|
#include <kaction.h>
|
|
|
|
#include <klocale.h>
|
|
|
|
|
|
|
|
#include <tqdict.h>
|
|
|
|
|
|
|
|
#include "kopeteaccountmanager.h"
|
|
|
|
#include "kopeteaccount.h"
|
|
|
|
#include "kopetecontact.h"
|
|
|
|
#include "kopeteglobal.h"
|
|
|
|
#include "kopetecontactproperty.h"
|
|
|
|
#include "kopetemetacontact.h"
|
|
|
|
|
|
|
|
namespace Kopete
|
|
|
|
{
|
|
|
|
|
|
|
|
class Protocol::Private
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
bool unloading;
|
|
|
|
int capabilities;
|
|
|
|
/*
|
|
|
|
* Make sure we always have a lastSeen and a fullname property as long as
|
|
|
|
* a protocol is loaded
|
|
|
|
*/
|
|
|
|
ContactPropertyTmpl mStickLastSeen;
|
|
|
|
ContactPropertyTmpl mStickFullName;
|
|
|
|
|
|
|
|
Kopete::OnlineStatus accountNotConnectedtqStatus;
|
|
|
|
};
|
|
|
|
|
|
|
|
Protocol::Protocol( KInstance *instance, TQObject *tqparent, const char *name )
|
|
|
|
: Plugin( instance, tqparent, name )
|
|
|
|
{
|
|
|
|
d = new Private;
|
|
|
|
d->mStickLastSeen = Global::Properties::self()->lastSeen();
|
|
|
|
d->mStickFullName = Global::Properties::self()->fullName();
|
|
|
|
d->unloading = false;
|
|
|
|
d->capabilities = 0;
|
|
|
|
d->accountNotConnectedtqStatus = Kopete::OnlineStatus( Kopete::OnlineStatus::Unknown, 0, this, Kopete::OnlineStatus::AccountOffline, TQString::tqfromLatin1( "account_offline_overlay" ), i18n( "Account Offline" ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
Protocol::~Protocol()
|
|
|
|
{
|
|
|
|
// Remove all active accounts
|
|
|
|
TQDict<Account> accounts = AccountManager::self()->accounts( this );
|
|
|
|
if ( !accounts.isEmpty() )
|
|
|
|
{
|
|
|
|
kdWarning( 14010 ) << k_funcinfo << "Deleting protocol with existing accounts! Did the account unloading go wrong?" << endl;
|
|
|
|
|
|
|
|
for( TQDictIterator<Account> it( accounts ); it.current() ; ++it )
|
|
|
|
delete *it;
|
|
|
|
}
|
|
|
|
|
|
|
|
delete d;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int Protocol::capabilities() const
|
|
|
|
{
|
|
|
|
return d->capabilities;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Protocol::setCapabilities( unsigned int capabilities )
|
|
|
|
{
|
|
|
|
d->capabilities = capabilities;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Kopete::OnlineStatus Protocol::accountOfflinetqStatus() const
|
|
|
|
{
|
|
|
|
return d->accountNotConnectedtqStatus;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Protocol::slotAccountOnlineStatusChanged( Contact *self )
|
|
|
|
{//slot connected in aboutToUnload
|
|
|
|
if ( !self || !self->account() || self->account()->isConnected())
|
|
|
|
return;
|
|
|
|
// some protocols change status several times during shutdown. We should only call deleteLater() once
|
|
|
|
disconnect( self, 0, this, 0 );
|
|
|
|
|
|
|
|
connect( self->account(), TQT_SIGNAL(accountDestroyed(const Kopete::Account* )),
|
|
|
|
this, TQT_SLOT( slotAccountDestroyed( ) ) );
|
|
|
|
|
|
|
|
self->account()->deleteLater();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Protocol::slotAccountDestroyed( )
|
|
|
|
{
|
|
|
|
TQDict<Account> dict = AccountManager::self()->accounts( this );
|
|
|
|
if ( dict.isEmpty() )
|
|
|
|
{
|
|
|
|
// While at this point we are still in a stack trace from the destroyed
|
|
|
|
// account it's safe to emit readyForUnload already, because it uses a
|
|
|
|
// deleteLater rather than a delete for exactly this reason, to keep the
|
|
|
|
// API managable
|
|
|
|
emit( readyForUnload() );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Protocol::aboutToUnload()
|
|
|
|
{
|
|
|
|
|
|
|
|
d->unloading = true;
|
|
|
|
|
|
|
|
// Disconnect all accounts
|
|
|
|
TQDict<Account> accounts = AccountManager::self()->accounts( this );
|
|
|
|
|
|
|
|
if ( accounts.isEmpty() )
|
|
|
|
emit readyForUnload();
|
|
|
|
else for ( TQDictIterator<Account> it( accounts ); it.current() ; ++it )
|
|
|
|
{
|
|
|
|
if ( it.current()->myself() && it.current()->myself()->isOnline() )
|
|
|
|
{
|
|
|
|
kdDebug( 14010 ) << k_funcinfo << it.current()->accountId() <<
|
|
|
|
" is still connected, disconnecting..." << endl;
|
|
|
|
|
|
|
|
TQObject::connect( it.current()->myself(),
|
|
|
|
TQT_SIGNAL( onlineStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ),
|
|
|
|
this, TQT_SLOT( slotAccountOnlineStatusChanged( Kopete::Contact * ) ) );
|
|
|
|
it.current()->disconnect();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Remove account, it's already disconnected
|
|
|
|
kdDebug( 14010 ) << k_funcinfo << it.current()->accountId() <<
|
|
|
|
" is already disconnected, deleting..." << endl;
|
|
|
|
|
|
|
|
TQObject::connect( it.current(), TQT_SIGNAL( accountDestroyed( const Kopete::Account* ) ),
|
|
|
|
this, TQT_SLOT( slotAccountDestroyed( ) ) );
|
|
|
|
it.current()->deleteLater();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void Protocol::slotMetaContactAboutToSave( MetaContact *metaContact )
|
|
|
|
{
|
|
|
|
TQMap<TQString, TQString> serializedData, sd;
|
|
|
|
TQMap<TQString, TQString> addressBookData, ad;
|
|
|
|
TQMap<TQString, TQString>::Iterator it;
|
|
|
|
|
|
|
|
//kdDebug( 14010 ) << "Protocol::metaContactAboutToSave: protocol " << pluginId() << ": serializing " << metaContact->displayName() << endl;
|
|
|
|
|
|
|
|
TQPtrList<Contact> contacts=metaContact->contacts();
|
|
|
|
for (Contact *c=contacts.first() ; c ; c=contacts.next() )
|
|
|
|
{
|
|
|
|
if( c->protocol()->pluginId() != pluginId() )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
sd.clear();
|
|
|
|
ad.clear();
|
|
|
|
|
|
|
|
// Preset the contactId and displayName, if the plugin doesn't want to save
|
|
|
|
// them, or use its own format, it can call clear() on the provided list
|
|
|
|
sd[ TQString::tqfromLatin1( "contactId" ) ] = c->contactId();
|
|
|
|
//TODO(nick) remove
|
|
|
|
sd[ TQString::tqfromLatin1( "displayName" ) ] = c->property(Global::Properties::self()->nickName()).value().toString();
|
|
|
|
if(c->account())
|
|
|
|
sd[ TQString::tqfromLatin1( "accountId" ) ] = c->account()->accountId();
|
|
|
|
|
|
|
|
// If there's an index field preset it too
|
|
|
|
TQString index = c->protocol()->addressBookIndexField();
|
|
|
|
if( !index.isEmpty() )
|
|
|
|
ad[ index ] = c->contactId();
|
|
|
|
|
|
|
|
c->serializeProperties( sd );
|
|
|
|
c->serialize( sd, ad );
|
|
|
|
|
|
|
|
// Merge the returned fields with what we already (may) have
|
|
|
|
for( it = sd.begin(); it != sd.end(); ++it )
|
|
|
|
{
|
|
|
|
// The Unicode chars E000-F800 are non-printable and reserved for
|
|
|
|
// private use in applications. For more details, see also
|
|
|
|
// http://www.tqunicode.org/charts/PDF/UE000.pdf.
|
|
|
|
// Inside libkabc the use of TQChar( 0xE000 ) has been standardized
|
|
|
|
// as separator for the string lists, use this also for the 'normal'
|
|
|
|
// serialized data.
|
|
|
|
if( serializedData.contains( it.key() ) )
|
|
|
|
serializedData[ it.key() ] = serializedData[ it.key() ] + TQChar( 0xE000 ) + it.data();
|
|
|
|
else
|
|
|
|
serializedData[ it.key() ] = it.data();
|
|
|
|
}
|
|
|
|
|
|
|
|
for( it = ad.begin(); it != ad.end(); ++it )
|
|
|
|
{
|
|
|
|
if( addressBookData.contains( it.key() ) )
|
|
|
|
addressBookData[ it.key() ] = addressBookData[ it.key() ] + TQChar( 0xE000 ) + it.data();
|
|
|
|
else
|
|
|
|
addressBookData[ it.key() ] = it.data();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Pass all returned fields to the contact list
|
|
|
|
//if( !serializedData.isEmpty() ) //even if we are empty, that mean there are no contact, so remove old value
|
|
|
|
metaContact->setPluginData( this, serializedData );
|
|
|
|
|
|
|
|
for( it = addressBookData.begin(); it != addressBookData.end(); ++it )
|
|
|
|
{
|
|
|
|
//kdDebug( 14010 ) << "Protocol::metaContactAboutToSave: addressBookData: key: " << it.key() << ", data: " << it.data() << endl;
|
|
|
|
// FIXME: This is a terrible hack to check the key name for the phrase "messaging/"
|
|
|
|
// to indicate what app name to use, but for now it's by far the easiest
|
|
|
|
// way to get this working.
|
|
|
|
// Once all this is in CVS and the actual storage in libkabc is working
|
|
|
|
// we can devise a better API, but with the constantly changing
|
|
|
|
// requirements every time I learn more about kabc I'd better no touch
|
|
|
|
// the API yet - Martijn
|
|
|
|
if( it.key().startsWith( TQString::tqfromLatin1( "messaging/" ) ) )
|
|
|
|
{
|
|
|
|
metaContact->setAddressBookField( this, it.key(), TQString::tqfromLatin1( "All" ), it.data() );
|
|
|
|
// kdDebug(14010) << k_funcinfo << "metaContact->setAddressBookField( " << this << ", " << it.key() << ", \"All\", " << it.data() << " );" << endl;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
metaContact->setAddressBookField( this, TQString::tqfromLatin1( "kopete" ), it.key(), it.data() );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Protocol::deserialize( MetaContact *metaContact, const TQMap<TQString, TQString> &data )
|
|
|
|
{
|
|
|
|
/*kdDebug( 14010 ) << "Protocol::deserialize: protocol " <<
|
|
|
|
pluginId() << ": deserializing " << metaContact->displayName() << endl;*/
|
|
|
|
|
|
|
|
TQMap<TQString, TQStringList> serializedData;
|
|
|
|
TQMap<TQString, TQStringList::Iterator> serializedDataIterators;
|
|
|
|
TQMap<TQString, TQString>::ConstIterator it;
|
|
|
|
for( it = data.begin(); it != data.end(); ++it )
|
|
|
|
{
|
|
|
|
serializedData[ it.key() ] = TQStringList::split( TQChar( 0xE000 ), it.data(), true );
|
|
|
|
serializedDataIterators[ it.key() ] = serializedData[ it.key() ].begin();
|
|
|
|
}
|
|
|
|
|
|
|
|
uint count = serializedData[TQString::tqfromLatin1("contactId")].count();
|
|
|
|
|
|
|
|
// Prepare the independent entries to pass to the plugin's implementation
|
|
|
|
for( uint i = 0; i < count ; i++ )
|
|
|
|
{
|
|
|
|
TQMap<TQString, TQString> sd;
|
|
|
|
TQMap<TQString, TQStringList::Iterator>::Iterator serializedDataIt;
|
|
|
|
for( serializedDataIt = serializedDataIterators.begin(); serializedDataIt != serializedDataIterators.end(); ++serializedDataIt )
|
|
|
|
{
|
|
|
|
sd[ serializedDataIt.key() ] = *( serializedDataIt.data() );
|
|
|
|
++( serializedDataIt.data() );
|
|
|
|
}
|
|
|
|
|
|
|
|
const TQString& accountId=sd[ TQString::tqfromLatin1( "accountId" ) ];
|
|
|
|
// myself was allowed in the contactlist in old version of kopete.
|
|
|
|
// But if one keep it on the contactlist now, it may conflict witht he myself metacontact.
|
|
|
|
// So ignore it
|
|
|
|
if(accountId == sd[ TQString::tqfromLatin1( "contactId" ) ] )
|
|
|
|
{
|
|
|
|
kdDebug( 14010 ) << k_funcinfo << "Myself contact was on the contactlist.xml for account " << accountId << ". Ignore it" << endl;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME: This code almost certainly breaks when having more than
|
|
|
|
// one contact in a meta contact. There are solutions, but
|
|
|
|
// they are all hacky and the API needs revision anyway (see
|
|
|
|
// FIXME a few lines below :), so I'm not going to add that
|
|
|
|
// for now.
|
|
|
|
// Note that even though it breaks, the current code will
|
|
|
|
// never notice, since none of the plugins use the address
|
|
|
|
// book data in the deserializer yet, only when serializing.
|
|
|
|
// - Martijn
|
|
|
|
TQMap<TQString, TQString> ad;
|
|
|
|
TQStringList kabcFields = addressBookFields();
|
|
|
|
for( TQStringList::Iterator fieldIt = kabcFields.begin(); fieldIt != kabcFields.end(); ++fieldIt )
|
|
|
|
{
|
|
|
|
// FIXME: This hack is even more ugly, and has the same reasons as the similar
|
|
|
|
// hack in the serialize code.
|
|
|
|
// Once this code is actually capable of talking to kabc this hack
|
|
|
|
// should be removed ASAP! - Martijn
|
|
|
|
if( ( *fieldIt ).startsWith( TQString::tqfromLatin1( "messaging/" ) ) )
|
|
|
|
ad[ *fieldIt ] = metaContact->addressBookField( this, *fieldIt, TQString::tqfromLatin1( "All" ) );
|
|
|
|
else
|
|
|
|
ad[ *fieldIt ] = metaContact->addressBookField( this, TQString::tqfromLatin1( "kopete" ), *fieldIt );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if we have an account id. If not we're deserializing a Kopete 0.6 contact
|
|
|
|
// (our our config is corrupted). Pick the first available account there. This
|
|
|
|
// might not be what you want for corrupted accounts, but it's correct for people
|
|
|
|
// who migrate from 0.6, as there's only one account in that case
|
|
|
|
if( accountId.isNull() )
|
|
|
|
{
|
|
|
|
TQDict<Account> accounts = AccountManager::self()->accounts( this );
|
|
|
|
if ( accounts.count() > 0 )
|
|
|
|
{
|
|
|
|
sd[ TQString::tqfromLatin1( "accountId" ) ] = TQDictIterator<Account>( accounts ).currentKey();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
kdWarning( 14010 ) << k_funcinfo <<
|
|
|
|
"No account available and account not set in " \
|
|
|
|
"contactlist.xml either!" << endl
|
|
|
|
<< "Not deserializing this contact." << endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Contact *c = deserializeContact( metaContact, sd, ad );
|
|
|
|
if (c) // should never be null but I do not like crashes
|
|
|
|
c->deserializeProperties( sd );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Contact *Protocol::deserializeContact(
|
|
|
|
MetaContact */*metaContact */,
|
|
|
|
const TQMap<TQString, TQString> & /* serializedData */,
|
|
|
|
const TQMap<TQString, TQString> & /* addressBookData */ )
|
|
|
|
{
|
|
|
|
/* Default implementation does nothing */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} //END namespace Kopete
|
|
|
|
|
|
|
|
#include "kopeteprotocol.moc"
|
|
|
|
|