/* kopeteprotocol.cpp - Kopete Protocol Copyright (c) 2002 by Duncan Mac-Vicar Prett Copyright (c) 2002-2003 by Martijn Klingens Copyright (c) 2002-2004 by Olivier Goffart Kopete (c) 2002-2004 by the Kopete developers ************************************************************************* * * * 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 #include #include #include #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( TDEInstance *instance, TQObject *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, TQString::fromLatin1( "account_offline_overlay" ), i18n( "Account Offline" ) ); } Protocol::~Protocol() { // Remove all active accounts TQDict 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 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(), TQ_SIGNAL(accountDestroyed(const Kopete::Account* )), this, TQ_SLOT( slotAccountDestroyed( ) ) ); self->account()->deleteLater(); } void Protocol::slotAccountDestroyed( ) { TQDict 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 accounts = AccountManager::self()->accounts( this ); if ( accounts.isEmpty() ) emit readyForUnload(); else for ( TQDictIterator 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(), TQ_SIGNAL( onlineStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ), this, TQ_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(), TQ_SIGNAL( accountDestroyed( const Kopete::Account* ) ), this, TQ_SLOT( slotAccountDestroyed( ) ) ); it.current()->deleteLater(); } } } void Protocol::slotMetaContactAboutToSave( MetaContact *metaContact ) { TQMap serializedData, sd; TQMap addressBookData, ad; TQMap::Iterator it; //kdDebug( 14010 ) << "Protocol::metaContactAboutToSave: protocol " << pluginId() << ": serializing " << metaContact->displayName() << endl; TQPtrList 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::fromLatin1( "contactId" ) ] = c->contactId(); //TODO(nick) remove sd[ TQString::fromLatin1( "displayName" ) ] = c->property(Global::Properties::self()->nickName()).value().toString(); if(c->account()) sd[ TQString::fromLatin1( "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.unicode.org/charts/PDF/UE000.pdf. // Inside libtdeabc 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 libtdeabc is working // we can devise a better API, but with the constantly changing // requirements every time I learn more about tdeabc I'd better no touch // the API yet - Martijn if( it.key().startsWith( TQString::fromLatin1( "messaging/" ) ) ) { metaContact->setAddressBookField( this, it.key(), TQString::fromLatin1( "All" ), it.data() ); // kdDebug(14010) << k_funcinfo << "metaContact->setAddressBookField( " << this << ", " << it.key() << ", \"All\", " << it.data() << " );" << endl; } else metaContact->setAddressBookField( this, TQString::fromLatin1( "kopete" ), it.key(), it.data() ); } } void Protocol::deserialize( MetaContact *metaContact, const TQMap &data ) { /*kdDebug( 14010 ) << "Protocol::deserialize: protocol " << pluginId() << ": deserializing " << metaContact->displayName() << endl;*/ TQMap serializedData; TQMap serializedDataIterators; TQMap::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::fromLatin1("contactId")].count(); // Prepare the independent entries to pass to the plugin's implementation for( uint i = 0; i < count ; i++ ) { TQMap sd; TQMap::Iterator serializedDataIt; for( serializedDataIt = serializedDataIterators.begin(); serializedDataIt != serializedDataIterators.end(); ++serializedDataIt ) { sd[ serializedDataIt.key() ] = *( serializedDataIt.data() ); ++( serializedDataIt.data() ); } const TQString& accountId=sd[ TQString::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[ TQString::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 TQMap ad; TQStringList tdeabcFields = addressBookFields(); for( TQStringList::Iterator fieldIt = tdeabcFields.begin(); fieldIt != tdeabcFields.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 tdeabc this hack // should be removed ASAP! - Martijn if( ( *fieldIt ).startsWith( TQString::fromLatin1( "messaging/" ) ) ) ad[ *fieldIt ] = metaContact->addressBookField( this, *fieldIt, TQString::fromLatin1( "All" ) ); else ad[ *fieldIt ] = metaContact->addressBookField( this, TQString::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() ) { TQDict accounts = AccountManager::self()->accounts( this ); if ( accounts.count() > 0 ) { sd[ TQString::fromLatin1( "accountId" ) ] = TQDictIterator( 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 & /* serializedData */, const TQMap & /* addressBookData */ ) { /* Default implementation does nothing */ return 0; } } //END namespace Kopete #include "kopeteprotocol.moc"