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 <kapplication.h> 00028 #include <kdcopservicestarter.h> 00029 #include <kdebug.h> 00030 #include <kmessagebox.h> 00031 #include <ksimpleconfig.h> 00032 #include <kiconloader.h> 00033 #include <kservice.h> 00034 #include <kservicetype.h> 00035 00036 #include "kimiface_stub.h" 00037 00038 #include "kimproxy.h" 00039 00040 static KStaticDeleter<KIMProxy> _staticDeleter; 00041 00042 KIMProxy * KIMProxy::s_instance = 0L; 00043 00044 struct AppPresenceCurrent 00045 { 00046 QCString appId; 00047 int presence; 00048 }; 00049 00050 class ContactPresenceListCurrent : public QValueList<AppPresenceCurrent> 00051 { 00052 public: 00053 // return value indicates if the supplied parameter was better than any existing presence 00054 bool update( const AppPresenceCurrent ); 00055 AppPresenceCurrent best(); 00056 }; 00057 00058 00059 struct KIMProxy::Private 00060 { 00061 DCOPClient * dc; 00062 // list of the strings in use by KIMIface 00063 QStringList presence_strings; 00064 // list of the icon names in use by KIMIface 00065 QStringList presence_icons; 00066 // map of presences 00067 PresenceStringMap presence_map; 00068 }; 00069 00070 bool ContactPresenceListCurrent::update( AppPresenceCurrent ap ) 00071 { 00072 if ( isEmpty() ) 00073 { 00074 append( ap ); 00075 return true; 00076 } 00077 00078 bool bestChanged = false; 00079 AppPresenceCurrent best; 00080 best.presence = -1; 00081 ContactPresenceListCurrent::iterator it = begin(); 00082 const ContactPresenceListCurrent::iterator itEnd = end(); 00083 ContactPresenceListCurrent::iterator existing = itEnd; 00084 00085 while ( it != itEnd ) 00086 { 00087 if ( (*it).presence > best.presence ) 00088 best = (*it); 00089 if ( (*it).appId = ap.appId ) 00090 existing = it; 00091 ++it; 00092 } 00093 00094 if ( ap.presence > best.presence || 00095 best.appId == ap.appId ) 00096 bestChanged = true; 00097 00098 if ( existing != itEnd ) 00099 { 00100 remove( existing ); 00101 append( ap ); 00102 } 00103 return bestChanged; 00104 } 00105 00106 AppPresenceCurrent ContactPresenceListCurrent::best() 00107 { 00108 AppPresenceCurrent best; 00109 best.presence = -1; 00110 ContactPresenceListCurrent::iterator it = begin(); 00111 const ContactPresenceListCurrent::iterator itEnd = end(); 00112 while ( it != itEnd ) 00113 { 00114 if ( (*it).presence > best.presence ) 00115 best = (*it); 00116 ++it; 00117 } 00118 // if it's still -1 here, we have no presence data, so we return Unknown 00119 if ( best.presence == -1 ) 00120 best.presence = 0; 00121 return best; 00122 } 00123 00124 // int bestPresence( AppPresence* ap ) 00125 // { 00126 // Q_ASSERT( ap ); 00127 // AppPresence::const_iterator it; 00128 // it = ap->begin(); 00129 // int best = 0; // unknown 00130 // if ( it != ap->end() ) 00131 // { 00132 // best = it.data(); 00133 // ++it; 00134 // for ( ; it != ap->end(); ++it ) 00135 // { 00136 // if ( it.data() > best ) 00137 // best = it.data(); 00138 // } 00139 // } 00140 // return best; 00141 // } 00142 // 00143 // QCString bestAppId( AppPresence* ap ) 00144 // { 00145 // Q_ASSERT( ap ); 00146 // AppPresence::const_iterator it; 00147 // QCString bestAppId; 00148 // it = ap->begin(); 00149 // if ( it != ap->end() ) 00150 // { 00151 // int best = it.data(); 00152 // bestAppId = it.key(); 00153 // ++it; 00154 // for ( ; it != ap->end(); ++it ) 00155 // { 00156 // if ( it.data() > best ) 00157 // { 00158 // best = it.data(); 00159 // bestAppId = it.key(); 00160 // } 00161 // } 00162 // } 00163 // return bestAppId; 00164 // } 00165 00166 KIMProxy * KIMProxy::instance( DCOPClient * client ) 00167 { 00168 if ( client ) 00169 { 00170 if ( !s_instance ) 00171 _staticDeleter.setObject( s_instance, new KIMProxy( client ) ); 00172 return s_instance; 00173 } 00174 else 00175 return 0L; 00176 } 00177 00178 KIMProxy::KIMProxy( DCOPClient* dc ) : DCOPObject( "KIMProxyIface" ), QObject(), d( new Private ) 00179 { 00180 m_im_client_stubs.setAutoDelete( true ); 00181 00182 d->dc = dc; 00183 m_initialized = false; 00184 connect( d->dc, SIGNAL( applicationRemoved( const QCString& ) ) , this, SLOT( unregisteredFromDCOP( const QCString& ) ) ); 00185 connect( d->dc, SIGNAL( applicationRegistered( const QCString& ) ) , this, SLOT( registeredToDCOP( const QCString& ) ) ); 00186 d->dc->setNotifications( true ); 00187 00188 d->presence_strings.append( "Unknown" ); 00189 d->presence_strings.append( "Offline" ); 00190 d->presence_strings.append( "Connecting" ); 00191 d->presence_strings.append( "Away" ); 00192 d->presence_strings.append( "Online" ); 00193 00194 d->presence_icons.append( "presence_unknown" ); 00195 d->presence_icons.append( "presence_offline" ); 00196 d->presence_icons.append( "presence_connecting" ); 00197 d->presence_icons.append( "presence_away" ); 00198 d->presence_icons.append( "presence_online" ); 00199 00200 //QCString senderApp = "Kopete"; 00201 //QCString senderObjectId = "KIMIface"; 00202 QCString method = "contactPresenceChanged( QString, QCString, int )"; 00203 //QCString receiverObjectId = "KIMProxyIface"; 00204 00205 // FIXME: make this work when the sender object id is set to KIMIFace 00206 if ( !connectDCOPSignal( 0, 0, method, method, false ) ) 00207 KMessageBox::information( 0, QString( "Couldn't connect DCOP signal.\nWon't receive any status notifications!" ) ); 00208 } 00209 00210 KIMProxy::~KIMProxy( ) 00211 { 00212 //d->dc->setNotifications( false ); 00213 } 00214 00215 bool KIMProxy::initialize() 00216 { 00217 if ( !m_initialized ) 00218 { 00219 m_initialized = true; // we should only do this once, as registeredToDCOP() will catch any new starts 00220 // So there is no error from a failed query when using kdelibs 3.2, which don't have this servicetype 00221 if ( KServiceType::serviceType( IM_SERVICE_TYPE ) ) 00222 { 00223 //kdDebug( 790 ) << k_funcinfo << endl; 00224 QCString dcopObjectId = "KIMIface"; 00225 00226 // see what apps implementing our service type are out there 00227 KService::List offers = KServiceType::offers( IM_SERVICE_TYPE ); 00228 KService::List::iterator offer; 00229 typedef QValueList<QCString> QCStringList; 00230 QCStringList registeredApps = d->dc->registeredApplications(); 00231 QCStringList::iterator app; 00232 const QCStringList::iterator end = registeredApps.end(); 00233 // for each registered app 00234 for ( app = registeredApps.begin(); app != end; ++app ) 00235 { 00236 //kdDebug( 790 ) << " considering: " << *app << endl; 00237 //for each offer 00238 for ( offer = offers.begin(); offer != offers.end(); ++offer ) 00239 { 00240 QCString dcopService = (*offer)->property("X-DCOP-ServiceName").toString().latin1(); 00241 if ( !dcopService.isEmpty() ) 00242 { 00243 //kdDebug( 790 ) << " is it: " << dcopService << "?" << endl; 00244 // get the application name ( minus any process ID ) 00245 QCString instanceName = (*app).left( dcopService.length() ); 00246 // if the application implements the dcop service, add it 00247 if ( instanceName == dcopService ) 00248 { 00249 m_apps_available = true; 00250 //kdDebug( 790 ) << " app name: " << (*offer)->name() << ", has instance " << *app << ", dcopService: " << dcopService << endl; 00251 if ( !m_im_client_stubs.find( dcopService ) ) 00252 { 00253 kdDebug( 790 ) << "App " << *app << ", dcopObjectId " << dcopObjectId << " found, using it for presence info." << endl; 00254 m_im_client_stubs.insert( *app, new KIMIface_stub( d->dc, *app, dcopObjectId ) ); 00255 pollApp( *app ); 00256 } 00257 } 00258 } 00259 } 00260 } 00261 } 00262 } 00263 return !m_im_client_stubs.isEmpty(); 00264 } 00265 00266 void KIMProxy::registeredToDCOP( const QCString& appId ) 00267 { 00268 //kdDebug( 790 ) << k_funcinfo << " appId '" << appId << "'" << endl; 00269 // check that appId implements our service 00270 // if the appId ends with a number, i.e. a pid like in foobar-12345, 00271 if ( appId.isEmpty() ) 00272 return; 00273 00274 bool newApp = false; 00275 // get an up to date list of offers in case a new app was installed 00276 // and check each of the offers that implement the service type we're looking for, 00277 // to see if any of them are the app that just registered 00278 const KService::List offers = KServiceType::offers( IM_SERVICE_TYPE ); 00279 KService::List::const_iterator it; 00280 for ( it = offers.begin(); it != offers.end(); ++it ) 00281 { 00282 QCString dcopObjectId = "KIMIface"; 00283 QCString dcopService = (*it)->property("X-DCOP-ServiceName").toString().latin1(); 00284 if ( appId.left( dcopService.length() ) == dcopService ) 00285 { 00286 // if it's not already known, insert it 00287 if ( !m_im_client_stubs.find( appId ) ) 00288 { 00289 newApp = true; 00290 kdDebug( 790 ) << "App: " << appId << ", dcopService: " << dcopService << " started, using it for presence info."<< endl; 00291 m_im_client_stubs.insert( appId, new KIMIface_stub( d->dc, appId, dcopObjectId ) ); 00292 } 00293 } 00294 //else 00295 // kdDebug( 790 ) << "App doesn't implement our ServiceType" << endl; 00296 } 00297 //if ( newApp ) 00298 // emit sigPresenceInfoExpired(); 00299 } 00300 00301 void KIMProxy::unregisteredFromDCOP( const QCString& appId ) 00302 { 00303 //kdDebug( 790 ) << k_funcinfo << appId << endl; 00304 if ( m_im_client_stubs.find( appId ) ) 00305 { 00306 kdDebug( 790 ) << appId << " quit, removing its presence info." << endl; 00307 00308 PresenceStringMap::Iterator it = d->presence_map.begin(); 00309 const PresenceStringMap::Iterator end = d->presence_map.end(); 00310 for ( ; it != end; ++it ) 00311 { 00312 ContactPresenceListCurrent list = it.data(); 00313 ContactPresenceListCurrent::iterator cpIt = list.begin(); 00314 while( cpIt != list.end() ) 00315 { 00316 ContactPresenceListCurrent::iterator gone = cpIt++; 00317 if ( (*gone).appId == appId ) 00318 { 00319 list.remove( gone ); 00320 } 00321 } 00322 } 00323 m_im_client_stubs.remove( appId ); 00324 emit sigPresenceInfoExpired(); 00325 } 00326 } 00327 00328 void KIMProxy::contactPresenceChanged( QString uid, QCString appId, int presence ) 00329 { 00330 // update the presence map 00331 //kdDebug( 790 ) << k_funcinfo << "uid: " << uid << " appId: " << appId << " presence " << presence << endl; 00332 ContactPresenceListCurrent current; 00333 current = d->presence_map[ uid ]; 00334 //kdDebug( 790 ) << "current best presence from : " << current.best().appId << " is: " << current.best().presence << endl; 00335 AppPresenceCurrent newPresence; 00336 newPresence.appId = appId; 00337 newPresence.presence = presence; 00338 00339 if ( current.update( newPresence ) ) 00340 { 00341 d->presence_map.insert( uid, current ); 00342 emit sigContactPresenceChanged( uid ); 00343 } 00344 } 00345 00346 int KIMProxy::presenceNumeric( const QString& uid ) 00347 { 00348 AppPresenceCurrent ap; 00349 ap.presence = 0; 00350 if ( initialize() ) 00351 { 00352 ContactPresenceListCurrent presence = d->presence_map[ uid ]; 00353 ap = presence.best(); 00354 } 00355 return ap.presence; 00356 } 00357 00358 QString KIMProxy::presenceString( const QString& uid ) 00359 { 00360 AppPresenceCurrent ap; 00361 ap.presence = 0; 00362 if ( initialize() ) 00363 { 00364 ContactPresenceListCurrent presence = d->presence_map[ uid ]; 00365 ap = presence.best(); 00366 } 00367 if ( ap.appId.isEmpty() ) 00368 return QString::null; 00369 else 00370 return d->presence_strings[ ap.presence ]; 00371 } 00372 00373 QPixmap KIMProxy::presenceIcon( const QString& uid ) 00374 { 00375 AppPresenceCurrent ap; 00376 ap.presence = 0; 00377 if ( initialize() ) 00378 { 00379 ContactPresenceListCurrent presence = d->presence_map[ uid ]; 00380 ap = presence.best(); 00381 } 00382 if ( ap.appId.isEmpty() ) 00383 { 00384 //kdDebug( 790 ) << k_funcinfo << "returning a null QPixmap because we were asked for an icon for a uid we know nothing about" << endl; 00385 return QPixmap(); 00386 } 00387 else 00388 { 00389 //kdDebug( 790 ) << k_funcinfo << "returning this: " << d->presence_icons[ ap.presence ] << endl; 00390 return SmallIcon( d->presence_icons[ ap.presence ]); 00391 } 00392 } 00393 00394 QStringList KIMProxy::allContacts() 00395 { 00396 QStringList value = d->presence_map.keys(); 00397 return value; 00398 } 00399 00400 QStringList KIMProxy::reachableContacts() 00401 { 00402 QStringList value; 00403 00404 if ( initialize() ) 00405 { 00406 QDictIterator<KIMIface_stub> it( m_im_client_stubs ); 00407 for ( ; it.current(); ++it ) 00408 { 00409 value += it.current()->reachableContacts( ); 00410 } 00411 } 00412 return value; 00413 } 00414 00415 QStringList KIMProxy::onlineContacts() 00416 { 00417 QStringList value; 00418 PresenceStringMap::iterator it = d->presence_map.begin(); 00419 const PresenceStringMap::iterator end= d->presence_map.end(); 00420 for ( ; it != end; ++it ) 00421 if ( it.data().best().presence > 2 /*Better than Connecting, ie Away or Online*/ ) 00422 value.append( it.key() ); 00423 00424 return value; 00425 } 00426 00427 QStringList KIMProxy::fileTransferContacts() 00428 { 00429 QStringList value; 00430 00431 if ( initialize() ) 00432 { 00433 QDictIterator<KIMIface_stub> it( m_im_client_stubs ); 00434 for ( ; it.current(); ++it ) 00435 { 00436 value += it.current()->fileTransferContacts( ); 00437 } 00438 } 00439 return value; 00440 } 00441 00442 bool KIMProxy::isPresent( const QString& uid ) 00443 { 00444 return ( !d->presence_map[ uid ].isEmpty() ); 00445 } 00446 00447 QString KIMProxy::displayName( const QString& uid ) 00448 { 00449 QString name; 00450 if ( initialize() ) 00451 { 00452 if ( KIMIface_stub* s = stubForUid( uid ) ) 00453 name = s->displayName( uid ); 00454 } 00455 //kdDebug( 790 ) << k_funcinfo << name << endl; 00456 return name; 00457 } 00458 00459 bool KIMProxy::canReceiveFiles( const QString & uid ) 00460 { 00461 if ( initialize() ) 00462 { 00463 if ( KIMIface_stub* s = stubForUid( uid ) ) 00464 return s->canReceiveFiles( uid ); 00465 } 00466 return false; 00467 } 00468 00469 bool KIMProxy::canRespond( const QString & uid ) 00470 { 00471 if ( initialize() ) 00472 { 00473 if ( KIMIface_stub* s = stubForUid( uid ) ) 00474 return s->canRespond( uid ); 00475 } 00476 return false; 00477 } 00478 00479 QString KIMProxy::context( const QString & uid ) 00480 { 00481 if ( initialize() ) 00482 { 00483 if ( KIMIface_stub* s = stubForUid( uid ) ) 00484 return s->context( uid ); 00485 } 00486 return QString::null; 00487 } 00488 00489 void KIMProxy::chatWithContact( const QString& uid ) 00490 { 00491 if ( initialize() ) 00492 { 00493 if ( KIMIface_stub* s = stubForUid( uid ) ) 00494 { 00495 kapp->updateRemoteUserTimestamp( s->app() ); 00496 s->chatWithContact( uid ); 00497 } 00498 } 00499 return; 00500 } 00501 00502 void KIMProxy::messageContact( const QString& uid, const QString& message ) 00503 { 00504 if ( initialize() ) 00505 { 00506 if ( KIMIface_stub* s = stubForUid( uid ) ) 00507 { 00508 kapp->updateRemoteUserTimestamp( s->app() ); 00509 s->messageContact( uid, message ); 00510 } 00511 } 00512 return; 00513 } 00514 00515 void KIMProxy::sendFile(const QString &uid, const KURL &sourceURL, const QString &altFileName, uint fileSize ) 00516 { 00517 if ( initialize() ) 00518 { 00519 QDictIterator<KIMIface_stub> it( m_im_client_stubs ); 00520 for ( ; it.current(); ++it ) 00521 { 00522 if ( it.current()->canReceiveFiles( uid ) ) 00523 { 00524 kapp->updateRemoteUserTimestamp( it.current()->app() ); 00525 it.current()->sendFile( uid, sourceURL, altFileName, fileSize ); 00526 break; 00527 } 00528 } 00529 } 00530 return; 00531 } 00532 00533 bool KIMProxy::addContact( const QString &contactId, const QString &protocol ) 00534 { 00535 if ( initialize() ) 00536 { 00537 if ( KIMIface_stub* s = stubForProtocol( protocol ) ) 00538 return s->addContact( contactId, protocol ); 00539 } 00540 return false; 00541 } 00542 00543 QString KIMProxy::locate( const QString & contactId, const QString & protocol ) 00544 { 00545 if ( initialize() ) 00546 { 00547 if ( KIMIface_stub* s = stubForProtocol( protocol ) ) 00548 return s->locate( contactId, protocol ); 00549 } 00550 return QString::null; 00551 } 00552 00553 bool KIMProxy::imAppsAvailable() 00554 { 00555 return ( !m_im_client_stubs.isEmpty() ); 00556 } 00557 00558 bool KIMProxy::startPreferredApp() 00559 { 00560 QString preferences = QString("[X-DCOP-ServiceName] = '%1'").arg( preferredApp() ); 00561 // start/find an instance of DCOP/InstantMessenger 00562 QString error; 00563 QCString dcopService; 00564 // Get a preferred IM client. 00565 // The app will notify itself to us using registeredToDCOP, so we don't need to record a stub for it here 00566 // FIXME: error in preferences, see debug output 00567 preferences = QString::null; 00568 int result = KDCOPServiceStarter::self()->findServiceFor( IM_SERVICE_TYPE, QString::null, preferences, &error, &dcopService ); 00569 00570 kdDebug( 790 ) << k_funcinfo << "error was: " << error << ", dcopService: " << dcopService << endl; 00571 00572 return ( result == 0 ); 00573 } 00574 00575 00576 void KIMProxy::pollAll( const QString &uid ) 00577 { 00578 /* // We only need to call this function if we don't have any data at all 00579 // otherwise, the data will be kept fresh by received presence change 00580 // DCOP signals 00581 if ( !d->presence_map.contains( uid ) ) 00582 { 00583 AppPresence *presence = new AppPresence(); 00584 // record current presence from known clients 00585 QDictIterator<KIMIface_stub> it( m_im_client_stubs ); 00586 for ( ; it.current(); ++it ) 00587 { 00588 presence->insert( it.currentKey().ascii(), it.current()->presenceStatus( uid ) ); // m_im_client_stubs has qstring keys... 00589 } 00590 d->presence_map.insert( uid, presence ); 00591 }*/ 00592 } 00593 00594 void KIMProxy::pollApp( const QCString & appId ) 00595 { 00596 //kdDebug( 790 ) << k_funcinfo << endl; 00597 KIMIface_stub * appStub = m_im_client_stubs[ appId ]; 00598 QStringList contacts = m_im_client_stubs[ appId ]->allContacts(); 00599 QStringList::iterator it = contacts.begin(); 00600 QStringList::iterator end = contacts.end(); 00601 for ( ; it != end; ++it ) 00602 { 00603 ContactPresenceListCurrent current = d->presence_map[ *it ]; 00604 AppPresenceCurrent ap; 00605 ap.appId = appId; 00606 ap.presence = appStub->presenceStatus( *it ); 00607 current.append( ap ); 00608 00609 d->presence_map.insert( *it, current ); 00610 if ( current.update( ap ) ) 00611 emit sigContactPresenceChanged( *it ); 00612 //kdDebug( 790 ) << " uid: " << *it << " presence: " << ap.presence << endl; 00613 } 00614 } 00615 00616 KIMIface_stub * KIMProxy::stubForUid( const QString &uid ) 00617 { 00618 // get best appPresence 00619 AppPresenceCurrent ap = d->presence_map[ uid ].best(); 00620 // look up the presence string from that app 00621 return m_im_client_stubs.find( ap.appId ); 00622 } 00623 00624 KIMIface_stub * KIMProxy::stubForProtocol( const QString &protocol) 00625 { 00626 KIMIface_stub * app; 00627 // see if the preferred client supports this protocol 00628 QString preferred = preferredApp(); 00629 if ( ( app = m_im_client_stubs.find( preferred ) ) ) 00630 { 00631 if ( app->protocols().grep( protocol ).count() > 0 ) 00632 return app; 00633 } 00634 // preferred doesn't do this protocol, try the first of the others that says it does 00635 QDictIterator<KIMIface_stub> it( m_im_client_stubs ); 00636 for ( ; it.current(); ++it ) 00637 { 00638 if ( it.current()->protocols().grep( protocol ).count() > 0 ) 00639 return it.current(); 00640 } 00641 return 0L; 00642 } 00643 00644 QString KIMProxy::preferredApp() 00645 { 00646 KConfig *store = new KSimpleConfig( IM_CLIENT_PREFERENCES_FILE ); 00647 store->setGroup( IM_CLIENT_PREFERENCES_SECTION ); 00648 QString preferredApp = store->readEntry( IM_CLIENT_PREFERENCES_ENTRY ); 00649 //kdDebug( 790 ) << k_funcinfo << "found preferred app: " << preferredApp << endl; 00650 return preferredApp; 00651 } 00652 00653 #include "kimproxy.moc"
KDE Logo
This file is part of the documentation for interfaces Library Version 3.4.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Apr 14 00:44:00 2005 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003