1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
10 #include <tubes/conference.hxx>
12 #include <tubes/collaboration.hxx>
13 #include <tubes/constants.h>
14 #include <tubes/file-transfer-helper.h>
15 #include <tubes/manager.hxx>
18 #include <telepathy-glib/telepathy-glib.h>
20 #if defined SAL_LOG_INFO
27 explicit InfoLogger( const void* pThat
, const char* pMethod
)
32 SAL_INFO( "tubes.method", mpThat
<< " entering " << mpMethod
);
36 SAL_INFO( "tubes.method", mpThat
<< " leaving " << mpMethod
);
40 #define INFO_LOGGER_F(s) InfoLogger aLogger(0,(s))
41 #define INFO_LOGGER(s) InfoLogger aLogger(this,(s))
43 #define INFO_LOGGER_F(s)
44 #define INFO_LOGGER(s)
45 #endif // SAL_LOG_INFO
47 class TeleConferenceImpl
50 guint maObjectRegistrationId
;
51 GDBusConnection
* mpTube
;
52 bool mbTubeOfferedHandlerInvoked
: 1;
54 TeleConferenceImpl() :
56 mbTubeOfferedHandlerInvoked( false )
59 ~TeleConferenceImpl() {}
62 static void TeleConference_MethodCallHandler(
63 GDBusConnection
* /*pConnection*/,
65 const gchar
* /*pObjectPath*/,
66 const gchar
* pInterfaceName
,
67 const gchar
* pMethodName
,
68 GVariant
* pParameters
,
69 GDBusMethodInvocation
* pInvocation
,
72 INFO_LOGGER_F( "TeleConference_MethodCallHandler");
74 TeleConference
* pConference
= reinterpret_cast<TeleConference
*>(pUserData
);
75 SAL_WARN_IF( !pConference
, "tubes", "TeleConference_MethodCallHandler: no conference");
79 if (tp_strdiff (pMethodName
, LIBO_TUBES_DBUS_MSG_METHOD
))
81 g_dbus_method_invocation_return_error ( pInvocation
,
82 G_DBUS_ERROR
, G_DBUS_ERROR_UNKNOWN_METHOD
,
83 "Unknown method '%s' on interface %s",
84 pMethodName
, pInterfaceName
);
88 if (!g_variant_is_of_type ( pParameters
, G_VARIANT_TYPE ("(ay)")))
90 g_dbus_method_invocation_return_error ( pInvocation
,
91 G_DBUS_ERROR
, G_DBUS_ERROR_INVALID_ARGS
,
92 "'%s' takes an array of bytes, not %s",
94 g_variant_get_type_string (pParameters
));
99 g_variant_get( pParameters
, "(@ay)", &ay
);
100 const char* pPacketData
= reinterpret_cast<const char*>( g_variant_get_data( ay
));
101 gsize nPacketSize
= g_variant_get_size( ay
);
103 SAL_WARN_IF( !pPacketData
, "tubes", "TeleConference_MethodCallHandler: couldn't get packet data");
107 SAL_INFO( "tubes", "TeleConference_MethodCallHandler: received packet from sender "
108 << (pSender
? pSender
: "(null)") << " with size " << nPacketSize
);
109 OString
aPacket( pPacketData
, nPacketSize
);
110 if (pConference
->getCollaboration())
111 pConference
->getCollaboration()->PacketReceived( aPacket
);
112 // Master needs to send the packet back to impose ordering,
113 // so the slave can execute his command.
114 if (pConference
->isMaster())
115 pConference
->sendPacket( aPacket
);
117 g_dbus_method_invocation_return_value( pInvocation
, 0 );
119 g_variant_unref( ay
);
123 static void TeleConference_ChannelCloseHandler(
124 TpChannel
* /*proxy*/,
125 const GError
* pError
,
127 GObject
* /*weak_object*/
130 INFO_LOGGER_F( "TeleConference_ChannelCloseHandler");
132 TeleConference
* pConference
= reinterpret_cast<TeleConference
*>(pUserData
);
133 SAL_WARN_IF( !pConference
, "tubes", "TeleConference_ChannelCloseHandler: no conference");
139 SAL_WARN( "tubes", "TeleConference_ChannelCloseHandler: entered with error: " << pError
->message
);
140 pConference
->finalize();
144 pConference
->finalize();
148 static void TeleConference_TubeOfferedHandler(
150 GAsyncResult
* pResult
,
153 INFO_LOGGER_F( "TeleConference_TubeOfferedHandler");
155 TeleConference
* pConference
= reinterpret_cast<TeleConference
*>(pUserData
);
156 SAL_WARN_IF( !pConference
, "tubes", "TeleConference_TubeOfferedHandler: no conference");
160 pConference
->setTubeOfferedHandlerInvoked( true);
162 TpDBusTubeChannel
* pChannel
= TP_DBUS_TUBE_CHANNEL( pSource
);
163 GError
* pError
= NULL
;
164 GDBusConnection
* pTube
= tp_dbus_tube_channel_offer_finish(
165 pChannel
, pResult
, &pError
);
167 // "can't find contact ... presence" means contact is not a contact.
168 /* FIXME: detect and handle */
169 SAL_WARN_IF( !pTube
, "tubes", "TeleConference_TubeOfferedHandler: entered with error: " << pError
->message
);
171 g_error_free( pError
);
175 pConference
->setTube( pTube
);
179 static void TeleConference_TubeAcceptedHandler(
181 GAsyncResult
* pResult
,
184 INFO_LOGGER_F( "TeleConference_TubeAcceptedHandler");
186 TeleConference
* pConference
= reinterpret_cast<TeleConference
*>(pUserData
);
187 SAL_WARN_IF( !pConference
, "tubes", "TeleConference_TubeAcceptedHandler: no conference");
191 pConference
->setTubeOfferedHandlerInvoked( true);
193 TpDBusTubeChannel
* pChannel
= TP_DBUS_TUBE_CHANNEL( pSource
);
194 GError
* pError
= NULL
;
195 GDBusConnection
* pTube
= tp_dbus_tube_channel_accept_finish(
196 pChannel
, pResult
, &pError
);
198 SAL_WARN_IF( !pTube
, "tubes", "TeleConference_TubeAcceptedHandler: entered with error: " << pError
->message
);
200 g_error_free( pError
);
203 GHashTable
* pParameters
= tp_dbus_tube_channel_get_parameters( pChannel
);
204 const char* sUuid
= tp_asv_get_string( pParameters
, LIBO_TUBES_UUID
);
205 pConference
->setUuid( OString( sUuid
));
207 pConference
->setTube( pTube
);
211 TeleConference::TeleConference( TpAccount
* pAccount
,
212 TpDBusTubeChannel
* pChannel
, const OString sUuid
, bool bMaster
)
214 mpCollaboration( NULL
),
219 pImpl( new TeleConferenceImpl() )
221 setChannel( pAccount
, pChannel
);
225 TeleConference::~TeleConference()
227 // We're destructed from finalize()
231 static void channel_closed_cb( TpChannel
*channel
, gpointer user_data
, GObject
* /* weak_object */ )
233 Collaboration
* pCollaboration
= reinterpret_cast<Collaboration
*> (user_data
);
234 if (TeleManager::existsCollaboration( pCollaboration
))
236 GtkWidget
*dialog
= gtk_message_dialog_new( NULL
, static_cast<GtkDialogFlags
> (0),
237 GTK_MESSAGE_WARNING
, GTK_BUTTONS_CLOSE
,
238 "Contact %s lost, you'll now be working locally.",
239 tp_contact_get_alias (tp_channel_get_target_contact (channel
)) );
240 g_signal_connect_swapped (dialog
, "response",
241 G_CALLBACK (gtk_widget_destroy
), dialog
);
242 gtk_widget_show_all (dialog
);
244 pCollaboration
->EndCollaboration();
249 void TeleConference::setChannel( TpAccount
*pAccount
, TpDBusTubeChannel
* pChannel
)
251 OSL_ENSURE( !mpChannel
, "TeleConference::setChannel: already have channel");
253 g_object_unref( mpChannel
);
255 g_object_unref( mpAccount
);
257 mpChannel
= pChannel
;
259 g_object_ref( mpChannel
);
261 mpAccount
= pAccount
;
263 g_object_ref( mpAccount
);
267 bool TeleConference::spinUntilTubeEstablished()
269 while (!isTubeOfferedHandlerInvoked())
271 g_main_context_iteration( NULL
, TRUE
);
274 bool bOpen
= pImpl
->mpTube
!= NULL
;
275 SAL_INFO( "tubes", "TeleConference::spinUntilTubeEstablished: tube open: " << bOpen
);
280 bool TeleConference::acceptTube()
282 INFO_LOGGER( "TeleConference::acceptTube");
284 SAL_WARN_IF( !mpChannel
, "tubes", "TeleConference::acceptTube: no channel setup");
285 SAL_WARN_IF( pImpl
->mpTube
, "tubes", "TeleConference::acceptTube: already tubed");
286 if (!mpChannel
|| pImpl
->mpTube
)
289 tp_dbus_tube_channel_accept_async( mpChannel
,
290 TeleConference_TubeAcceptedHandler
,
292 return spinUntilTubeEstablished();
296 bool TeleConference::offerTube()
298 INFO_LOGGER( "TeleConference::offerTube");
300 OSL_ENSURE( mpChannel
, "TeleConference::offerTube: no channel");
304 GHashTable
* pParameters
= tp_asv_new (
305 LIBO_TUBES_UUID
, G_TYPE_STRING
, msUuid
.getStr(),
308 tp_dbus_tube_channel_offer_async(
311 TeleConference_TubeOfferedHandler
,
314 return spinUntilTubeEstablished();
318 bool TeleConference::setTube( GDBusConnection
* pTube
)
320 INFO_LOGGER( "TeleConference::setTube");
322 OSL_ENSURE( !pImpl
->mpTube
, "TeleConference::setTube: already tubed");
324 pImpl
->mpTube
= pTube
;
326 GDBusNodeInfo
*introspection_data
;
327 static const GDBusInterfaceVTable interface_vtable
=
329 TeleConference_MethodCallHandler
,
334 static const gchar introspection_xml
[] =
336 " <interface name='" LIBO_TUBES_DBUS_INTERFACE
"'>"
337 " <method name='" LIBO_TUBES_DBUS_MSG_METHOD
"'>"
338 " <arg type='ay' name='packet' direction='in'/>"
343 introspection_data
= g_dbus_node_info_new_for_xml (introspection_xml
, NULL
);
344 g_assert (introspection_data
!= NULL
);
346 pImpl
->maObjectRegistrationId
= g_dbus_connection_register_object( pImpl
->mpTube
,
347 LIBO_TUBES_DBUS_PATH
, introspection_data
->interfaces
[0],
348 &interface_vtable
, this, NULL
, NULL
);
349 g_assert (pImpl
->maObjectRegistrationId
> 0);
351 g_dbus_node_info_unref (introspection_data
);
356 void TeleConference::setTubeOfferedHandlerInvoked( bool b
)
358 pImpl
->mbTubeOfferedHandlerInvoked
= b
;
361 bool TeleConference::isTubeOfferedHandlerInvoked() const
363 return pImpl
->mbTubeOfferedHandlerInvoked
;
366 bool TeleConference::isReady() const
368 return mpChannel
&& pImpl
->mpTube
;
371 void TeleConference::close()
373 INFO_LOGGER( "TeleConference::close");
376 tp_cli_channel_call_close( TP_CHANNEL( mpChannel
), 5000, TeleConference_ChannelCloseHandler
, this, NULL
, NULL
);
382 void TeleConference::finalize()
384 INFO_LOGGER( "TeleConference::finalize");
386 TeleManager::unregisterDemoConference( this );
390 g_object_unref( mpChannel
);
396 g_object_unref( mpAccount
);
402 g_dbus_connection_unregister_object( pImpl
->mpTube
, pImpl
->maObjectRegistrationId
);
403 g_dbus_connection_close_sync( pImpl
->mpTube
, NULL
, NULL
);
404 g_object_unref( pImpl
->mpTube
);
405 pImpl
->mpTube
= NULL
;
408 //! *this gets destructed here!
412 bool TeleConference::sendPacket( const OString
& rPacket
)
414 INFO_LOGGER( "TeleConference::sendPacket");
416 if (!mpChannel
&& !pImpl
->mpTube
)
418 TeleManager::broadcastPacket( rPacket
);
422 SAL_WARN_IF( !pImpl
->mpTube
, "tubes", "TeleConference::sendPacket: no tube");
426 /* FIXME: in GLib 2.32 we can use g_variant_new_fixed_array(). It does
429 void* pData
= g_memdup( rPacket
.getStr(), rPacket
.getLength() );
430 GVariant
*pParameters
= g_variant_new_from_data( G_VARIANT_TYPE("(ay)"),
431 pData
, rPacket
.getLength(),
435 g_dbus_connection_call( pImpl
->mpTube
,
436 NULL
, /* bus name; in multi-user case we'd address this to the master. */
437 LIBO_TUBES_DBUS_PATH
,
438 LIBO_TUBES_DBUS_INTERFACE
,
439 LIBO_TUBES_DBUS_MSG_METHOD
,
440 pParameters
, /* consumes the floating reference */
442 G_DBUS_CALL_FLAGS_NONE
,
443 -1, NULL
, NULL
, NULL
);
445 // If we started the session, we can execute commands immediately.
446 if (mbMaster
&& mpCollaboration
)
447 mpCollaboration
->PacketReceived( rPacket
);
452 bool TeleConference::isMaster() const
457 Collaboration
* TeleConference::getCollaboration() const
459 return mpCollaboration
;
462 void TeleConference::setCollaboration( Collaboration
* pCollaboration
)
464 mpCollaboration
= pCollaboration
;
467 GError
*error
= NULL
;
468 if (!tp_cli_channel_connect_to_closed( TP_CHANNEL (mpChannel
),
469 channel_closed_cb
, mpCollaboration
, NULL
, NULL
, &error
))
471 SAL_WARN( "tubes", "Error when connecting to signal closed: " << error
->message
);
472 g_error_free (error
);
477 void TeleConference::invite( TpContact
*pContact
)
479 INFO_LOGGER( "TeleConference::invite" );
480 TpHandle aHandle
= tp_contact_get_handle( pContact
);
481 GArray handles
= { reinterpret_cast<gchar
*> (&aHandle
), 1 };
482 tp_cli_channel_interface_group_call_add_members( TP_CHANNEL( mpChannel
),
483 -1, &handles
, NULL
, NULL
, NULL
, NULL
, NULL
);
487 class SendFileRequest
{
489 SendFileRequest( TeleConference::FileSentCallback pCallback
, void* pUserData
, const char* sUuid
)
490 : mpCallback(pCallback
)
491 , mpUserData(pUserData
)
495 TeleConference::FileSentCallback mpCallback
;
501 static void TeleConference_TransferDone( EmpathyFTHandler
*handler
, TpFileTransferChannel
*, gpointer user_data
)
503 SendFileRequest
*request
= reinterpret_cast<SendFileRequest
*>(user_data
);
505 if (request
->mpCallback
)
506 request
->mpCallback(true, request
->mpUserData
);
508 g_object_unref (handler
);
511 static void TeleConference_TransferError( EmpathyFTHandler
*handler
, const GError
*error
, gpointer user_data
)
513 SendFileRequest
*request
= reinterpret_cast<SendFileRequest
*>(user_data
);
515 SAL_INFO( "tubes", "TeleConference_TransferError: " << error
->message
);
517 if (request
->mpCallback
)
518 request
->mpCallback(false, request
->mpUserData
);
520 g_object_unref (handler
);
523 static void TeleConference_FTReady( EmpathyFTHandler
*handler
, GError
*error
, gpointer user_data
)
525 SendFileRequest
*request
= reinterpret_cast<SendFileRequest
*>(user_data
);
529 if (request
->mpCallback
)
530 request
->mpCallback(error
== 0, request
->mpUserData
);
532 g_object_unref (handler
);
536 g_signal_connect(handler
, "transfer-done",
537 G_CALLBACK (TeleConference_TransferDone
), request
);
538 g_signal_connect(handler
, "transfer-error",
539 G_CALLBACK (TeleConference_TransferError
), request
);
540 empathy_ft_handler_set_service_name(handler
, TeleManager::getFullServiceName().getStr());
541 empathy_ft_handler_set_description(handler
, request
->msUuid
);
542 empathy_ft_handler_start_transfer(handler
);
546 // TODO: move sending file to TeleManager
547 extern void TeleManager_fileReceived( const OUString
&, const OString
& );
548 void TeleConference::sendFile( TpContact
* pContact
, const OUString
& localUri
, FileSentCallback pCallback
, void* pUserData
)
550 INFO_LOGGER( "TeleConference::sendFile");
555 TeleManager_fileReceived( localUri
, "demo" );
559 SAL_WARN_IF( ( !mpAccount
|| !mpChannel
), "tubes",
560 "can't send a file before the tube is set up");
561 if ( !mpAccount
|| !mpChannel
)
564 GFile
*pSource
= g_file_new_for_uri(
565 OUStringToOString( localUri
, RTL_TEXTENCODING_UTF8
).getStr() );
566 SendFileRequest
*pReq
= new SendFileRequest( pCallback
, pUserData
, msUuid
.getStr() );
568 empathy_ft_handler_new_outgoing( mpAccount
,
572 TeleConference_FTReady
, pReq
);
575 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */