interfaces Library API Documentation

kimproxy.cpp

00001 /*    
00002     kimproxy.cpp
00003     
00004     IM service library for KDE
00005     
00006     Copyright (c) 2004 Will Stephenson   <lists@stevello.free-online.co.uk>
00007 
00008     This library is free software; you can redistribute it and/or
00009     modify it under the terms of the GNU Library General Public
00010     License as published by the Free Software Foundation; either
00011     version 2 of the License, or (at your option) any later version.
00012 
00013     This library is distributed in the hope that it will be useful,
00014     but WITHOUT ANY WARRANTY; without even the implied warranty of
00015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016     Library General Public License for more details.
00017 
00018     You should have received a copy of the GNU Library General Public License
00019     along with this library; see the file COPYING.LIB.  If not, write to
00020     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00021     Boston, MA 02111-1307, USA.
00022 */
00023 
00024 #include <qglobal.h>
00025 #include <qpixmapcache.h>
00026 #include <dcopclient.h>
00027 #include <kdcopservicestarter.h> 
00028 #include <kdebug.h>
00029 #include <kmessagebox.h>
00030 #include <ksimpleconfig.h>
00031 #include <kiconloader.h>
00032 #include <kservice.h>
00033 #include <kservicetype.h>
00034 
00035 #include "kimiface_stub.h"
00036 
00037 #include "kimproxy.h"
00038 
00039 static KStaticDeleter<KIMProxy> _staticDeleter;
00040 
00041 KIMProxy * KIMProxy::s_instance = 0L;
00042 
00043 int bestPresence( AppPresence* ap )
00044 {
00045     Q_ASSERT( ap );
00046     AppPresence::const_iterator it;
00047     it = ap->begin();
00048     int best = 0; // unknown
00049     if ( it != ap->end() )
00050     {
00051         best = it.data();
00052         ++it;
00053         for ( ; it != ap->end(); ++it )
00054         {
00055             if ( it.data() > best )
00056                 best = it.data(); 
00057         }
00058     }
00059     return best;
00060 }
00061 
00062 QCString bestAppId( AppPresence* ap )
00063 {
00064     Q_ASSERT( ap );
00065     AppPresence::const_iterator it;
00066     QCString bestAppId;
00067     it = ap->begin();
00068     if ( it != ap->end() )
00069     {
00070         int best = it.data();
00071         bestAppId = it.key();
00072         ++it;
00073         for ( ; it != ap->end(); ++it )
00074         {
00075             if ( it.data() > best )
00076             {
00077                 best = it.data();
00078                 bestAppId = it.key();
00079             }
00080         }
00081     }
00082     return bestAppId;
00083 }
00084 
00085 KIMProxy * KIMProxy::instance( DCOPClient * client ) 
00086 {
00087     if ( client )
00088     {
00089         if ( !s_instance )
00090             _staticDeleter.setObject( s_instance, new KIMProxy( client ) );
00091         return s_instance;
00092     }
00093     else
00094         return 0L;
00095 }
00096 
00097 KIMProxy::KIMProxy( DCOPClient* dc ) : DCOPObject( "KIMProxyIface" ), QObject()
00098 {
00099     m_im_client_stubs.setAutoDelete( true );
00100     m_presence_map.setAutoDelete( true );
00101 
00102     m_apps_available = false;
00103     m_dc = dc;
00104     m_initialized = false;
00105     connect( m_dc, SIGNAL( applicationRemoved( const QCString& ) ) , this, SLOT( unregisteredFromDCOP( const QCString& ) ) );
00106     connect( m_dc, SIGNAL( applicationRegistered( const QCString& ) ) , this, SLOT( registeredToDCOP( const QCString& ) ) );
00107     m_dc->setNotifications( true );
00108 
00109     //QCString senderApp = "Kopete";
00110     //QCString senderObjectId = "KIMIface";
00111     QCString method = "contactPresenceChanged( QString, QCString, int )";
00112     //QCString receiverObjectId = "KIMProxyIface";
00113     
00114     // FIXME: make this work when the sender object id is set to KIMIFace
00115     if ( !connectDCOPSignal( 0, 0, method, method, false ) )
00116         KMessageBox::information( 0, QString( "Couldn't connect DCOP signal.\nWon't receive any status notifications!" ) );
00117 }
00118 
00119 KIMProxy::~KIMProxy( )
00120 {
00121     //m_dc->setNotifications( false );
00122 }
00123 
00124 bool KIMProxy::initialize()
00125 {
00126     if ( !m_initialized )
00127     {
00128         m_initialized = true; // we should only do this once, as registeredToDCOP() will catch any new starts
00129         // So there is no error from a failed query when using kdelibs 3.2, which don't have this servicetype
00130         if ( KServiceType::serviceType( IM_SERVICE_TYPE ) ) 
00131         {
00132             kdDebug( 5301 ) << k_funcinfo << endl;
00133             QCString dcopObjectId = "KIMIface";
00134     
00135             // see what apps implementing our service type are out there
00136             KService::List offers = KServiceType::offers( IM_SERVICE_TYPE );
00137             KService::List::iterator offer;
00138             typedef QValueList<QCString> QCStringList;
00139             QCStringList registeredApps = m_dc->registeredApplications();
00140             QCStringList::iterator app;
00141             const QCStringList::iterator end = registeredApps.end();
00142             // for each registered app
00143             for ( app = registeredApps.begin(); app != end; ++app )
00144             {
00145                 //kdDebug( 5301 ) << " considering: " << *app << endl;
00146                 //for each offer
00147                 for ( offer = offers.begin(); offer != offers.end(); ++offer )
00148                 {
00149                     QCString dcopService = (*offer)->property("X-DCOP-ServiceName").toString().latin1();
00150                     if ( !dcopService.isEmpty() )
00151                     {
00152                         //kdDebug( 5301 ) << " is it: " << dcopService << "?" << endl;
00153                         // get the application name ( minus any process ID )
00154                         QCString instanceName =  (*app).left( dcopService.length() );
00155                         // if the application implements the dcop service, add it 
00156                         if ( instanceName == dcopService )
00157                         {
00158                             m_apps_available = true;
00159                             //kdDebug( 5301 ) << " app name: " << (*offer)->name() << ", has instance " << *app << ", dcopService: " << dcopService << endl;
00160                             if ( !m_im_client_stubs.find( dcopService ) )
00161                             {
00162                                 kdDebug( 5301 ) << "inserting new stub for " << *app << " dcopObjectId " << dcopObjectId << endl;
00163                                 m_im_client_stubs.insert( *app, new KIMIface_stub( m_dc, *app, dcopObjectId ) );
00164                             }
00165                         }
00166                     }
00167                 }
00168             }
00169         }
00170     }
00171     return !m_im_client_stubs.isEmpty();
00172 }
00173 
00174 QStringList KIMProxy::allContacts()
00175 {
00176     QStringList value;
00177     
00178     if ( initialize() )
00179     {
00180         QDictIterator<KIMIface_stub> it( m_im_client_stubs );
00181         for ( ; it.current(); ++it )
00182         {
00183             value += it.current()->allContacts();
00184         }
00185     }
00186     return value;
00187 }
00188 
00189 QStringList KIMProxy::reachableContacts()
00190 {
00191     QStringList value;
00192     
00193     if ( initialize() )
00194     {
00195         QDictIterator<KIMIface_stub> it( m_im_client_stubs );
00196         for ( ; it.current(); ++it )
00197         {
00198             value += it.current()->reachableContacts( );
00199         }
00200     }
00201     return value;
00202 }
00203 
00204 QStringList KIMProxy::onlineContacts()
00205 {
00206     QStringList value;
00207     
00208     if ( initialize() )
00209     {
00210         QDictIterator<KIMIface_stub> it( m_im_client_stubs );
00211         for ( ; it.current(); ++it )
00212         {
00213             value += it.current()->onlineContacts( );
00214         }
00215     }
00216     return value;
00217 }
00218 
00219 QStringList KIMProxy::fileTransferContacts()
00220 {
00221     QStringList value;
00222     
00223     if ( initialize() )
00224     {
00225         QDictIterator<KIMIface_stub> it( m_im_client_stubs );
00226         for ( ; it.current(); ++it )
00227         {
00228             value += it.current()->fileTransferContacts( );
00229         }
00230     }
00231     return value;
00232 }
00233 
00234 bool KIMProxy::isPresent( const QString& uid )
00235 {
00236     bool present = false;
00237     if ( initialize() )
00238     {
00239         QDictIterator<KIMIface_stub> it( m_im_client_stubs );
00240         for ( ; it.current(); ++it )
00241         {
00242             if (it.current()->isPresent( uid ) )
00243             {
00244                 present = true;
00245                 break;
00246             }
00247         }
00248     }
00249     return present;
00250 }
00251 
00252 QString KIMProxy::displayName( const QString& uid )
00253 {
00254     QString name;
00255     if ( initialize() )
00256     {
00257         if ( KIMIface_stub* s = stubForUid( uid ) )
00258             name = s->displayName( uid );
00259     }
00260     //kdDebug( 5301 ) << k_funcinfo << name << endl;
00261     return name;
00262 }
00263 
00264 int KIMProxy::presenceNumeric( const QString& uid )
00265 {
00266     int presence = -1;
00267     if ( initialize() )
00268     {
00269         pollAll( uid );
00270         AppPresence *ap = m_presence_map.find( uid );
00271         if ( ap ) // find returns 0 on not found.
00272             presence = bestPresence( ap );
00273     }
00274     return presence;
00275 }
00276 
00277 QString KIMProxy::presenceString( const QString& uid )
00278 {
00279     //kdDebug( 5301 ) << k_funcinfo << endl;
00280     
00281     QString presence;
00282     KIMIface_stub* s = stubForUid( uid );
00283     if ( initialize() && s )
00284     {
00285         
00286         PresenceStringMap * appPresenceStrings = m_client_presence_strings[ s ];
00287         if ( !appPresenceStrings ) // we have no presence strings mapped at all for this stub
00288         {
00289             //kdDebug( 5301 ) << " no string cache found for this stub , creating one" << endl;
00290             appPresenceStrings = new PresenceStringMap();
00291             m_client_presence_strings.insert( s, appPresenceStrings );
00292         }
00293         int numeric = presenceNumeric( uid );
00294         presence = (*appPresenceStrings)[ numeric ];
00295         if ( presence.isEmpty() ) //  cache miss
00296         {
00297             //kdDebug( 5301 ) << " no cached string found for this app, fetching it" << endl;
00298             presence = s->presenceString( uid );
00299             appPresenceStrings->insert( numeric, presence );
00300         }
00301         //kdDebug( 5301 ) << " resulting presence string for " << uid << " : " << presence << endl;
00302     }
00303     return presence;
00304 }
00305 
00306 QPixmap KIMProxy::presenceIcon( const QString& uid )
00307 {
00308     QPixmap presence;
00309     if ( initialize() )
00310     {
00311         // get the app id 
00312         pollAll( uid );
00313         AppPresence *existing = m_presence_map.find( uid );
00314         // try the pixmap cache, and insert if not found
00315         QCString ba = bestAppId( existing );
00316         if ( !ba.isNull() )
00317         {
00318             QString appPresenceKey = QString::number( presenceNumeric( uid ) ).append( ba );
00319             if ( !QPixmapCache::find( appPresenceKey, presence ) )
00320             {
00321                 if ( KIMIface_stub* s = stubForUid( uid ) )
00322                 {
00323                     presence = s->icon( uid );
00324                     QPixmapCache::insert( appPresenceKey, presence );
00325                 }
00326             }
00327         }
00328     }
00329     return presence;
00330 }
00331 
00332 bool KIMProxy::canReceiveFiles( const QString & uid )
00333 {
00334     if ( initialize() )
00335     {
00336         if ( KIMIface_stub* s = stubForUid( uid ) )
00337             return s->canReceiveFiles( uid );
00338     }
00339     return false;
00340 }
00341 
00342 bool KIMProxy::canRespond( const QString & uid )
00343 {
00344     if ( initialize() )
00345     {
00346         if ( KIMIface_stub* s = stubForUid( uid ) )
00347             return s->canRespond( uid );
00348     }
00349     return false;
00350 }
00351 
00352 QString KIMProxy::context( const QString & uid )
00353 {
00354     if ( initialize() )
00355     {
00356         if ( KIMIface_stub* s = stubForUid( uid ) )
00357             return s->context( uid );
00358     }
00359     return QString::null;
00360 }
00361     
00362 void KIMProxy::chatWithContact( const QString& uid )
00363 {
00364     if ( initialize() )
00365     {
00366         if ( KIMIface_stub* s = stubForUid( uid ) )
00367             s->chatWithContact( uid );
00368     }
00369     return;
00370 }
00371 
00372 void KIMProxy::messageContact( const QString& uid, const QString& message )
00373 {
00374     if ( initialize() )
00375     {
00376         if ( KIMIface_stub* s = stubForUid( uid ) )
00377             s->messageContact( uid, message );
00378     }
00379     return;
00380 }
00381 
00382 void KIMProxy::sendFile(const QString &uid, const KURL &sourceURL, const QString &altFileName, uint fileSize )
00383 {
00384     if ( initialize() )
00385     {
00386         QDictIterator<KIMIface_stub> it( m_im_client_stubs );
00387         for ( ; it.current(); ++it )
00388         {
00389             if ( it.current()->canReceiveFiles( uid ) )
00390             {
00391                 it.current()->sendFile( uid, sourceURL, altFileName, fileSize );
00392                 break;
00393             }
00394         }   
00395     }
00396     return;
00397 }
00398 
00399 bool KIMProxy::addContact( const QString &contactId, const QString &protocol )
00400 {
00401     if ( initialize() )
00402     {
00403         if ( KIMIface_stub* s = stubForProtocol( protocol ) )
00404         return s->addContact( contactId, protocol );
00405     }
00406     return false;
00407 }
00408 
00409 QString KIMProxy::locate( const QString & contactId, const QString & protocol )
00410 {
00411     if ( initialize() )
00412     {
00413         if ( KIMIface_stub* s = stubForProtocol( protocol ) )
00414         return s->locate( contactId, protocol );
00415     }
00416     return QString::null;
00417 }
00418 
00419 bool KIMProxy::imAppsAvailable()
00420 {
00421     kdDebug( 5301 ) << k_funcinfo << " returning " << m_apps_available<< endl;
00422     return m_apps_available;
00423 }
00424 
00425 bool KIMProxy::startPreferredApp()
00426 {
00427     QString preferences = QString("[X-DCOP-ServiceName] = '%1'").arg( preferredApp() );
00428     // start/find an instance of DCOP/InstantMessenger
00429     QString error;
00430     QCString dcopService;
00431     // Get a preferred IM client.
00432     // The app will notify itself to us using registeredToDCOP, so we don't need to record a stub for it here
00433     // FIXME: error in preferences, see debug output
00434     preferences = QString::null;
00435     int result = KDCOPServiceStarter::self()->findServiceFor( IM_SERVICE_TYPE, QString::null, preferences, &error, &dcopService );
00436 
00437     kdDebug( 5301 ) << k_funcinfo << "error was: " << error << ", dcopService: " << dcopService << endl;
00438 
00439     return ( result == 0 );
00440 }
00441 
00442 void KIMProxy::unregisteredFromDCOP( const QCString& appId )
00443 {
00444     kdDebug( 5301 ) << k_funcinfo << appId << endl;
00445     if ( m_im_client_stubs.find( appId ) )
00446     {
00447         kdDebug( 5301 ) << "removing references to " << appId << endl;
00448         // invalidate all
00449         QDictIterator<AppPresence> it( m_presence_map ); 
00450         for ( ; it.current(); ++it )
00451         {
00452             AppPresence::iterator apIt;
00453             for ( apIt = it.current()->begin(); apIt != it.current()->end(); ++apIt )
00454                 if ( apIt.key() == appId )
00455                 {
00456                     it.current()->remove( apIt );
00457                 }
00458         }
00459         m_im_client_stubs.remove( appId );
00460         emit sigPresenceInfoExpired();
00461     }
00462 }
00463 
00464 void KIMProxy::registeredToDCOP( const QCString& appId )
00465 {
00466     // check that appId implements our service
00467     // if the appId ends with a number, i.e. a pid like in foobar-12345,
00468     // there's no chance - why did i make this decision
00469     if ( appId.isEmpty() ) /*|| QChar( appId[ appId.length() - 1 ] ).isDigit() ) */
00470         return;
00471     // So there is no error from a failed query when using kdelibs 3.2, which don't have this servicetype
00472     if ( !KServiceType::serviceType( IM_SERVICE_TYPE ) ) 
00473         return;
00474 
00475     kdDebug( 5301 ) << k_funcinfo << appId << endl;
00476     bool newApp = false;
00477     // get an up to date list of offers in case a new app was installed
00478     const KService::List offers = KServiceType::offers( IM_SERVICE_TYPE );
00479     KService::List::const_iterator it;
00480     // check each of the offers that implement the service type we're lookin for,
00481     // to see if any of them are the app that just registered
00482     for ( it = offers.begin(); it != offers.end(); ++it )
00483     {
00484         QCString dcopObjectId = "KIMIface";
00485         QCString dcopService = (*it)->property("X-DCOP-ServiceName").toString().latin1();
00486         kdDebug( 5301 ) << "dcopService: " << dcopService << ", appId: " << appId << endl;
00487         if ( appId.left( dcopService.length() ) == dcopService )
00488         {
00489             m_apps_available = true;
00490             // if it's not already known, insert it
00491             if ( !m_im_client_stubs.find( appId ) )
00492             {   
00493                 newApp = true;
00494                 m_im_client_stubs.insert( appId, new KIMIface_stub( m_dc, appId, dcopObjectId ) );
00495             }
00496         }
00497         //else
00498         //  kdDebug( 5301 ) << "App doesn't implement our ServiceType" << endl;
00499     }
00500     if ( newApp )
00501         emit sigPresenceInfoExpired();
00502 }
00503 
00504 void KIMProxy::contactPresenceChanged( QString uid, QCString appId, int presence )
00505 {
00506     // update the presence map
00507     kdDebug( 5301 ) << k_funcinfo << "uid: " << uid << " appId: " << appId << " presence " << presence << endl;
00508     if ( updatePresence( uid, appId, presence ) )
00509         emit sigContactPresenceChanged( uid );
00510 }
00511 
00512 bool KIMProxy::updatePresence( const QString &uid, const QCString &appId, int presence )
00513 {
00514     // if exists    
00515     AppPresence *userPresences = 0L;
00516     if ( ( userPresences = m_presence_map.find( uid ) ) )
00517     {
00518         // get the old presence
00519         int oldBest = bestPresence( userPresences );
00520         QCString oldBestApp = bestAppId( userPresences );
00521         // update the presence
00522         userPresences->insert( appId, presence );
00523         int newBest =  bestPresence( userPresences );
00524         // return if the update is better than the old presence
00525         return ( newBest > oldBest || appId == oldBestApp );
00526     }
00527     else
00528     {
00529         // else insert
00530         userPresences = new AppPresence();
00531         userPresences->insert( appId, presence );
00532         m_presence_map.insert( uid, userPresences );
00533         return true;
00534     }
00535     return false;
00536 }
00537 
00538 void KIMProxy::pollAll( const QString &uid )
00539 {
00540     // We only need to call this function if we don't have any data at all
00541     // otherwise, the data will be kept fresh by received presence change
00542     // DCOP signals
00543     if ( !m_presence_map.find( uid ) )
00544     {
00545         AppPresence *presence = new AppPresence();
00546         // record current presence from known clients
00547         QDictIterator<KIMIface_stub> it( m_im_client_stubs );
00548         for ( ; it.current(); ++it )
00549         {
00550             presence->insert( it.currentKey().ascii(), it.current()->presenceStatus( uid ) ); // m_im_client_stubs has qstring keys...
00551         }
00552         m_presence_map.insert( uid, presence );
00553     }
00554 }
00555 
00556 KIMIface_stub * KIMProxy::stubForUid( const QString &uid )
00557 {
00558     pollAll( uid );
00559     // get best appPresence
00560     
00561     AppPresence *ap = m_presence_map.find( uid );
00562     // look up the presence string from that app
00563     if ( ap )
00564         return m_im_client_stubs.find( bestAppId( ap ) );
00565     else
00566         return 0L;
00567 }
00568 
00569 KIMIface_stub * KIMProxy::stubForProtocol( const QString &protocol)
00570 {
00571     KIMIface_stub * app;
00572     // see if the preferred client supports this protocol
00573     QString preferred = preferredApp();
00574     if ( ( app = m_im_client_stubs.find( preferred ) ) )
00575     {
00576         if ( app->protocols().grep( protocol ).count() > 0 )
00577             return app;
00578     }
00579     // preferred doesn't do this protocol, try the first of the others that says it does
00580     QDictIterator<KIMIface_stub> it( m_im_client_stubs );
00581     for ( ; it.current(); ++it )
00582     {
00583         if ( it.current()->protocols().grep( protocol ).count() > 0 )
00584             return it.current();
00585     }   
00586     return 0L;
00587 }
00588 
00589 QString KIMProxy::preferredApp()
00590 {
00591     KConfig *store = new KSimpleConfig( IM_CLIENT_PREFERENCES_FILE );
00592     store->setGroup( IM_CLIENT_PREFERENCES_SECTION );
00593     QString preferredApp = store->readEntry( IM_CLIENT_PREFERENCES_ENTRY );
00594     //kdDebug( 5301 ) << k_funcinfo << "found preferred app: " << preferredApp << endl;
00595     return preferredApp;
00596 }   
00597 
00598 #include "kimproxy.moc"
KDE Logo
This file is part of the documentation for interfaces Library Version 3.3.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Jul 22 10:17:55 2005 by doxygen 1.3.6 written by Dimitri van Heesch, © 1997-2003