TDE core libraries
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653
  1. /*
  2. tdeimproxy.cpp
  3. IM service library for KDE
  4. Copyright (c) 2004 Will Stephenson <lists@stevello.free-online.co.uk>
  5. This library is free software; you can redistribute it and/or
  6. modify it under the terms of the GNU Library General Public
  7. License as published by the Free Software Foundation; either
  8. version 2 of the License, or (at your option) any later version.
  9. This library is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. Library General Public License for more details.
  13. You should have received a copy of the GNU Library General Public License
  14. along with this library; see the file COPYING.LIB. If not, write to
  15. the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  16. Boston, MA 02110-1301, USA.
  17. */
  18. #include <tqglobal.h>
  19. #include <tqpixmapcache.h>
  20. #include <dcopclient.h>
  21. #include <tdeapplication.h>
  22. #include <kdcopservicestarter.h>
  23. #include <kdebug.h>
  24. #include <tdemessagebox.h>
  25. #include <ksimpleconfig.h>
  26. #include <kiconloader.h>
  27. #include <kservice.h>
  28. #include <kservicetype.h>
  29. #include "kimiface_stub.h"
  30. #include "tdeimproxy.h"
  31. static KStaticDeleter<KIMProxy> _staticDeleter;
  32. KIMProxy * KIMProxy::s_instance = 0L;
  33. struct AppPresenceCurrent
  34. {
  35. TQCString appId;
  36. int presence;
  37. };
  38. class ContactPresenceListCurrent : public TQValueList<AppPresenceCurrent>
  39. {
  40. public:
  41. // return value indicates if the supplied parameter was better than any existing presence
  42. bool update( const AppPresenceCurrent );
  43. AppPresenceCurrent best();
  44. };
  45. struct KIMProxy::Private
  46. {
  47. DCOPClient * dc;
  48. // list of the strings in use by KIMIface
  49. TQStringList presence_strings;
  50. // list of the icon names in use by KIMIface
  51. TQStringList presence_icons;
  52. // map of presences
  53. PresenceStringMap presence_map;
  54. };
  55. bool ContactPresenceListCurrent::update( AppPresenceCurrent ap )
  56. {
  57. if ( isEmpty() )
  58. {
  59. append( ap );
  60. return true;
  61. }
  62. bool bestChanged = false;
  63. AppPresenceCurrent best;
  64. best.presence = -1;
  65. ContactPresenceListCurrent::iterator it = begin();
  66. const ContactPresenceListCurrent::iterator itEnd = end();
  67. ContactPresenceListCurrent::iterator existing = itEnd;
  68. while ( it != itEnd )
  69. {
  70. if ( (*it).presence > best.presence )
  71. best = (*it);
  72. if ( (*it).appId == ap.appId )
  73. existing = it;
  74. ++it;
  75. }
  76. if ( ap.presence > best.presence ||
  77. best.appId == ap.appId )
  78. bestChanged = true;
  79. if ( existing != itEnd )
  80. {
  81. remove( existing );
  82. append( ap );
  83. }
  84. return bestChanged;
  85. }
  86. AppPresenceCurrent ContactPresenceListCurrent::best()
  87. {
  88. AppPresenceCurrent best;
  89. best.presence = -1;
  90. ContactPresenceListCurrent::iterator it = begin();
  91. const ContactPresenceListCurrent::iterator itEnd = end();
  92. while ( it != itEnd )
  93. {
  94. if ( (*it).presence > best.presence )
  95. best = (*it);
  96. ++it;
  97. }
  98. // if it's still -1 here, we have no presence data, so we return Unknown
  99. if ( best.presence == -1 )
  100. best.presence = 0;
  101. return best;
  102. }
  103. // int bestPresence( AppPresence* ap )
  104. // {
  105. // Q_ASSERT( ap );
  106. // AppPresence::const_iterator it;
  107. // it = ap->begin();
  108. // int best = 0; // unknown
  109. // if ( it != ap->end() )
  110. // {
  111. // best = it.data();
  112. // ++it;
  113. // for ( ; it != ap->end(); ++it )
  114. // {
  115. // if ( it.data() > best )
  116. // best = it.data();
  117. // }
  118. // }
  119. // return best;
  120. // }
  121. //
  122. // TQCString bestAppId( AppPresence* ap )
  123. // {
  124. // Q_ASSERT( ap );
  125. // AppPresence::const_iterator it;
  126. // TQCString bestAppId;
  127. // it = ap->begin();
  128. // if ( it != ap->end() )
  129. // {
  130. // int best = it.data();
  131. // bestAppId = it.key();
  132. // ++it;
  133. // for ( ; it != ap->end(); ++it )
  134. // {
  135. // if ( it.data() > best )
  136. // {
  137. // best = it.data();
  138. // bestAppId = it.key();
  139. // }
  140. // }
  141. // }
  142. // return bestAppId;
  143. // }
  144. KIMProxy * KIMProxy::instance( DCOPClient * client )
  145. {
  146. if ( client )
  147. {
  148. if ( !s_instance )
  149. _staticDeleter.setObject( s_instance, new KIMProxy( client ) );
  150. return s_instance;
  151. }
  152. else
  153. return 0L;
  154. }
  155. KIMProxy::KIMProxy( DCOPClient* dc ) : DCOPObject( "KIMProxyIface" ), TQObject(), d( new Private )
  156. {
  157. m_im_client_stubs.setAutoDelete( true );
  158. d->dc = dc;
  159. m_initialized = false;
  160. connect( d->dc, TQT_SIGNAL( applicationRemoved( const TQCString& ) ) , this, TQT_SLOT( unregisteredFromDCOP( const TQCString& ) ) );
  161. connect( d->dc, TQT_SIGNAL( applicationRegistered( const TQCString& ) ) , this, TQT_SLOT( registeredToDCOP( const TQCString& ) ) );
  162. d->dc->setNotifications( true );
  163. d->presence_strings.append( "Unknown" );
  164. d->presence_strings.append( "Offline" );
  165. d->presence_strings.append( "Connecting" );
  166. d->presence_strings.append( "Away" );
  167. d->presence_strings.append( "Online" );
  168. d->presence_icons.append( "presence_unknown" );
  169. d->presence_icons.append( "presence_offline" );
  170. d->presence_icons.append( "presence_connecting" );
  171. d->presence_icons.append( "presence_away" );
  172. d->presence_icons.append( "presence_online" );
  173. //TQCString senderApp = "Kopete";
  174. //TQCString senderObjectId = "KIMIface";
  175. TQCString method = "contactPresenceChanged( TQString, TQCString, int )";
  176. //TQCString receiverObjectId = "KIMProxyIface";
  177. // FIXME: make this work when the sender object id is set to KIMIFace
  178. if ( !connectDCOPSignal( 0, 0, method, method, false ) )
  179. kdWarning() << "Couldn't connect DCOP signal. Won't receive any status notifications!" << endl;
  180. }
  181. KIMProxy::~KIMProxy( )
  182. {
  183. //d->dc->setNotifications( false );
  184. }
  185. bool KIMProxy::initialize()
  186. {
  187. if ( !m_initialized )
  188. {
  189. m_initialized = true; // we should only do this once, as registeredToDCOP() will catch any new starts
  190. // So there is no error from a failed query when using tdelibs 3.2, which don't have this servicetype
  191. if ( KServiceType::serviceType( IM_SERVICE_TYPE ) )
  192. {
  193. //kdDebug( 790 ) << k_funcinfo << endl;
  194. TQCString dcopObjectId = "KIMIface";
  195. // see what apps implementing our service type are out there
  196. KService::List offers = KServiceType::offers( IM_SERVICE_TYPE );
  197. KService::List::iterator offer;
  198. typedef TQValueList<TQCString> QCStringList;
  199. QCStringList registeredApps = d->dc->registeredApplications();
  200. QCStringList::iterator app;
  201. const QCStringList::iterator end = registeredApps.end();
  202. // for each registered app
  203. for ( app = registeredApps.begin(); app != end; ++app )
  204. {
  205. //kdDebug( 790 ) << " considering: " << *app << endl;
  206. //for each offer
  207. for ( offer = offers.begin(); offer != offers.end(); ++offer )
  208. {
  209. TQCString dcopService = (*offer)->property("X-DCOP-ServiceName").toString().latin1();
  210. if ( !dcopService.isEmpty() )
  211. {
  212. //kdDebug( 790 ) << " is it: " << dcopService << "?" << endl;
  213. // get the application name ( minus any process ID )
  214. TQCString instanceName = (*app).left( dcopService.length() );
  215. // if the application implements the dcop service, add it
  216. if ( instanceName == dcopService )
  217. {
  218. m_apps_available = true;
  219. //kdDebug( 790 ) << " app name: " << (*offer)->name() << ", has instance " << *app << ", dcopService: " << dcopService << endl;
  220. if ( !m_im_client_stubs.find( dcopService ) )
  221. {
  222. kdDebug( 790 ) << "App " << *app << ", dcopObjectId " << dcopObjectId << " found, using it for presence info." << endl;
  223. m_im_client_stubs.insert( *app, new KIMIface_stub( d->dc, *app, dcopObjectId ) );
  224. pollApp( *app );
  225. }
  226. }
  227. }
  228. }
  229. }
  230. }
  231. }
  232. return !m_im_client_stubs.isEmpty();
  233. }
  234. void KIMProxy::registeredToDCOP( const TQCString& appId )
  235. {
  236. //kdDebug( 790 ) << k_funcinfo << " appId '" << appId << "'" << endl;
  237. // check that appId implements our service
  238. // if the appId ends with a number, i.e. a pid like in foobar-12345,
  239. if ( appId.isEmpty() )
  240. return;
  241. bool newApp = false;
  242. // get an up to date list of offers in case a new app was installed
  243. // and check each of the offers that implement the service type we're looking for,
  244. // to see if any of them are the app that just registered
  245. const KService::List offers = KServiceType::offers( IM_SERVICE_TYPE );
  246. KService::List::const_iterator it;
  247. for ( it = offers.begin(); it != offers.end(); ++it )
  248. {
  249. TQCString dcopObjectId = "KIMIface";
  250. TQCString dcopService = (*it)->property("X-DCOP-ServiceName").toString().latin1();
  251. if ( appId.left( dcopService.length() ) == dcopService )
  252. {
  253. // if it's not already known, insert it
  254. if ( !m_im_client_stubs.find( appId ) )
  255. {
  256. newApp = true;
  257. kdDebug( 790 ) << "App: " << appId << ", dcopService: " << dcopService << " started, using it for presence info."<< endl;
  258. m_im_client_stubs.insert( appId, new KIMIface_stub( d->dc, appId, dcopObjectId ) );
  259. }
  260. }
  261. //else
  262. // kdDebug( 790 ) << "App doesn't implement our ServiceType" << endl;
  263. }
  264. //if ( newApp )
  265. // emit sigPresenceInfoExpired();
  266. }
  267. void KIMProxy::unregisteredFromDCOP( const TQCString& appId )
  268. {
  269. //kdDebug( 790 ) << k_funcinfo << appId << endl;
  270. if ( m_im_client_stubs.find( appId ) )
  271. {
  272. kdDebug( 790 ) << appId << " quit, removing its presence info." << endl;
  273. PresenceStringMap::Iterator it = d->presence_map.begin();
  274. const PresenceStringMap::Iterator end = d->presence_map.end();
  275. for ( ; it != end; ++it )
  276. {
  277. ContactPresenceListCurrent list = it.data();
  278. ContactPresenceListCurrent::iterator cpIt = list.begin();
  279. while( cpIt != list.end() )
  280. {
  281. ContactPresenceListCurrent::iterator gone = cpIt++;
  282. if ( (*gone).appId == appId )
  283. {
  284. list.remove( gone );
  285. }
  286. }
  287. }
  288. m_im_client_stubs.remove( appId );
  289. emit sigPresenceInfoExpired();
  290. }
  291. }
  292. void KIMProxy::contactPresenceChanged( TQString uid, TQCString appId, int presence )
  293. {
  294. // update the presence map
  295. //kdDebug( 790 ) << k_funcinfo << "uid: " << uid << " appId: " << appId << " presence " << presence << endl;
  296. ContactPresenceListCurrent current;
  297. current = d->presence_map[ uid ];
  298. //kdDebug( 790 ) << "current best presence from : " << current.best().appId << " is: " << current.best().presence << endl;
  299. AppPresenceCurrent newPresence;
  300. newPresence.appId = appId;
  301. newPresence.presence = presence;
  302. if ( current.update( newPresence ) )
  303. {
  304. d->presence_map.insert( uid, current );
  305. emit sigContactPresenceChanged( uid );
  306. }
  307. }
  308. int KIMProxy::presenceNumeric( const TQString& uid )
  309. {
  310. AppPresenceCurrent ap;
  311. ap.presence = 0;
  312. if ( initialize() )
  313. {
  314. ContactPresenceListCurrent presence = d->presence_map[ uid ];
  315. ap = presence.best();
  316. }
  317. return ap.presence;
  318. }
  319. TQString KIMProxy::presenceString( const TQString& uid )
  320. {
  321. AppPresenceCurrent ap;
  322. ap.presence = 0;
  323. if ( initialize() )
  324. {
  325. ContactPresenceListCurrent presence = d->presence_map[ uid ];
  326. ap = presence.best();
  327. }
  328. if ( ap.appId.isEmpty() )
  329. return TQString::null;
  330. else
  331. return d->presence_strings[ ap.presence ];
  332. }
  333. TQPixmap KIMProxy::presenceIcon( const TQString& uid )
  334. {
  335. AppPresenceCurrent ap;
  336. ap.presence = 0;
  337. if ( initialize() )
  338. {
  339. ContactPresenceListCurrent presence = d->presence_map[ uid ];
  340. ap = presence.best();
  341. }
  342. if ( ap.appId.isEmpty() )
  343. {
  344. //kdDebug( 790 ) << k_funcinfo << "returning a null TQPixmap because we were asked for an icon for a uid we know nothing about" << endl;
  345. return TQPixmap();
  346. }
  347. else
  348. {
  349. //kdDebug( 790 ) << k_funcinfo << "returning this: " << d->presence_icons[ ap.presence ] << endl;
  350. return SmallIcon( d->presence_icons[ ap.presence ]);
  351. }
  352. }
  353. TQStringList KIMProxy::allContacts()
  354. {
  355. TQStringList value = d->presence_map.keys();
  356. return value;
  357. }
  358. TQStringList KIMProxy::reachableContacts()
  359. {
  360. TQStringList value;
  361. if ( initialize() )
  362. {
  363. TQDictIterator<KIMIface_stub> it( m_im_client_stubs );
  364. for ( ; it.current(); ++it )
  365. {
  366. value += it.current()->reachableContacts( );
  367. }
  368. }
  369. return value;
  370. }
  371. TQStringList KIMProxy::onlineContacts()
  372. {
  373. TQStringList value;
  374. PresenceStringMap::iterator it = d->presence_map.begin();
  375. const PresenceStringMap::iterator end= d->presence_map.end();
  376. for ( ; it != end; ++it )
  377. if ( it.data().best().presence > 2 /*Better than Connecting, ie Away or Online*/ )
  378. value.append( it.key() );
  379. return value;
  380. }
  381. TQStringList KIMProxy::fileTransferContacts()
  382. {
  383. TQStringList value;
  384. if ( initialize() )
  385. {
  386. TQDictIterator<KIMIface_stub> it( m_im_client_stubs );
  387. for ( ; it.current(); ++it )
  388. {
  389. value += it.current()->fileTransferContacts( );
  390. }
  391. }
  392. return value;
  393. }
  394. bool KIMProxy::isPresent( const TQString& uid )
  395. {
  396. return ( !d->presence_map[ uid ].isEmpty() );
  397. }
  398. TQString KIMProxy::displayName( const TQString& uid )
  399. {
  400. TQString name;
  401. if ( initialize() )
  402. {
  403. if ( KIMIface_stub* s = stubForUid( uid ) )
  404. name = s->displayName( uid );
  405. }
  406. //kdDebug( 790 ) << k_funcinfo << name << endl;
  407. return name;
  408. }
  409. bool KIMProxy::canReceiveFiles( const TQString & uid )
  410. {
  411. if ( initialize() )
  412. {
  413. if ( KIMIface_stub* s = stubForUid( uid ) )
  414. return s->canReceiveFiles( uid );
  415. }
  416. return false;
  417. }
  418. bool KIMProxy::canRespond( const TQString & uid )
  419. {
  420. if ( initialize() )
  421. {
  422. if ( KIMIface_stub* s = stubForUid( uid ) )
  423. return s->canRespond( uid );
  424. }
  425. return false;
  426. }
  427. TQString KIMProxy::context( const TQString & uid )
  428. {
  429. if ( initialize() )
  430. {
  431. if ( KIMIface_stub* s = stubForUid( uid ) )
  432. return s->context( uid );
  433. }
  434. return TQString::null;
  435. }
  436. void KIMProxy::chatWithContact( const TQString& uid )
  437. {
  438. if ( initialize() )
  439. {
  440. if ( KIMIface_stub* s = stubForUid( uid ) )
  441. {
  442. kapp->updateRemoteUserTimestamp( s->app() );
  443. s->chatWithContact( uid );
  444. }
  445. }
  446. return;
  447. }
  448. void KIMProxy::messageContact( const TQString& uid, const TQString& message )
  449. {
  450. if ( initialize() )
  451. {
  452. if ( KIMIface_stub* s = stubForUid( uid ) )
  453. {
  454. kapp->updateRemoteUserTimestamp( s->app() );
  455. s->messageContact( uid, message );
  456. }
  457. }
  458. return;
  459. }
  460. void KIMProxy::sendFile(const TQString &uid, const KURL &sourceURL, const TQString &altFileName, uint fileSize )
  461. {
  462. if ( initialize() )
  463. {
  464. TQDictIterator<KIMIface_stub> it( m_im_client_stubs );
  465. for ( ; it.current(); ++it )
  466. {
  467. if ( it.current()->canReceiveFiles( uid ) )
  468. {
  469. kapp->updateRemoteUserTimestamp( it.current()->app() );
  470. it.current()->sendFile( uid, sourceURL, altFileName, fileSize );
  471. break;
  472. }
  473. }
  474. }
  475. return;
  476. }
  477. bool KIMProxy::addContact( const TQString &contactId, const TQString &protocol )
  478. {
  479. if ( initialize() )
  480. {
  481. if ( KIMIface_stub* s = stubForProtocol( protocol ) )
  482. return s->addContact( contactId, protocol );
  483. }
  484. return false;
  485. }
  486. TQString KIMProxy::locate( const TQString & contactId, const TQString & protocol )
  487. {
  488. if ( initialize() )
  489. {
  490. if ( KIMIface_stub* s = stubForProtocol( protocol ) )
  491. return s->locate( contactId, protocol );
  492. }
  493. return TQString::null;
  494. }
  495. bool KIMProxy::imAppsAvailable()
  496. {
  497. return ( !m_im_client_stubs.isEmpty() );
  498. }
  499. bool KIMProxy::startPreferredApp()
  500. {
  501. TQString preferences = TQString("[X-DCOP-ServiceName] = '%1'").arg( preferredApp() );
  502. // start/find an instance of DCOP/InstantMessenger
  503. TQString error;
  504. TQCString dcopService;
  505. // Get a preferred IM client.
  506. // The app will notify itself to us using registeredToDCOP, so we don't need to record a stub for it here
  507. // FIXME: error in preferences, see debug output
  508. preferences = TQString::null;
  509. int result = KDCOPServiceStarter::self()->findServiceFor( IM_SERVICE_TYPE, TQString::null, preferences, &error, &dcopService );
  510. kdDebug( 790 ) << k_funcinfo << "error was: " << error << ", dcopService: " << dcopService << endl;
  511. return ( result == 0 );
  512. }
  513. void KIMProxy::pollAll( const TQString &uid )
  514. {
  515. /* // We only need to call this function if we don't have any data at all
  516. // otherwise, the data will be kept fresh by received presence change
  517. // DCOP signals
  518. if ( !d->presence_map.contains( uid ) )
  519. {
  520. AppPresence *presence = new AppPresence();
  521. // record current presence from known clients
  522. TQDictIterator<KIMIface_stub> it( m_im_client_stubs );
  523. for ( ; it.current(); ++it )
  524. {
  525. presence->insert( it.currentKey().ascii(), it.current()->presenceStatus( uid ) ); // m_im_client_stubs has qstring keys...
  526. }
  527. d->presence_map.insert( uid, presence );
  528. }*/
  529. }
  530. void KIMProxy::pollApp( const TQCString & appId )
  531. {
  532. //kdDebug( 790 ) << k_funcinfo << endl;
  533. KIMIface_stub * appStub = m_im_client_stubs[ appId ];
  534. TQStringList contacts = m_im_client_stubs[ appId ]->allContacts();
  535. TQStringList::iterator it = contacts.begin();
  536. TQStringList::iterator end = contacts.end();
  537. for ( ; it != end; ++it )
  538. {
  539. ContactPresenceListCurrent current = d->presence_map[ *it ];
  540. AppPresenceCurrent ap;
  541. ap.appId = appId;
  542. ap.presence = appStub->presenceStatus( *it );
  543. current.append( ap );
  544. d->presence_map.insert( *it, current );
  545. if ( current.update( ap ) )
  546. emit sigContactPresenceChanged( *it );
  547. //kdDebug( 790 ) << " uid: " << *it << " presence: " << ap.presence << endl;
  548. }
  549. }
  550. KIMIface_stub * KIMProxy::stubForUid( const TQString &uid )
  551. {
  552. // get best appPresence
  553. AppPresenceCurrent ap = d->presence_map[ uid ].best();
  554. // look up the presence string from that app
  555. return m_im_client_stubs.find( ap.appId );
  556. }
  557. KIMIface_stub * KIMProxy::stubForProtocol( const TQString &protocol)
  558. {
  559. KIMIface_stub * app;
  560. // see if the preferred client supports this protocol
  561. TQString preferred = preferredApp();
  562. if ( ( app = m_im_client_stubs.find( preferred ) ) )
  563. {
  564. if ( app->protocols().grep( protocol ).count() > 0 )
  565. return app;
  566. }
  567. // preferred doesn't do this protocol, try the first of the others that says it does
  568. TQDictIterator<KIMIface_stub> it( m_im_client_stubs );
  569. for ( ; it.current(); ++it )
  570. {
  571. if ( it.current()->protocols().grep( protocol ).count() > 0 )
  572. return it.current();
  573. }
  574. return 0L;
  575. }
  576. TQString KIMProxy::preferredApp()
  577. {
  578. TDEConfig *store = new KSimpleConfig( IM_CLIENT_PREFERENCES_FILE );
  579. store->setGroup( IM_CLIENT_PREFERENCES_SECTION );
  580. TQString preferredApp = store->readEntry( IM_CLIENT_PREFERENCES_ENTRY );
  581. //kdDebug( 790 ) << k_funcinfo << "found preferred app: " << preferredApp << endl;
  582. return preferredApp;
  583. }
  584. #include "tdeimproxy.moc"