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