bump product version to 4.1.6.2
[LibreOffice.git] / tubes / source / manager.cxx
blob58c2b5bde6769adffbc710e43d8ffc4c1306975c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include <tubes/manager.hxx>
12 #include <tubes/collaboration.hxx>
13 #include <tubes/conference.hxx>
14 #include <tubes/constants.h>
15 #include <tubes/file-transfer-helper.h>
17 #include <com/sun/star/uno/Sequence.hxx>
18 #include <com/sun/star/frame/Desktop.hpp>
19 #include <com/sun/star/frame/XComponentLoader.hpp>
20 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
21 #include <com/sun/star/beans/PropertyValue.hpp>
22 #include <com/sun/star/util/XCloseable.hpp>
23 #include <comphelper/processfactory.hxx>
24 #include <osl/mutex.hxx>
25 #include <rtl/strbuf.hxx>
26 #include <rtl/uuid.h>
28 #include <telepathy-glib/telepathy-glib.h>
29 #include <stdio.h>
30 #include <map>
31 #include <set>
33 #if defined SAL_LOG_INFO
34 namespace
36 struct InfoLogger
38 const void* mpThat;
39 const char* mpMethod;
40 explicit InfoLogger( const void* pThat, const char* pMethod )
42 mpThat( pThat),
43 mpMethod( pMethod)
45 SAL_INFO( "tubes.method", mpThat << " entering " << mpMethod);
47 ~InfoLogger()
49 SAL_INFO( "tubes.method", mpThat << " leaving " << mpMethod);
53 #define INFO_LOGGER_F(s) InfoLogger aLogger(0,(s))
54 #else
55 #define INFO_LOGGER_F(s)
56 #endif // SAL_LOG_INFO
59 using namespace rtl;
60 using namespace osl;
62 /** Refcounted singleton implementation class. */
63 class TeleManagerImpl
65 public:
66 TpSimpleClientFactory* mpFactory;
67 TpBaseClient* mpClient;
68 TpBaseClient* mpFileTransferClient;
69 TpAccountManager* mpAccountManager;
70 static bool mbAccountManagerReady;
71 static bool mbAccountManagerReadyHandlerInvoked;
72 static bool mbChannelReadyHandlerInvoked;
73 OString msCurrentUUID;
74 OString msNameSuffix;
75 typedef std::map< OString, TeleConference* > MapStringConference;
76 MapStringConference maAcceptedConferences;
77 typedef std::set< TeleConference* > DemoConferences;
78 DemoConferences maDemoConferences;
79 typedef std::set< Collaboration* > Collaborations;
80 Collaborations maCollaborations;
81 typedef std::set< TpContact* > RegisteredContacts;
82 RegisteredContacts maRegisteredContacts;
84 TeleManagerImpl();
85 ~TeleManagerImpl();
86 static void AccountManagerReadyHandler( GObject* pSourceObject, GAsyncResult* pResult, gpointer pUserData );
87 static void ChannelReadyHandler( GObject* pSourceObject, GAsyncResult* pResult, gpointer pUserData );
90 TeleManagerImpl* TeleManager::pImpl = new TeleManagerImpl();
91 bool TeleManagerImpl::mbAccountManagerReady;
92 bool TeleManagerImpl::mbAccountManagerReadyHandlerInvoked;
93 bool TeleManagerImpl::mbChannelReadyHandlerInvoked;
95 static void TeleManager_DBusChannelHandler(
96 TpSimpleHandler* /*handler*/,
97 TpAccount* pAccount,
98 TpConnection* /*connection*/,
99 GList* pChannels,
100 GList* /*requests_satisfied*/,
101 gint64 /*user_action_time*/,
102 TpHandleChannelsContext* pContext,
103 gpointer /*pUserData*/ )
105 bool aAccepted = false;
106 INFO_LOGGER_F( "TeleManager_DBusChannelHandler");
108 for (GList* p = pChannels; p; p = p->next)
110 TpChannel* pChannel = TP_CHANNEL(p->data);
111 if (!pChannel)
112 continue;
114 SAL_INFO( "tubes", "TeleManager_DBusChannelHandler: incoming dbus channel: "
115 << tp_channel_get_identifier( pChannel));
117 if (TP_IS_DBUS_TUBE_CHANNEL( pChannel))
119 SAL_INFO( "tubes", "accepting");
120 aAccepted = true;
122 TeleConference* pConference = new TeleConference( pAccount, TP_DBUS_TUBE_CHANNEL( pChannel ) );
123 pConference->acceptTube();
124 TeleManager::addConference( pConference );
126 else
128 SAL_INFO( "tubes", "ignored");
132 if (aAccepted)
133 tp_handle_channels_context_accept( pContext);
134 else
136 GError *pError = g_error_new_literal( TP_ERRORS, TP_ERROR_CONFUSED,
137 "None of these channels were LibreOffice D-Bus tubes; "
138 "why did the Channel Dispatcher give them to us?");
139 tp_handle_channels_context_fail( pContext, pError);
140 g_clear_error (&pError);
144 void TeleManager::addConference( TeleConference* pConference )
146 MutexGuard aGuard( GetMutex());
148 SAL_WARN_IF( pConference->getUuid().isEmpty(), "tubes",
149 "Adding conference with empty UUID should not happen!" );
150 pImpl->maAcceptedConferences[ pConference->getUuid() ] = pConference;
153 TeleConference* TeleManager::getConference()
155 MutexGuard aGuard( GetMutex());
157 TeleManagerImpl::MapStringConference::const_iterator it =
158 pImpl->maAcceptedConferences.find( pImpl->msCurrentUUID );
159 TeleConference* pConference = NULL;
160 if (it != pImpl->maAcceptedConferences.end())
161 pConference = it->second;
162 SAL_WARN_IF( !pConference, "tubes", "TeleManager::getConference: "
163 << pImpl->msCurrentUUID.getStr() << " not found!" );
164 pImpl->msCurrentUUID = OString();
165 return pConference;
168 void TeleManager::registerCollaboration( Collaboration* pCollaboration )
170 MutexGuard aGuard( GetMutex());
172 pImpl->maCollaborations.insert( pCollaboration );
175 void TeleManager::unregisterCollaboration( Collaboration* pCollaboration )
177 MutexGuard aGuard( GetMutex());
179 pImpl->maCollaborations.erase( pCollaboration );
182 bool TeleManager::existsCollaboration( Collaboration* pCollaboration )
184 MutexGuard aGuard( GetMutex());
186 return pImpl->maCollaborations.find( pCollaboration ) != pImpl->maCollaborations.end();
189 void TeleManager::displayAllContacts()
191 MutexGuard aGuard( GetMutex());
193 for (TeleManagerImpl::Collaborations::iterator it = pImpl->maCollaborations.begin();
194 it != pImpl->maCollaborations.end(); ++it)
195 (*it)->DisplayContacts();
198 void TeleManager::registerDemoConference( TeleConference* pConference )
200 MutexGuard aGuard( GetMutex());
202 pImpl->maDemoConferences.insert( pConference );
205 void TeleManager::unregisterDemoConference( TeleConference* pConference )
207 MutexGuard aGuard( GetMutex());
209 pImpl->maDemoConferences.erase( pConference );
212 void TeleManager::broadcastPacket( const OString& rPacket )
214 MutexGuard aGuard( GetMutex());
216 INFO_LOGGER_F( "TeleManager::broadcastPacket" );
217 for (TeleManagerImpl::DemoConferences::iterator it = pImpl->maDemoConferences.begin();
218 it != pImpl->maDemoConferences.end(); ++it)
219 if ((*it)->getCollaboration())
220 (*it)->getCollaboration()->PacketReceived( rPacket );
223 bool TeleManager::hasWaitingConference()
225 MutexGuard aGuard( GetMutex());
227 return !pImpl->msCurrentUUID.isEmpty();
230 void TeleManager::setCurrentUuid( const OString& rUuid )
232 MutexGuard aGuard( GetMutex());
234 pImpl->msCurrentUUID = rUuid;
237 // FIXME: should be static and not used in conference.cxx
238 void TeleManager_fileReceived( const OUString& rStr, const OString& rUuid )
240 SAL_INFO( "tubes", "TeleManager_fileReceived: incoming file: " << rStr );
242 OString sUuid( rUuid );
243 if (sUuid == "demo")
245 sUuid = TeleManager::createUuid();
246 TeleConference* pConference = new TeleConference( NULL, NULL, sUuid );
247 TeleManager::addConference( pConference );
248 TeleManager::registerDemoConference( pConference );
250 TeleManager::setCurrentUuid( sUuid );
254 css::uno::Reference < css::frame::XDesktop2 > xLoader = css::frame::Desktop::create(
255 ::comphelper::getProcessComponentContext() );
256 css::uno::Sequence < css::beans::PropertyValue > args(0);
257 css::uno::Reference < css::util::XCloseable > xDoc(
258 xLoader->loadComponentFromURL( rStr, "_blank", 0, args ),
259 css::uno::UNO_QUERY_THROW );
261 catch ( const css::uno::Exception& e )
263 // Expected to happen for unit test
264 SAL_WARN( "tubes", "TeleManager_fileReceived: exception when loading: " << e.Message );
268 static void TeleManager_TransferDone( EmpathyFTHandler *handler, TpFileTransferChannel *, gpointer )
270 SAL_INFO( "tubes", "TeleManager_TransferDone: hooray!");
271 GFile *gfile = empathy_ft_handler_get_gfile( handler);
272 char *uri = g_file_get_uri( gfile);
273 OUString aUri( OUString::createFromAscii( uri ) );
274 g_free( uri);
276 TeleManager_fileReceived( aUri, empathy_ft_handler_get_description( handler ) );
278 g_object_unref( handler);
281 static void TeleManager_TransferError( EmpathyFTHandler *handler, const GError *error, void*)
283 SAL_INFO( "tubes", "TeleManager_TransferError: " << error->message);
285 g_object_unref( handler);
288 static void lcl_IncomingHandlerReady (
289 EmpathyFTHandler* pHandler,
290 GError* pError,
291 void* /*pUserData*/ )
293 if (pError)
295 SAL_INFO ("tubes", "failed to prepare incoming transfer: " << pError->message);
296 g_object_unref( pHandler);
297 return;
300 /* The filename suggested by the sender, which in our case is the last bit
301 * of whatever URI got passed to ::sendFile()
303 const char* pFileName = empathy_ft_handler_get_filename( pHandler);
304 char* pLocalUri = g_strdup_printf( "file:///tmp/LibreOffice-collab-%s", pFileName);
305 GFile *pDestination = g_file_new_for_uri( pLocalUri);
306 g_free( pLocalUri);
308 empathy_ft_handler_incoming_set_destination( pHandler, pDestination);
309 g_object_unref( pDestination);
311 g_signal_connect( pHandler, "transfer-done", G_CALLBACK (TeleManager_TransferDone), NULL);
312 g_signal_connect( pHandler, "transfer-error", G_CALLBACK (TeleManager_TransferError), NULL);
313 SAL_INFO ("tubes", "lcl_IncomingHandlerReady: starting file transfer..");
314 empathy_ft_handler_start_transfer( pHandler);
317 static void TeleManager_FileTransferHandler(
318 TpSimpleHandler* /*handler*/,
319 TpAccount* /*Account*/,
320 TpConnection* /*connection*/,
321 GList* pChannels,
322 GList* /*requests_satisfied*/,
323 gint64 /*user_action_time*/,
324 TpHandleChannelsContext* pContext,
325 gpointer /*pUserData*/ )
327 bool aAccepted = false;
328 INFO_LOGGER_F( "TeleManager_FileTransferHandler");
330 for (GList* p = pChannels; p; p = p->next)
332 TpChannel* pChannel = TP_CHANNEL(p->data);
334 SAL_INFO( "tubes", "TeleManager_FileTransferHandler: incoming dbus channel: "
335 << tp_channel_get_identifier( pChannel));
337 if (TP_IS_FILE_TRANSFER_CHANNEL( pChannel))
339 SAL_INFO( "tubes", "accepting file transfer");
340 empathy_ft_handler_new_incoming( TP_FILE_TRANSFER_CHANNEL( pChannel),
341 lcl_IncomingHandlerReady, NULL);
342 aAccepted = true;
344 else
346 SAL_INFO( "tubes", "ignored");
350 if (aAccepted)
351 tp_handle_channels_context_accept( pContext);
352 else
354 GError *pError = g_error_new_literal( TP_ERRORS, TP_ERROR_CONFUSED,
355 "None of these channels were file transfers; "
356 "why did the Channel Dispatcher give them to us?");
357 tp_handle_channels_context_fail( pContext, pError);
358 g_clear_error (&pError);
362 void TeleManagerImpl::ChannelReadyHandler(
363 GObject* pSourceObject,
364 GAsyncResult* pResult,
365 gpointer pUserData
368 INFO_LOGGER_F( "TeleManagerImpl::ChannelReadyHandler");
370 TeleConference* pConference = reinterpret_cast<TeleConference*>(pUserData);
371 SAL_WARN_IF( !pConference, "tubes", "TeleManagerImpl::ChannelReadyHandler: no conference");
372 if (!pConference)
373 return;
375 mbChannelReadyHandlerInvoked = true;
377 TpAccountChannelRequest* pChannelRequest = TP_ACCOUNT_CHANNEL_REQUEST( pSourceObject);
378 GError* pError = NULL;
379 TpChannel * pChannel = tp_account_channel_request_create_and_handle_channel_finish(
380 pChannelRequest, pResult, NULL, &pError);
381 if (!pChannel)
383 // "account isn't Enabled" means just that..
384 /* FIXME: detect and handle, domain=132, code=3 */
385 SAL_WARN( "tubes", "TeleManagerImpl::ChannelReadyHandler: no channel: " << pError->message);
386 g_error_free( pError);
387 return;
389 pConference->setChannel( tp_account_channel_request_get_account( pChannelRequest),
390 TP_DBUS_TUBE_CHANNEL (pChannel));
391 pConference->offerTube();
394 void TeleManagerImpl::AccountManagerReadyHandler(
395 GObject* pSourceObject,
396 GAsyncResult* pResult,
397 gpointer /*pUserData*/
400 INFO_LOGGER_F( "TeleManagerImpl::AccountManagerReadyHandler");
402 GError* pError = NULL;
403 gboolean bPrepared = tp_proxy_prepare_finish( pSourceObject, pResult, &pError);
404 SAL_WARN_IF( !bPrepared, "tubes", "TeleManagerImpl::AccountManagerReadyHandler: not prepared");
405 if (!bPrepared || pError)
407 SAL_WARN_IF( pError, "tubes", "TeleManagerImpl::AccountManagerReadyHandler: error: " << pError->message);
408 g_error_free( pError);
411 mbAccountManagerReady = bPrepared;
412 mbAccountManagerReadyHandlerInvoked = true;
415 bool TeleManager::init( bool bListen )
417 if (createAccountManager())
419 if (bListen && !registerClients())
420 SAL_WARN( "tubes", "TeleManager::init: Could not register client handlers." );
422 return true;
424 else
425 SAL_WARN( "tubes", "TeleManager::init: Could not create AccountManager." );
427 return false;
430 void TeleManager::finalize()
432 delete pImpl;
435 bool TeleManager::createAccountManager()
437 INFO_LOGGER_F( "TeleManager::createAccountManager");
439 MutexGuard aGuard( GetMutex());
441 SAL_INFO_IF( pImpl->mpAccountManager, "tubes", "TeleManager::createAccountManager: already connected");
442 if (pImpl->mpAccountManager)
443 return true;
445 GError* pError = NULL;
446 TpDBusDaemon *pDBus = tp_dbus_daemon_dup( &pError);
447 SAL_WARN_IF( !pDBus, "tubes", "TeleManager::createAccountManager: no dbus daemon");
448 if (!pDBus || pError)
450 SAL_WARN_IF( pError, "tubes", "TeleManager::createAccountManager: dbus daemon error: " << pError->message);
451 g_error_free( pError);
452 return false;
455 pImpl->mpFactory = TP_SIMPLE_CLIENT_FACTORY( tp_automatic_client_factory_new( pDBus));
456 g_object_unref( pDBus);
457 SAL_WARN_IF( !pImpl->mpFactory, "tubes", "TeleManager::createAccountManager: no client factory");
458 if (!pImpl->mpFactory)
459 return false;
461 /* Tell the client factory (which creates and prepares proxy objects) to
462 * get the features we need ready before giving us any objects.
464 /* We need every online account's connection object to be available... */
465 tp_simple_client_factory_add_account_features_varargs (pImpl->mpFactory,
466 TP_ACCOUNT_FEATURE_CONNECTION,
468 /* ...and we want those connection objects to have the contact list
469 * available... */
470 tp_simple_client_factory_add_connection_features_varargs (pImpl->mpFactory,
471 TP_CONNECTION_FEATURE_CONTACT_LIST,
473 /* ...and those contacts should have their alias and their capabilities
474 * available.
476 tp_simple_client_factory_add_contact_features_varargs (pImpl->mpFactory,
477 TP_CONTACT_FEATURE_ALIAS,
478 TP_CONTACT_FEATURE_AVATAR_DATA,
479 TP_CONTACT_FEATURE_CAPABILITIES,
480 TP_CONTACT_FEATURE_PRESENCE,
481 TP_CONTACT_FEATURE_INVALID);
483 pImpl->mpAccountManager = tp_account_manager_new_with_factory (pImpl->mpFactory);
484 tp_account_manager_set_default (pImpl->mpAccountManager);
486 pImpl->mbAccountManagerReadyHandlerInvoked = false;
487 tp_proxy_prepare_async( pImpl->mpAccountManager, NULL, TeleManagerImpl::AccountManagerReadyHandler, NULL);
488 while (!pImpl->mbAccountManagerReadyHandlerInvoked)
489 g_main_context_iteration( NULL, TRUE);
491 return pImpl->mbAccountManagerReady;
494 bool TeleManager::registerClients()
496 INFO_LOGGER_F( "TeleManager::registerClients");
498 MutexGuard aGuard( GetMutex());
500 /* TODO: also check whether client could be registered and retry if not? */
501 SAL_INFO_IF( pImpl->mpClient && pImpl->mpFileTransferClient, "tubes", "TeleManager::registerClients: already registered");
502 if (pImpl->mpClient && pImpl->mpFileTransferClient)
503 return true;
505 pImpl->mpClient = tp_simple_handler_new_with_factory(
506 pImpl->mpFactory, // factory
507 FALSE, // bypass_approval
508 FALSE, // requests
509 getFullClientName().getStr(), // name
510 FALSE, // uniquify
511 TeleManager_DBusChannelHandler, // callback
512 NULL, // user_data
513 NULL // destroy
515 SAL_WARN_IF( !pImpl->mpClient, "tubes", "TeleManager::registerClients: no client");
516 if (!pImpl->mpClient)
517 return false;
519 // Setup client handler for buddy channels with our service.
520 tp_base_client_take_handler_filter( pImpl->mpClient,
521 tp_asv_new(
522 TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, TP_IFACE_CHANNEL_TYPE_DBUS_TUBE,
523 TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_CONTACT,
524 TP_PROP_CHANNEL_TYPE_DBUS_TUBE_SERVICE_NAME, G_TYPE_STRING, getFullServiceName().getStr(),
525 NULL));
527 /* TODO: setup filters for LibreOfficeCalc, LibreOfficeWriter, ... */
529 // Setup client handler for MUC channels with our service.
530 tp_base_client_take_handler_filter( pImpl->mpClient,
531 tp_asv_new(
532 TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, TP_IFACE_CHANNEL_TYPE_DBUS_TUBE,
533 TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_ROOM,
534 TP_PROP_CHANNEL_TYPE_DBUS_TUBE_SERVICE_NAME, G_TYPE_STRING, getFullServiceName().getStr(),
535 NULL));
537 GError* pError = NULL;
538 if (!tp_base_client_register( pImpl->mpClient, &pError))
540 SAL_WARN( "tubes", "TeleManager::registerClients: error registering client handler: " << pError->message);
541 g_error_free( pError);
542 return false;
545 SAL_INFO( "tubes", "TeleManager::registerClients: bus name: " << tp_base_client_get_bus_name( pImpl->mpClient));
546 SAL_INFO( "tubes", "TeleManager::registerClients: object path: " << tp_base_client_get_object_path( pImpl->mpClient));
548 /* Register a second "head" for incoming file transfers. This uses a more
549 * specific filter than Empathy's handler by matching on the file
550 * transfer's ServiceName property, and uses bypass_approval to ensure the
551 * user isn't prompted before the channel gets passed to us.
553 pImpl->mpFileTransferClient = tp_simple_handler_new_with_factory (
554 pImpl->mpFactory, // factory
555 TRUE, // bypass_approval
556 FALSE, // requests
557 getFullClientName().getStr(), // name
558 TRUE, // uniquify to get a different bus name to the main client, above
559 TeleManager_FileTransferHandler, // callback
560 NULL, // user_data
561 NULL // destroy
563 tp_base_client_take_handler_filter( pImpl->mpFileTransferClient,
564 tp_asv_new(
565 TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER,
566 TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_CONTACT,
567 TP_PROP_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA_SERVICE_NAME, G_TYPE_STRING, getFullServiceName().getStr(),
568 NULL));
570 if (!tp_base_client_register( pImpl->mpFileTransferClient, &pError))
572 /* This shouldn't fail if registering the main handler succeeded */
573 SAL_WARN( "tubes", "TeleManager::registerClients: error registering file transfer handler: " << pError->message);
574 g_error_free( pError);
575 return false;
578 return true;
581 TeleConference* TeleManager::startDemoSession()
583 INFO_LOGGER_F( "TeleManager::startDemoSession");
585 TeleConference* pConference = new TeleConference( NULL, NULL, "demo" );
586 registerDemoConference( pConference );
588 return pConference;
591 /* TODO: factor out common code with startBuddySession() */
592 TeleConference* TeleManager::startGroupSession( TpAccount *pAccount,
593 const OUString& rUConferenceRoom,
594 const OUString& rUConferenceServer )
596 INFO_LOGGER_F( "TeleManager::startGroupSession");
598 OString aSessionId( TeleManager::createUuid());
600 /* FIXME: does this work at all _creating_ a MUC? */
601 // Use conference and server if given, else create conference.
602 OString aConferenceRoom( OUStringToOString( rUConferenceRoom, RTL_TEXTENCODING_UTF8));
603 OString aConferenceServer( OUStringToOString( rUConferenceServer, RTL_TEXTENCODING_UTF8));
604 OStringBuffer aBuf(64);
605 if (!aConferenceRoom.isEmpty() && !aConferenceServer.isEmpty())
606 aBuf.append( aConferenceRoom).append( '@').append( aConferenceServer);
607 else
609 aBuf.append( aSessionId);
610 if (!aConferenceServer.isEmpty())
611 aBuf.append( '@').append( aConferenceServer);
612 /* FIXME: else? bail out? we have only a session ID without server then */
614 OString aTarget( aBuf.makeStringAndClear());
616 SAL_INFO( "tubes", "TeleManager::startGroupSession: creating channel request from "
617 << tp_account_get_path_suffix( pAccount ) << " to " << aTarget.getStr() );
619 // MUC request
620 GHashTable* pRequest = tp_asv_new(
621 TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, TP_IFACE_CHANNEL_TYPE_DBUS_TUBE,
622 TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, TP_TYPE_HANDLE, TP_HANDLE_TYPE_ROOM,
623 TP_PROP_CHANNEL_TARGET_ID, G_TYPE_STRING, aTarget.getStr(),
624 TP_PROP_CHANNEL_TYPE_DBUS_TUBE_SERVICE_NAME, G_TYPE_STRING, getFullServiceName().getStr(),
625 NULL);
627 TpAccountChannelRequest * pChannelRequest = tp_account_channel_request_new(
628 pAccount, pRequest, TP_USER_ACTION_TIME_NOT_USER_ACTION);
629 SAL_WARN_IF( !pChannelRequest, "tubes", "TeleManager::startGroupSession: no channel");
630 if (!pChannelRequest)
632 g_hash_table_unref( pRequest);
633 return NULL;
636 pImpl->mbChannelReadyHandlerInvoked = false;
638 TeleConference* pConference = new TeleConference( NULL, NULL, aSessionId );
640 tp_account_channel_request_create_and_handle_channel_async(
641 pChannelRequest, NULL, TeleManagerImpl::ChannelReadyHandler, pConference);
643 while (!pImpl->mbChannelReadyHandlerInvoked)
644 g_main_context_iteration( NULL, TRUE );
646 g_object_unref( pChannelRequest);
647 g_hash_table_unref( pRequest);
649 if (!pConference->isReady())
650 return NULL;
652 return pConference;
656 static void lcl_ensureLegacyChannel( TpAccount* pAccount, TpContact* pBuddy )
658 /* This is a workaround for a Telepathy bug.
659 * <https://bugs.freedesktop.org/show_bug.cgi?id=47760>. The first time you
660 * request a tube to a contact on an account, you actually get two channels
661 * back: the tube you asked for, along with a legacy Channel.Type.Tubes
662 * object. This breaks create_and_handle_channel_async(), which expects to
663 * only get one channel back.
665 * To work around this, we make sure the legacy Tubes channel already
666 * exists before we request the channel we actually want. We don't actually
667 * have to wait for this request to succeed—we fire it off and forget about
668 * it.
670 GHashTable* pRequest = tp_asv_new(
671 TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, TP_IFACE_CHANNEL_TYPE_TUBES,
672 TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, TP_TYPE_HANDLE, TP_HANDLE_TYPE_CONTACT,
673 TP_PROP_CHANNEL_TARGET_ID, G_TYPE_STRING, tp_contact_get_identifier (pBuddy),
674 NULL);
675 TpAccountChannelRequest* pChannelRequest = tp_account_channel_request_new(
676 pAccount, pRequest, TP_USER_ACTION_TIME_NOT_USER_ACTION);
677 tp_account_channel_request_ensure_channel_async( pChannelRequest, NULL,
678 NULL, NULL, NULL );
679 g_object_unref( pChannelRequest );
680 g_hash_table_unref( pRequest );
684 /* TODO: factor out common code with startGroupSession() */
685 TeleConference* TeleManager::startBuddySession( TpAccount *pAccount, TpContact *pBuddy )
687 INFO_LOGGER_F( "TeleManager::startBuddySession");
689 lcl_ensureLegacyChannel( pAccount, pBuddy );
691 const char *pIdentifier = tp_contact_get_identifier( pBuddy);
692 SAL_INFO( "tubes", "TeleManager::startBuddySession: creating channel request from "
693 << tp_account_get_path_suffix( pAccount)
694 << " to " << pIdentifier);
696 GHashTable* pRequest = tp_asv_new(
697 TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, TP_IFACE_CHANNEL_TYPE_DBUS_TUBE,
698 TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, TP_TYPE_HANDLE, TP_HANDLE_TYPE_CONTACT,
699 TP_PROP_CHANNEL_TARGET_ID, G_TYPE_STRING, pIdentifier,
700 TP_PROP_CHANNEL_TYPE_DBUS_TUBE_SERVICE_NAME, G_TYPE_STRING, getFullServiceName().getStr(),
701 NULL);
703 TpAccountChannelRequest * pChannelRequest = tp_account_channel_request_new(
704 pAccount, pRequest, TP_USER_ACTION_TIME_NOT_USER_ACTION);
705 SAL_WARN_IF( !pChannelRequest, "tubes", "TeleManager::startBuddySession: no channel");
706 if (!pChannelRequest)
708 g_hash_table_unref( pRequest);
709 return NULL;
712 pImpl->mbChannelReadyHandlerInvoked = false;
714 TeleConference* pConference = new TeleConference( NULL, NULL, createUuid(), true );
716 tp_account_channel_request_create_and_handle_channel_async(
717 pChannelRequest, NULL, TeleManagerImpl::ChannelReadyHandler, pConference );
719 while (!pImpl->mbChannelReadyHandlerInvoked)
720 g_main_context_iteration( NULL, TRUE );
722 g_object_unref( pChannelRequest);
723 g_hash_table_unref( pRequest);
725 if (!pConference->isReady())
726 return NULL;
728 return pConference;
731 static bool tb_presence_is_online( const TpConnectionPresenceType& presence )
733 switch (presence)
735 case TP_CONNECTION_PRESENCE_TYPE_UNSET:
736 case TP_CONNECTION_PRESENCE_TYPE_OFFLINE:
737 return false;
738 case TP_CONNECTION_PRESENCE_TYPE_AVAILABLE:
739 case TP_CONNECTION_PRESENCE_TYPE_AWAY:
740 case TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY:
741 case TP_CONNECTION_PRESENCE_TYPE_HIDDEN:
742 case TP_CONNECTION_PRESENCE_TYPE_BUSY:
743 return true;
744 case TP_CONNECTION_PRESENCE_TYPE_UNKNOWN:
745 case TP_CONNECTION_PRESENCE_TYPE_ERROR:
746 default:
747 return false;
751 static bool tb_contact_is_online( TpContact *contact )
753 return tb_presence_is_online (tp_contact_get_presence_type (contact));
756 static void presence_changed_cb( TpContact* /* contact */,
757 guint /* type */,
758 gchar* /* status */,
759 gchar* /* message */,
760 gpointer /* pContactList*/ )
762 TeleManager::displayAllContacts();
765 AccountContactPairV TeleManager::getContacts()
767 AccountContactPairV pairs;
769 for (GList *accounts = tp_account_manager_get_valid_accounts (pImpl->mpAccountManager);
770 accounts != NULL; accounts = g_list_delete_link (accounts, accounts))
772 TpAccount *account = reinterpret_cast<TpAccount *>(accounts->data);
773 TpConnection *connection = tp_account_get_connection (account);
775 /* Verify account is online and received its contact list. If state is not
776 * SUCCESS this means we didn't received the roster from server yet and
777 * we would have to wait for the "notify:contact-list-state" signal. */
778 if (connection == NULL || tp_connection_get_contact_list_state (connection) !=
779 TP_CONTACT_LIST_STATE_SUCCESS)
780 continue;
782 TpContact *self = tp_connection_get_self_contact (connection);
783 GPtrArray *contacts = tp_connection_dup_contact_list (connection);
784 for (guint i = 0; i < contacts->len; i++)
786 TpContact *contact = reinterpret_cast<TpContact *>(g_ptr_array_index (contacts, i));
787 if (pImpl->maRegisteredContacts.find (contact) == pImpl->maRegisteredContacts.end())
789 pImpl->maRegisteredContacts.insert (contact);
790 g_signal_connect (contact, "presence-changed",
791 G_CALLBACK (presence_changed_cb), NULL );
793 if (contact != self && tb_contact_is_online (contact))
795 g_object_ref (account);
796 g_object_ref (contact);
798 AccountContactPair pair(account, contact);
799 pairs.push_back(pair);
802 g_ptr_array_unref (contacts);
805 return pairs;
808 OString TeleManager::getFullClientName()
810 OStringBuffer aBuf(64);
811 aBuf.append( LIBO_CLIENT_SUFFIX ).append( pImpl->msNameSuffix);
812 return aBuf.makeStringAndClear();
815 OString TeleManager::getFullServiceName()
817 OStringBuffer aBuf(64);
818 aBuf.append( LIBO_DTUBE_SERVICE ).append( pImpl->msNameSuffix);
819 return aBuf.makeStringAndClear();
822 OString TeleManager::getFullObjectPath()
824 OStringBuffer aBuf(64);
825 aBuf.append( '/').append( LIBO_DTUBE_SERVICE ).append( pImpl->msNameSuffix);
826 OString aStr( aBuf.makeStringAndClear().replace( '.', '/'));
827 return aStr;
830 OString TeleManager::createUuid()
832 sal_uInt8 nId[16];
833 rtl_createUuid( nId, 0, sal_True);
834 char aBuf[33];
835 for (size_t i=0; i<16; ++i)
837 snprintf( aBuf+2*i, 3, "%02x", (unsigned char)nId[i]);
839 aBuf[32] = 0;
840 return OString( aBuf);
843 Mutex& TeleManager::GetMutex()
845 static Mutex* pMutex = NULL;
846 if (!pMutex)
848 MutexGuard aGuard( Mutex::getGlobalMutex());
849 if (!pMutex)
850 pMutex = new Mutex;
852 return *pMutex;
855 void TeleManager::addSuffixToNames( const char* pName )
857 pImpl->msNameSuffix = pName;
860 // ===========================================================================
862 TeleManagerImpl::TeleManagerImpl()
864 mpFactory( NULL),
865 mpClient( NULL),
866 mpFileTransferClient( NULL),
867 mpAccountManager( NULL)
869 #if !GLIB_CHECK_VERSION(2,36,0)
870 g_type_init();
871 #endif
874 TeleManagerImpl::~TeleManagerImpl()
876 // There may be unused conferences left opened, so close them.
877 // It should not make a problem to close already closed conference.
878 for (MapStringConference::iterator it = maAcceptedConferences.begin();
879 it != maAcceptedConferences.end(); ++it)
880 it->second->close();
881 if (mpClient)
883 tp_base_client_unregister( mpClient);
884 g_object_unref( mpClient);
886 if (mpFileTransferClient)
888 tp_base_client_unregister( mpFileTransferClient);
889 g_object_unref( mpFileTransferClient);
891 if (mpFactory)
892 g_object_unref( mpFactory);
893 if (mpAccountManager)
894 g_object_unref( mpAccountManager);
897 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */