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.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Sep 29 09:41:42 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003