diff options
Diffstat (limited to 'interfaces/kimproxy/library/kimproxy.cpp')
-rw-r--r-- | interfaces/kimproxy/library/kimproxy.cpp | 653 |
1 files changed, 653 insertions, 0 deletions
diff --git a/interfaces/kimproxy/library/kimproxy.cpp b/interfaces/kimproxy/library/kimproxy.cpp new file mode 100644 index 000000000..746c6da90 --- /dev/null +++ b/interfaces/kimproxy/library/kimproxy.cpp @@ -0,0 +1,653 @@ +/* + kimproxy.cpp + + IM service library for KDE + + Copyright (c) 2004 Will Stephenson <lists@stevello.free-online.co.uk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qglobal.h> +#include <qpixmapcache.h> +#include <dcopclient.h> +#include <kapplication.h> +#include <kdcopservicestarter.h> +#include <kdebug.h> +#include <kmessagebox.h> +#include <ksimpleconfig.h> +#include <kiconloader.h> +#include <kservice.h> +#include <kservicetype.h> + +#include "kimiface_stub.h" + +#include "kimproxy.h" + +static KStaticDeleter<KIMProxy> _staticDeleter; + +KIMProxy * KIMProxy::s_instance = 0L; + +struct AppPresenceCurrent +{ + QCString appId; + int presence; +}; + +class ContactPresenceListCurrent : public QValueList<AppPresenceCurrent> +{ + public: + // return value indicates if the supplied parameter was better than any existing presence + bool update( const AppPresenceCurrent ); + AppPresenceCurrent best(); +}; + + +struct KIMProxy::Private +{ + DCOPClient * dc; + // list of the strings in use by KIMIface + QStringList presence_strings; + // list of the icon names in use by KIMIface + QStringList presence_icons; + // map of presences + PresenceStringMap presence_map; +}; + +bool ContactPresenceListCurrent::update( AppPresenceCurrent ap ) +{ + if ( isEmpty() ) + { + append( ap ); + return true; + } + + bool bestChanged = false; + AppPresenceCurrent best; + best.presence = -1; + ContactPresenceListCurrent::iterator it = begin(); + const ContactPresenceListCurrent::iterator itEnd = end(); + ContactPresenceListCurrent::iterator existing = itEnd; + + while ( it != itEnd ) + { + if ( (*it).presence > best.presence ) + best = (*it); + if ( (*it).appId == ap.appId ) + existing = it; + ++it; + } + + if ( ap.presence > best.presence || + best.appId == ap.appId ) + bestChanged = true; + + if ( existing != itEnd ) + { + remove( existing ); + append( ap ); + } + return bestChanged; +} + +AppPresenceCurrent ContactPresenceListCurrent::best() +{ + AppPresenceCurrent best; + best.presence = -1; + ContactPresenceListCurrent::iterator it = begin(); + const ContactPresenceListCurrent::iterator itEnd = end(); + while ( it != itEnd ) + { + if ( (*it).presence > best.presence ) + best = (*it); + ++it; + } + // if it's still -1 here, we have no presence data, so we return Unknown + if ( best.presence == -1 ) + best.presence = 0; + return best; +} + +// int bestPresence( AppPresence* ap ) +// { +// Q_ASSERT( ap ); +// AppPresence::const_iterator it; +// it = ap->begin(); +// int best = 0; // unknown +// if ( it != ap->end() ) +// { +// best = it.data(); +// ++it; +// for ( ; it != ap->end(); ++it ) +// { +// if ( it.data() > best ) +// best = it.data(); +// } +// } +// return best; +// } +// +// QCString bestAppId( AppPresence* ap ) +// { +// Q_ASSERT( ap ); +// AppPresence::const_iterator it; +// QCString bestAppId; +// it = ap->begin(); +// if ( it != ap->end() ) +// { +// int best = it.data(); +// bestAppId = it.key(); +// ++it; +// for ( ; it != ap->end(); ++it ) +// { +// if ( it.data() > best ) +// { +// best = it.data(); +// bestAppId = it.key(); +// } +// } +// } +// return bestAppId; +// } + +KIMProxy * KIMProxy::instance( DCOPClient * client ) +{ + if ( client ) + { + if ( !s_instance ) + _staticDeleter.setObject( s_instance, new KIMProxy( client ) ); + return s_instance; + } + else + return 0L; +} + +KIMProxy::KIMProxy( DCOPClient* dc ) : DCOPObject( "KIMProxyIface" ), QObject(), d( new Private ) +{ + m_im_client_stubs.setAutoDelete( true ); + + d->dc = dc; + m_initialized = false; + connect( d->dc, SIGNAL( applicationRemoved( const QCString& ) ) , this, SLOT( unregisteredFromDCOP( const QCString& ) ) ); + connect( d->dc, SIGNAL( applicationRegistered( const QCString& ) ) , this, SLOT( registeredToDCOP( const QCString& ) ) ); + d->dc->setNotifications( true ); + + d->presence_strings.append( "Unknown" ); + d->presence_strings.append( "Offline" ); + d->presence_strings.append( "Connecting" ); + d->presence_strings.append( "Away" ); + d->presence_strings.append( "Online" ); + + d->presence_icons.append( "presence_unknown" ); + d->presence_icons.append( "presence_offline" ); + d->presence_icons.append( "presence_connecting" ); + d->presence_icons.append( "presence_away" ); + d->presence_icons.append( "presence_online" ); + + //QCString senderApp = "Kopete"; + //QCString senderObjectId = "KIMIface"; + QCString method = "contactPresenceChanged( QString, QCString, int )"; + //QCString receiverObjectId = "KIMProxyIface"; + + // FIXME: make this work when the sender object id is set to KIMIFace + if ( !connectDCOPSignal( 0, 0, method, method, false ) ) + KMessageBox::information( 0, QString( "Couldn't connect DCOP signal.\nWon't receive any status notifications!" ) ); +} + +KIMProxy::~KIMProxy( ) +{ + //d->dc->setNotifications( false ); +} + +bool KIMProxy::initialize() +{ + if ( !m_initialized ) + { + m_initialized = true; // we should only do this once, as registeredToDCOP() will catch any new starts + // So there is no error from a failed query when using kdelibs 3.2, which don't have this servicetype + if ( KServiceType::serviceType( IM_SERVICE_TYPE ) ) + { + //kdDebug( 790 ) << k_funcinfo << endl; + QCString dcopObjectId = "KIMIface"; + + // see what apps implementing our service type are out there + KService::List offers = KServiceType::offers( IM_SERVICE_TYPE ); + KService::List::iterator offer; + typedef QValueList<QCString> QCStringList; + QCStringList registeredApps = d->dc->registeredApplications(); + QCStringList::iterator app; + const QCStringList::iterator end = registeredApps.end(); + // for each registered app + for ( app = registeredApps.begin(); app != end; ++app ) + { + //kdDebug( 790 ) << " considering: " << *app << endl; + //for each offer + for ( offer = offers.begin(); offer != offers.end(); ++offer ) + { + QCString dcopService = (*offer)->property("X-DCOP-ServiceName").toString().latin1(); + if ( !dcopService.isEmpty() ) + { + //kdDebug( 790 ) << " is it: " << dcopService << "?" << endl; + // get the application name ( minus any process ID ) + QCString instanceName = (*app).left( dcopService.length() ); + // if the application implements the dcop service, add it + if ( instanceName == dcopService ) + { + m_apps_available = true; + //kdDebug( 790 ) << " app name: " << (*offer)->name() << ", has instance " << *app << ", dcopService: " << dcopService << endl; + if ( !m_im_client_stubs.find( dcopService ) ) + { + kdDebug( 790 ) << "App " << *app << ", dcopObjectId " << dcopObjectId << " found, using it for presence info." << endl; + m_im_client_stubs.insert( *app, new KIMIface_stub( d->dc, *app, dcopObjectId ) ); + pollApp( *app ); + } + } + } + } + } + } + } + return !m_im_client_stubs.isEmpty(); +} + +void KIMProxy::registeredToDCOP( const QCString& appId ) +{ + //kdDebug( 790 ) << k_funcinfo << " appId '" << appId << "'" << endl; + // check that appId implements our service + // if the appId ends with a number, i.e. a pid like in foobar-12345, + if ( appId.isEmpty() ) + return; + + bool newApp = false; + // get an up to date list of offers in case a new app was installed + // and check each of the offers that implement the service type we're looking for, + // to see if any of them are the app that just registered + const KService::List offers = KServiceType::offers( IM_SERVICE_TYPE ); + KService::List::const_iterator it; + for ( it = offers.begin(); it != offers.end(); ++it ) + { + QCString dcopObjectId = "KIMIface"; + QCString dcopService = (*it)->property("X-DCOP-ServiceName").toString().latin1(); + if ( appId.left( dcopService.length() ) == dcopService ) + { + // if it's not already known, insert it + if ( !m_im_client_stubs.find( appId ) ) + { + newApp = true; + kdDebug( 790 ) << "App: " << appId << ", dcopService: " << dcopService << " started, using it for presence info."<< endl; + m_im_client_stubs.insert( appId, new KIMIface_stub( d->dc, appId, dcopObjectId ) ); + } + } + //else + // kdDebug( 790 ) << "App doesn't implement our ServiceType" << endl; + } + //if ( newApp ) + // emit sigPresenceInfoExpired(); +} + +void KIMProxy::unregisteredFromDCOP( const QCString& appId ) +{ + //kdDebug( 790 ) << k_funcinfo << appId << endl; + if ( m_im_client_stubs.find( appId ) ) + { + kdDebug( 790 ) << appId << " quit, removing its presence info." << endl; + + PresenceStringMap::Iterator it = d->presence_map.begin(); + const PresenceStringMap::Iterator end = d->presence_map.end(); + for ( ; it != end; ++it ) + { + ContactPresenceListCurrent list = it.data(); + ContactPresenceListCurrent::iterator cpIt = list.begin(); + while( cpIt != list.end() ) + { + ContactPresenceListCurrent::iterator gone = cpIt++; + if ( (*gone).appId == appId ) + { + list.remove( gone ); + } + } + } + m_im_client_stubs.remove( appId ); + emit sigPresenceInfoExpired(); + } +} + +void KIMProxy::contactPresenceChanged( QString uid, QCString appId, int presence ) +{ + // update the presence map + //kdDebug( 790 ) << k_funcinfo << "uid: " << uid << " appId: " << appId << " presence " << presence << endl; + ContactPresenceListCurrent current; + current = d->presence_map[ uid ]; + //kdDebug( 790 ) << "current best presence from : " << current.best().appId << " is: " << current.best().presence << endl; + AppPresenceCurrent newPresence; + newPresence.appId = appId; + newPresence.presence = presence; + + if ( current.update( newPresence ) ) + { + d->presence_map.insert( uid, current ); + emit sigContactPresenceChanged( uid ); + } +} + +int KIMProxy::presenceNumeric( const QString& uid ) +{ + AppPresenceCurrent ap; + ap.presence = 0; + if ( initialize() ) + { + ContactPresenceListCurrent presence = d->presence_map[ uid ]; + ap = presence.best(); + } + return ap.presence; +} + +QString KIMProxy::presenceString( const QString& uid ) +{ + AppPresenceCurrent ap; + ap.presence = 0; + if ( initialize() ) + { + ContactPresenceListCurrent presence = d->presence_map[ uid ]; + ap = presence.best(); + } + if ( ap.appId.isEmpty() ) + return QString::null; + else + return d->presence_strings[ ap.presence ]; +} + +QPixmap KIMProxy::presenceIcon( const QString& uid ) +{ + AppPresenceCurrent ap; + ap.presence = 0; + if ( initialize() ) + { + ContactPresenceListCurrent presence = d->presence_map[ uid ]; + ap = presence.best(); + } + if ( ap.appId.isEmpty() ) + { + //kdDebug( 790 ) << k_funcinfo << "returning a null QPixmap because we were asked for an icon for a uid we know nothing about" << endl; + return QPixmap(); + } + else + { + //kdDebug( 790 ) << k_funcinfo << "returning this: " << d->presence_icons[ ap.presence ] << endl; + return SmallIcon( d->presence_icons[ ap.presence ]); + } +} + +QStringList KIMProxy::allContacts() +{ + QStringList value = d->presence_map.keys(); + return value; +} + +QStringList KIMProxy::reachableContacts() +{ + QStringList value; + + if ( initialize() ) + { + QDictIterator<KIMIface_stub> it( m_im_client_stubs ); + for ( ; it.current(); ++it ) + { + value += it.current()->reachableContacts( ); + } + } + return value; +} + +QStringList KIMProxy::onlineContacts() +{ + QStringList value; + PresenceStringMap::iterator it = d->presence_map.begin(); + const PresenceStringMap::iterator end= d->presence_map.end(); + for ( ; it != end; ++it ) + if ( it.data().best().presence > 2 /*Better than Connecting, ie Away or Online*/ ) + value.append( it.key() ); + + return value; +} + +QStringList KIMProxy::fileTransferContacts() +{ + QStringList value; + + if ( initialize() ) + { + QDictIterator<KIMIface_stub> it( m_im_client_stubs ); + for ( ; it.current(); ++it ) + { + value += it.current()->fileTransferContacts( ); + } + } + return value; +} + +bool KIMProxy::isPresent( const QString& uid ) +{ + return ( !d->presence_map[ uid ].isEmpty() ); +} + +QString KIMProxy::displayName( const QString& uid ) +{ + QString name; + if ( initialize() ) + { + if ( KIMIface_stub* s = stubForUid( uid ) ) + name = s->displayName( uid ); + } + //kdDebug( 790 ) << k_funcinfo << name << endl; + return name; +} + +bool KIMProxy::canReceiveFiles( const QString & uid ) +{ + if ( initialize() ) + { + if ( KIMIface_stub* s = stubForUid( uid ) ) + return s->canReceiveFiles( uid ); + } + return false; +} + +bool KIMProxy::canRespond( const QString & uid ) +{ + if ( initialize() ) + { + if ( KIMIface_stub* s = stubForUid( uid ) ) + return s->canRespond( uid ); + } + return false; +} + +QString KIMProxy::context( const QString & uid ) +{ + if ( initialize() ) + { + if ( KIMIface_stub* s = stubForUid( uid ) ) + return s->context( uid ); + } + return QString::null; +} + +void KIMProxy::chatWithContact( const QString& uid ) +{ + if ( initialize() ) + { + if ( KIMIface_stub* s = stubForUid( uid ) ) + { + kapp->updateRemoteUserTimestamp( s->app() ); + s->chatWithContact( uid ); + } + } + return; +} + +void KIMProxy::messageContact( const QString& uid, const QString& message ) +{ + if ( initialize() ) + { + if ( KIMIface_stub* s = stubForUid( uid ) ) + { + kapp->updateRemoteUserTimestamp( s->app() ); + s->messageContact( uid, message ); + } + } + return; +} + +void KIMProxy::sendFile(const QString &uid, const KURL &sourceURL, const QString &altFileName, uint fileSize ) +{ + if ( initialize() ) + { + QDictIterator<KIMIface_stub> it( m_im_client_stubs ); + for ( ; it.current(); ++it ) + { + if ( it.current()->canReceiveFiles( uid ) ) + { + kapp->updateRemoteUserTimestamp( it.current()->app() ); + it.current()->sendFile( uid, sourceURL, altFileName, fileSize ); + break; + } + } + } + return; +} + +bool KIMProxy::addContact( const QString &contactId, const QString &protocol ) +{ + if ( initialize() ) + { + if ( KIMIface_stub* s = stubForProtocol( protocol ) ) + return s->addContact( contactId, protocol ); + } + return false; +} + +QString KIMProxy::locate( const QString & contactId, const QString & protocol ) +{ + if ( initialize() ) + { + if ( KIMIface_stub* s = stubForProtocol( protocol ) ) + return s->locate( contactId, protocol ); + } + return QString::null; +} + +bool KIMProxy::imAppsAvailable() +{ + return ( !m_im_client_stubs.isEmpty() ); +} + +bool KIMProxy::startPreferredApp() +{ + QString preferences = QString("[X-DCOP-ServiceName] = '%1'").arg( preferredApp() ); + // start/find an instance of DCOP/InstantMessenger + QString error; + QCString dcopService; + // Get a preferred IM client. + // The app will notify itself to us using registeredToDCOP, so we don't need to record a stub for it here + // FIXME: error in preferences, see debug output + preferences = QString::null; + int result = KDCOPServiceStarter::self()->findServiceFor( IM_SERVICE_TYPE, QString::null, preferences, &error, &dcopService ); + + kdDebug( 790 ) << k_funcinfo << "error was: " << error << ", dcopService: " << dcopService << endl; + + return ( result == 0 ); +} + + +void KIMProxy::pollAll( const QString &uid ) +{ +/* // We only need to call this function if we don't have any data at all + // otherwise, the data will be kept fresh by received presence change + // DCOP signals + if ( !d->presence_map.contains( uid ) ) + { + AppPresence *presence = new AppPresence(); + // record current presence from known clients + QDictIterator<KIMIface_stub> it( m_im_client_stubs ); + for ( ; it.current(); ++it ) + { + presence->insert( it.currentKey().ascii(), it.current()->presenceStatus( uid ) ); // m_im_client_stubs has qstring keys... + } + d->presence_map.insert( uid, presence ); + }*/ +} + +void KIMProxy::pollApp( const QCString & appId ) +{ + //kdDebug( 790 ) << k_funcinfo << endl; + KIMIface_stub * appStub = m_im_client_stubs[ appId ]; + QStringList contacts = m_im_client_stubs[ appId ]->allContacts(); + QStringList::iterator it = contacts.begin(); + QStringList::iterator end = contacts.end(); + for ( ; it != end; ++it ) + { + ContactPresenceListCurrent current = d->presence_map[ *it ]; + AppPresenceCurrent ap; + ap.appId = appId; + ap.presence = appStub->presenceStatus( *it ); + current.append( ap ); + + d->presence_map.insert( *it, current ); + if ( current.update( ap ) ) + emit sigContactPresenceChanged( *it ); + //kdDebug( 790 ) << " uid: " << *it << " presence: " << ap.presence << endl; + } +} + +KIMIface_stub * KIMProxy::stubForUid( const QString &uid ) +{ + // get best appPresence + AppPresenceCurrent ap = d->presence_map[ uid ].best(); + // look up the presence string from that app + return m_im_client_stubs.find( ap.appId ); +} + +KIMIface_stub * KIMProxy::stubForProtocol( const QString &protocol) +{ + KIMIface_stub * app; + // see if the preferred client supports this protocol + QString preferred = preferredApp(); + if ( ( app = m_im_client_stubs.find( preferred ) ) ) + { + if ( app->protocols().grep( protocol ).count() > 0 ) + return app; + } + // preferred doesn't do this protocol, try the first of the others that says it does + QDictIterator<KIMIface_stub> it( m_im_client_stubs ); + for ( ; it.current(); ++it ) + { + if ( it.current()->protocols().grep( protocol ).count() > 0 ) + return it.current(); + } + return 0L; +} + +QString KIMProxy::preferredApp() +{ + KConfig *store = new KSimpleConfig( IM_CLIENT_PREFERENCES_FILE ); + store->setGroup( IM_CLIENT_PREFERENCES_SECTION ); + QString preferredApp = store->readEntry( IM_CLIENT_PREFERENCES_ENTRY ); + //kdDebug( 790 ) << k_funcinfo << "found preferred app: " << preferredApp << endl; + return preferredApp; +} + +#include "kimproxy.moc" |