bump product version to 4.1.6.2
[LibreOffice.git] / tubes / source / conference.cxx
blob86986fc760fb8ad7a16323a36b1010769182a3ee
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/conference.hxx>
12 #include <tubes/collaboration.hxx>
13 #include <tubes/constants.h>
14 #include <tubes/file-transfer-helper.h>
15 #include <tubes/manager.hxx>
17 #include <gtk/gtk.h>
18 #include <telepathy-glib/telepathy-glib.h>
20 #if defined SAL_LOG_INFO
21 namespace
23 struct InfoLogger
25 const void* mpThat;
26 const char* mpMethod;
27 explicit InfoLogger( const void* pThat, const char* pMethod )
29 mpThat( pThat),
30 mpMethod( pMethod)
32 SAL_INFO( "tubes.method", mpThat << " entering " << mpMethod);
34 ~InfoLogger()
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))
42 #else
43 #define INFO_LOGGER_F(s)
44 #define INFO_LOGGER(s)
45 #endif // SAL_LOG_INFO
47 class TeleConferenceImpl
49 public:
50 guint maObjectRegistrationId;
51 GDBusConnection* mpTube;
52 bool mbTubeOfferedHandlerInvoked : 1;
54 TeleConferenceImpl() :
55 mpTube( NULL ),
56 mbTubeOfferedHandlerInvoked( false )
59 ~TeleConferenceImpl() {}
62 static void TeleConference_MethodCallHandler(
63 GDBusConnection* /*pConnection*/,
64 const gchar* pSender,
65 const gchar* /*pObjectPath*/,
66 const gchar* pInterfaceName,
67 const gchar* pMethodName,
68 GVariant* pParameters,
69 GDBusMethodInvocation* pInvocation,
70 void* pUserData)
72 INFO_LOGGER_F( "TeleConference_MethodCallHandler");
74 TeleConference* pConference = reinterpret_cast<TeleConference*>(pUserData);
75 SAL_WARN_IF( !pConference, "tubes", "TeleConference_MethodCallHandler: no conference");
76 if (!pConference)
77 return;
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 );
85 return;
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",
93 pMethodName,
94 g_variant_get_type_string (pParameters));
95 return;
98 GVariant *ay;
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");
104 if (!pPacketData)
105 return;
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,
126 gpointer pUserData,
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");
134 if (!pConference)
135 return;
137 if (pError)
139 SAL_WARN( "tubes", "TeleConference_ChannelCloseHandler: entered with error: " << pError->message);
140 pConference->finalize();
141 return;
144 pConference->finalize();
148 static void TeleConference_TubeOfferedHandler(
149 GObject* pSource,
150 GAsyncResult* pResult,
151 gpointer pUserData)
153 INFO_LOGGER_F( "TeleConference_TubeOfferedHandler");
155 TeleConference* pConference = reinterpret_cast<TeleConference*>(pUserData);
156 SAL_WARN_IF( !pConference, "tubes", "TeleConference_TubeOfferedHandler: no conference");
157 if (!pConference)
158 return;
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);
170 if (pError) {
171 g_error_free( pError);
172 return;
175 pConference->setTube( pTube);
179 static void TeleConference_TubeAcceptedHandler(
180 GObject* pSource,
181 GAsyncResult* pResult,
182 gpointer pUserData)
184 INFO_LOGGER_F( "TeleConference_TubeAcceptedHandler");
186 TeleConference* pConference = reinterpret_cast<TeleConference*>(pUserData);
187 SAL_WARN_IF( !pConference, "tubes", "TeleConference_TubeAcceptedHandler: no conference");
188 if (!pConference)
189 return;
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);
199 if (pError) {
200 g_error_free( pError);
201 return;
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 ),
215 mpAccount( NULL ),
216 mpChannel( NULL ),
217 msUuid( sUuid ),
218 mbMaster( bMaster ),
219 pImpl( new TeleConferenceImpl() )
221 setChannel( pAccount, pChannel );
225 TeleConference::~TeleConference()
227 // We're destructed from finalize()
228 delete pImpl;
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");
252 if (mpChannel)
253 g_object_unref( mpChannel);
254 if (mpAccount)
255 g_object_unref( mpAccount);
257 mpChannel = pChannel;
258 if (mpChannel)
259 g_object_ref( mpChannel);
261 mpAccount = pAccount;
262 if (mpAccount)
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);
276 return 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)
287 return false;
289 tp_dbus_tube_channel_accept_async( mpChannel,
290 TeleConference_TubeAcceptedHandler,
291 this);
292 return spinUntilTubeEstablished();
296 bool TeleConference::offerTube()
298 INFO_LOGGER( "TeleConference::offerTube");
300 OSL_ENSURE( mpChannel, "TeleConference::offerTube: no channel");
301 if (!mpChannel)
302 return false;
304 GHashTable* pParameters = tp_asv_new (
305 LIBO_TUBES_UUID, G_TYPE_STRING, msUuid.getStr(),
306 NULL);
308 tp_dbus_tube_channel_offer_async(
309 mpChannel,
310 pParameters,
311 TeleConference_TubeOfferedHandler,
312 this);
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,
330 NULL,
331 NULL,
332 { NULL },
334 static const gchar introspection_xml[] =
335 "<node>"
336 " <interface name='" LIBO_TUBES_DBUS_INTERFACE "'>"
337 " <method name='" LIBO_TUBES_DBUS_MSG_METHOD "'>"
338 " <arg type='ay' name='packet' direction='in'/>"
339 " </method>"
340 " </interface>"
341 "</node>";
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);
353 return true;
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");
375 if (mpChannel)
376 tp_cli_channel_call_close( TP_CHANNEL( mpChannel), 5000, TeleConference_ChannelCloseHandler, this, NULL, NULL);
377 else
378 finalize();
382 void TeleConference::finalize()
384 INFO_LOGGER( "TeleConference::finalize");
386 TeleManager::unregisterDemoConference( this );
388 if (mpChannel)
390 g_object_unref( mpChannel);
391 mpChannel = NULL;
394 if (mpAccount)
396 g_object_unref( mpAccount);
397 mpAccount = NULL;
400 if (pImpl->mpTube)
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 );
419 return true;
422 SAL_WARN_IF( !pImpl->mpTube, "tubes", "TeleConference::sendPacket: no tube");
423 if (!pImpl->mpTube)
424 return false;
426 /* FIXME: in GLib 2.32 we can use g_variant_new_fixed_array(). It does
427 * essentially this.
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(),
432 FALSE,
433 g_free, pData);
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 */
441 NULL,
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 );
449 return true;
452 bool TeleConference::isMaster() const
454 return mbMaster;
457 Collaboration* TeleConference::getCollaboration() const
459 return mpCollaboration;
462 void TeleConference::setCollaboration( Collaboration* pCollaboration )
464 mpCollaboration = pCollaboration;
465 if (mpChannel)
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 );
486 namespace {
487 class SendFileRequest {
488 public:
489 SendFileRequest( TeleConference::FileSentCallback pCallback, void* pUserData, const char* sUuid )
490 : mpCallback(pCallback)
491 , mpUserData(pUserData)
492 , msUuid(sUuid)
495 TeleConference::FileSentCallback mpCallback;
496 void* mpUserData;
497 const char* msUuid;
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);
507 delete request;
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);
519 delete request;
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);
527 if ( error != 0 )
529 if (request->mpCallback)
530 request->mpCallback(error == 0, request->mpUserData);
531 delete request;
532 g_object_unref (handler);
534 else
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");
552 if (!pContact)
554 // used in demo mode
555 TeleManager_fileReceived( localUri, "demo" );
556 return;
559 SAL_WARN_IF( ( !mpAccount || !mpChannel), "tubes",
560 "can't send a file before the tube is set up");
561 if ( !mpAccount || !mpChannel)
562 return;
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,
569 pContact,
570 pSource,
572 TeleConference_FTReady, pReq);
575 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */