summaryrefslogtreecommitdiffstats
path: root/kopete/libkopete/kopeteprotocol.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kopete/libkopete/kopeteprotocol.cpp')
-rw-r--r--kopete/libkopete/kopeteprotocol.cpp340
1 files changed, 340 insertions, 0 deletions
diff --git a/kopete/libkopete/kopeteprotocol.cpp b/kopete/libkopete/kopeteprotocol.cpp
new file mode 100644
index 00000000..7854a1a3
--- /dev/null
+++ b/kopete/libkopete/kopeteprotocol.cpp
@@ -0,0 +1,340 @@
+/*
+ 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 <qdict.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 accountNotConnectedStatus;
+};
+
+Protocol::Protocol( KInstance *instance, QObject *parent, const char *name )
+: Plugin( instance, parent, name )
+{
+ d = new Private;
+ d->mStickLastSeen = Global::Properties::self()->lastSeen();
+ d->mStickFullName = Global::Properties::self()->fullName();
+ d->unloading = false;
+ d->capabilities = 0;
+ d->accountNotConnectedStatus = Kopete::OnlineStatus( Kopete::OnlineStatus::Unknown, 0, this, Kopete::OnlineStatus::AccountOffline, QString::fromLatin1( "account_offline_overlay" ), i18n( "Account Offline" ) );
+}
+
+Protocol::~Protocol()
+{
+ // Remove all active accounts
+ QDict<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( QDictIterator<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::accountOfflineStatus() const
+{
+ return d->accountNotConnectedStatus;
+}
+
+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(), SIGNAL(accountDestroyed(const Kopete::Account* )),
+ this, SLOT( slotAccountDestroyed( ) ) );
+
+ self->account()->deleteLater();
+}
+
+void Protocol::slotAccountDestroyed( )
+{
+ QDict<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
+ QDict<Account> accounts = AccountManager::self()->accounts( this );
+
+ if ( accounts.isEmpty() )
+ emit readyForUnload();
+ else for ( QDictIterator<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;
+
+ QObject::connect( it.current()->myself(),
+ SIGNAL( onlineStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ),
+ this, 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;
+
+ QObject::connect( it.current(), SIGNAL( accountDestroyed( const Kopete::Account* ) ),
+ this, SLOT( slotAccountDestroyed( ) ) );
+ it.current()->deleteLater();
+ }
+ }
+}
+
+
+
+void Protocol::slotMetaContactAboutToSave( MetaContact *metaContact )
+{
+ QMap<QString, QString> serializedData, sd;
+ QMap<QString, QString> addressBookData, ad;
+ QMap<QString, QString>::Iterator it;
+
+ //kdDebug( 14010 ) << "Protocol::metaContactAboutToSave: protocol " << pluginId() << ": serializing " << metaContact->displayName() << endl;
+
+ QPtrList<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[ QString::fromLatin1( "contactId" ) ] = c->contactId();
+ //TODO(nick) remove
+ sd[ QString::fromLatin1( "displayName" ) ] = c->property(Global::Properties::self()->nickName()).value().toString();
+ if(c->account())
+ sd[ QString::fromLatin1( "accountId" ) ] = c->account()->accountId();
+
+ // If there's an index field preset it too
+ QString 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.unicode.org/charts/PDF/UE000.pdf.
+ // Inside libkabc the use of QChar( 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() ] + QChar( 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() ] + QChar( 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( QString::fromLatin1( "messaging/" ) ) )
+ {
+ metaContact->setAddressBookField( this, it.key(), QString::fromLatin1( "All" ), it.data() );
+// kdDebug(14010) << k_funcinfo << "metaContact->setAddressBookField( " << this << ", " << it.key() << ", \"All\", " << it.data() << " );" << endl;
+ }
+ else
+ metaContact->setAddressBookField( this, QString::fromLatin1( "kopete" ), it.key(), it.data() );
+ }
+}
+
+void Protocol::deserialize( MetaContact *metaContact, const QMap<QString, QString> &data )
+{
+ /*kdDebug( 14010 ) << "Protocol::deserialize: protocol " <<
+ pluginId() << ": deserializing " << metaContact->displayName() << endl;*/
+
+ QMap<QString, QStringList> serializedData;
+ QMap<QString, QStringList::Iterator> serializedDataIterators;
+ QMap<QString, QString>::ConstIterator it;
+ for( it = data.begin(); it != data.end(); ++it )
+ {
+ serializedData[ it.key() ] = QStringList::split( QChar( 0xE000 ), it.data(), true );
+ serializedDataIterators[ it.key() ] = serializedData[ it.key() ].begin();
+ }
+
+ uint count = serializedData[QString::fromLatin1("contactId")].count();
+
+ // Prepare the independent entries to pass to the plugin's implementation
+ for( uint i = 0; i < count ; i++ )
+ {
+ QMap<QString, QString> sd;
+ QMap<QString, QStringList::Iterator>::Iterator serializedDataIt;
+ for( serializedDataIt = serializedDataIterators.begin(); serializedDataIt != serializedDataIterators.end(); ++serializedDataIt )
+ {
+ sd[ serializedDataIt.key() ] = *( serializedDataIt.data() );
+ ++( serializedDataIt.data() );
+ }
+
+ const QString& accountId=sd[ QString::fromLatin1( "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[ QString::fromLatin1( "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
+ QMap<QString, QString> ad;
+ QStringList kabcFields = addressBookFields();
+ for( QStringList::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( QString::fromLatin1( "messaging/" ) ) )
+ ad[ *fieldIt ] = metaContact->addressBookField( this, *fieldIt, QString::fromLatin1( "All" ) );
+ else
+ ad[ *fieldIt ] = metaContact->addressBookField( this, QString::fromLatin1( "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() )
+ {
+ QDict<Account> accounts = AccountManager::self()->accounts( this );
+ if ( accounts.count() > 0 )
+ {
+ sd[ QString::fromLatin1( "accountId" ) ] = QDictIterator<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 QMap<QString, QString> & /* serializedData */,
+ const QMap<QString, QString> & /* addressBookData */ )
+{
+ /* Default implementation does nothing */
+ return 0;
+}
+
+
+} //END namespace Kopete
+
+#include "kopeteprotocol.moc"
+