Added clear() method to OString and OUString class, Updated appropriate call-sites. Change-Id: I0ba97fa6dc7af3e31b605953089a4e8e9c3e61ac Signed-off-by: Stephan Bergmann <sbergman@redhat.com>
901 lines
32 KiB
C++
901 lines
32 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
#include <tubes/manager.hxx>
|
|
|
|
#include <tubes/collaboration.hxx>
|
|
#include <tubes/conference.hxx>
|
|
#include <tubes/constants.h>
|
|
#include <tubes/file-transfer-helper.h>
|
|
|
|
#include <com/sun/star/uno/Sequence.hxx>
|
|
#include <com/sun/star/frame/Desktop.hpp>
|
|
#include <com/sun/star/frame/XComponentLoader.hpp>
|
|
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
|
|
#include <com/sun/star/beans/PropertyValue.hpp>
|
|
#include <com/sun/star/util/XCloseable.hpp>
|
|
#include <comphelper/processfactory.hxx>
|
|
#include <osl/mutex.hxx>
|
|
#include <rtl/strbuf.hxx>
|
|
#include <rtl/uuid.h>
|
|
|
|
#include <telepathy-glib/telepathy-glib.h>
|
|
#include <stdio.h>
|
|
#include <map>
|
|
#include <set>
|
|
|
|
#if defined SAL_LOG_INFO
|
|
namespace
|
|
{
|
|
struct InfoLogger
|
|
{
|
|
const void* mpThat;
|
|
const char* mpMethod;
|
|
explicit InfoLogger( const void* pThat, const char* pMethod )
|
|
:
|
|
mpThat( pThat),
|
|
mpMethod( pMethod)
|
|
{
|
|
SAL_INFO( "tubes.method", mpThat << " entering " << mpMethod);
|
|
}
|
|
~InfoLogger()
|
|
{
|
|
SAL_INFO( "tubes.method", mpThat << " leaving " << mpMethod);
|
|
}
|
|
};
|
|
}
|
|
#define INFO_LOGGER_F(s) InfoLogger aLogger(0,(s))
|
|
#else
|
|
#define INFO_LOGGER_F(s)
|
|
#endif // SAL_LOG_INFO
|
|
|
|
using namespace osl;
|
|
|
|
/** Refcounted singleton implementation class. */
|
|
class TeleManagerImpl
|
|
{
|
|
public:
|
|
TpSimpleClientFactory* mpFactory;
|
|
TpBaseClient* mpClient;
|
|
TpBaseClient* mpFileTransferClient;
|
|
TpAccountManager* mpAccountManager;
|
|
static bool mbAccountManagerReady;
|
|
static bool mbAccountManagerReadyHandlerInvoked;
|
|
static bool mbChannelReadyHandlerInvoked;
|
|
OString msCurrentUUID;
|
|
OString msNameSuffix;
|
|
typedef std::map< OString, TeleConference* > MapStringConference;
|
|
MapStringConference maAcceptedConferences;
|
|
typedef std::set< TeleConference* > DemoConferences;
|
|
DemoConferences maDemoConferences;
|
|
typedef std::set< Collaboration* > Collaborations;
|
|
Collaborations maCollaborations;
|
|
typedef std::set< TpContact* > RegisteredContacts;
|
|
RegisteredContacts maRegisteredContacts;
|
|
|
|
TeleManagerImpl();
|
|
~TeleManagerImpl();
|
|
static void AccountManagerReadyHandler( GObject* pSourceObject, GAsyncResult* pResult, gpointer pUserData );
|
|
static void ChannelReadyHandler( GObject* pSourceObject, GAsyncResult* pResult, gpointer pUserData );
|
|
};
|
|
|
|
TeleManagerImpl* TeleManager::pImpl = new TeleManagerImpl();
|
|
bool TeleManagerImpl::mbAccountManagerReady;
|
|
bool TeleManagerImpl::mbAccountManagerReadyHandlerInvoked;
|
|
bool TeleManagerImpl::mbChannelReadyHandlerInvoked;
|
|
|
|
static void TeleManager_DBusChannelHandler(
|
|
TpSimpleHandler* /*handler*/,
|
|
TpAccount* pAccount,
|
|
TpConnection* /*connection*/,
|
|
GList* pChannels,
|
|
GList* /*requests_satisfied*/,
|
|
gint64 /*user_action_time*/,
|
|
TpHandleChannelsContext* pContext,
|
|
gpointer /*pUserData*/ )
|
|
{
|
|
bool aAccepted = false;
|
|
INFO_LOGGER_F( "TeleManager_DBusChannelHandler");
|
|
|
|
for (GList* p = pChannels; p; p = p->next)
|
|
{
|
|
TpChannel* pChannel = TP_CHANNEL(p->data);
|
|
if (!pChannel)
|
|
continue;
|
|
|
|
SAL_INFO( "tubes", "TeleManager_DBusChannelHandler: incoming dbus channel: "
|
|
<< tp_channel_get_identifier( pChannel));
|
|
|
|
if (TP_IS_DBUS_TUBE_CHANNEL( pChannel))
|
|
{
|
|
SAL_INFO( "tubes", "accepting");
|
|
aAccepted = true;
|
|
|
|
TeleConference* pConference = new TeleConference( pAccount, TP_DBUS_TUBE_CHANNEL( pChannel ) );
|
|
pConference->acceptTube();
|
|
TeleManager::addConference( pConference );
|
|
}
|
|
else
|
|
{
|
|
SAL_INFO( "tubes", "ignored");
|
|
}
|
|
}
|
|
|
|
if (aAccepted)
|
|
tp_handle_channels_context_accept( pContext);
|
|
else
|
|
{
|
|
GError *pError = g_error_new_literal( TP_ERRORS, TP_ERROR_CONFUSED,
|
|
"None of these channels were LibreOffice D-Bus tubes; "
|
|
"why did the Channel Dispatcher give them to us?");
|
|
tp_handle_channels_context_fail( pContext, pError);
|
|
g_clear_error (&pError);
|
|
}
|
|
}
|
|
|
|
// - TeleManager -
|
|
|
|
void TeleManager::addConference( TeleConference* pConference )
|
|
{
|
|
MutexGuard aGuard( GetMutex());
|
|
|
|
SAL_WARN_IF( pConference->getUuid().isEmpty(), "tubes",
|
|
"Adding conference with empty UUID should not happen!" );
|
|
pImpl->maAcceptedConferences[ pConference->getUuid() ] = pConference;
|
|
}
|
|
|
|
TeleConference* TeleManager::getConference()
|
|
{
|
|
MutexGuard aGuard( GetMutex());
|
|
|
|
TeleManagerImpl::MapStringConference::const_iterator it =
|
|
pImpl->maAcceptedConferences.find( pImpl->msCurrentUUID );
|
|
TeleConference* pConference = NULL;
|
|
if (it != pImpl->maAcceptedConferences.end())
|
|
pConference = it->second;
|
|
SAL_WARN_IF( !pConference, "tubes", "TeleManager::getConference: "
|
|
<< pImpl->msCurrentUUID.getStr() << " not found!" );
|
|
(pImpl->msCurrentUUID).clear();
|
|
return pConference;
|
|
}
|
|
|
|
void TeleManager::registerCollaboration( Collaboration* pCollaboration )
|
|
{
|
|
MutexGuard aGuard( GetMutex());
|
|
|
|
pImpl->maCollaborations.insert( pCollaboration );
|
|
}
|
|
|
|
void TeleManager::unregisterCollaboration( Collaboration* pCollaboration )
|
|
{
|
|
MutexGuard aGuard( GetMutex());
|
|
|
|
pImpl->maCollaborations.erase( pCollaboration );
|
|
}
|
|
|
|
bool TeleManager::existsCollaboration( Collaboration* pCollaboration )
|
|
{
|
|
MutexGuard aGuard( GetMutex());
|
|
|
|
return pImpl->maCollaborations.find( pCollaboration ) != pImpl->maCollaborations.end();
|
|
}
|
|
|
|
void TeleManager::displayAllContacts()
|
|
{
|
|
MutexGuard aGuard( GetMutex());
|
|
|
|
for (TeleManagerImpl::Collaborations::iterator it = pImpl->maCollaborations.begin();
|
|
it != pImpl->maCollaborations.end(); ++it)
|
|
(*it)->DisplayContacts();
|
|
}
|
|
|
|
void TeleManager::registerDemoConference( TeleConference* pConference )
|
|
{
|
|
MutexGuard aGuard( GetMutex());
|
|
|
|
pImpl->maDemoConferences.insert( pConference );
|
|
}
|
|
|
|
void TeleManager::unregisterDemoConference( TeleConference* pConference )
|
|
{
|
|
MutexGuard aGuard( GetMutex());
|
|
|
|
pImpl->maDemoConferences.erase( pConference );
|
|
}
|
|
|
|
void TeleManager::broadcastPacket( const OString& rPacket )
|
|
{
|
|
MutexGuard aGuard( GetMutex());
|
|
|
|
INFO_LOGGER_F( "TeleManager::broadcastPacket" );
|
|
for (TeleManagerImpl::DemoConferences::iterator it = pImpl->maDemoConferences.begin();
|
|
it != pImpl->maDemoConferences.end(); ++it)
|
|
if ((*it)->getCollaboration())
|
|
(*it)->getCollaboration()->PacketReceived( rPacket );
|
|
}
|
|
|
|
bool TeleManager::hasWaitingConference()
|
|
{
|
|
MutexGuard aGuard( GetMutex());
|
|
|
|
return !pImpl->msCurrentUUID.isEmpty();
|
|
}
|
|
|
|
void TeleManager::setCurrentUuid( const OString& rUuid )
|
|
{
|
|
MutexGuard aGuard( GetMutex());
|
|
|
|
pImpl->msCurrentUUID = rUuid;
|
|
}
|
|
|
|
// FIXME: should be static and not used in conference.cxx
|
|
void TeleManager_fileReceived( const OUString& rStr, const OString& rUuid )
|
|
{
|
|
SAL_INFO( "tubes", "TeleManager_fileReceived: incoming file: " << rStr );
|
|
|
|
OString sUuid( rUuid );
|
|
if (sUuid == "demo")
|
|
{
|
|
sUuid = TeleManager::createUuid();
|
|
TeleConference* pConference = new TeleConference( NULL, NULL, sUuid );
|
|
TeleManager::addConference( pConference );
|
|
TeleManager::registerDemoConference( pConference );
|
|
}
|
|
TeleManager::setCurrentUuid( sUuid );
|
|
|
|
try
|
|
{
|
|
css::uno::Reference < css::frame::XDesktop2 > xLoader = css::frame::Desktop::create(
|
|
::comphelper::getProcessComponentContext() );
|
|
css::uno::Sequence < css::beans::PropertyValue > args(0);
|
|
css::uno::Reference < css::util::XCloseable > xDoc(
|
|
xLoader->loadComponentFromURL( rStr, "_blank", 0, args ),
|
|
css::uno::UNO_QUERY_THROW );
|
|
}
|
|
catch ( const css::uno::Exception& e )
|
|
{
|
|
// Expected to happen for unit test
|
|
SAL_WARN( "tubes", "TeleManager_fileReceived: exception when loading: " << e.Message );
|
|
}
|
|
}
|
|
|
|
static void TeleManager_TransferDone( EmpathyFTHandler *handler, TpFileTransferChannel *, gpointer )
|
|
{
|
|
SAL_INFO( "tubes", "TeleManager_TransferDone: hooray!");
|
|
GFile *gfile = empathy_ft_handler_get_gfile( handler);
|
|
char *uri = g_file_get_uri( gfile);
|
|
OUString aUri( OUString::createFromAscii( uri ) );
|
|
g_free( uri);
|
|
|
|
TeleManager_fileReceived( aUri, empathy_ft_handler_get_description( handler ) );
|
|
|
|
g_object_unref( handler);
|
|
}
|
|
|
|
static void TeleManager_TransferError( EmpathyFTHandler *handler, const GError *error, void*)
|
|
{
|
|
SAL_INFO( "tubes", "TeleManager_TransferError: " << error->message);
|
|
|
|
g_object_unref( handler);
|
|
}
|
|
|
|
static void lcl_IncomingHandlerReady (
|
|
EmpathyFTHandler* pHandler,
|
|
GError* pError,
|
|
void* /*pUserData*/ )
|
|
{
|
|
if (pError)
|
|
{
|
|
SAL_INFO ("tubes", "failed to prepare incoming transfer: " << pError->message);
|
|
g_object_unref( pHandler);
|
|
return;
|
|
}
|
|
|
|
/* The filename suggested by the sender, which in our case is the last bit
|
|
* of whatever URI got passed to ::sendFile()
|
|
*/
|
|
const char* pFileName = empathy_ft_handler_get_filename( pHandler);
|
|
char* pLocalUri = g_strdup_printf( "file:///tmp/LibreOffice-collab-%s", pFileName);
|
|
GFile *pDestination = g_file_new_for_uri( pLocalUri);
|
|
g_free( pLocalUri);
|
|
|
|
empathy_ft_handler_incoming_set_destination( pHandler, pDestination);
|
|
g_object_unref( pDestination);
|
|
|
|
g_signal_connect( pHandler, "transfer-done", G_CALLBACK (TeleManager_TransferDone), NULL);
|
|
g_signal_connect( pHandler, "transfer-error", G_CALLBACK (TeleManager_TransferError), NULL);
|
|
SAL_INFO ("tubes", "lcl_IncomingHandlerReady: starting file transfer..");
|
|
empathy_ft_handler_start_transfer( pHandler);
|
|
}
|
|
|
|
static void TeleManager_FileTransferHandler(
|
|
TpSimpleHandler* /*handler*/,
|
|
TpAccount* /*Account*/,
|
|
TpConnection* /*connection*/,
|
|
GList* pChannels,
|
|
GList* /*requests_satisfied*/,
|
|
gint64 /*user_action_time*/,
|
|
TpHandleChannelsContext* pContext,
|
|
gpointer /*pUserData*/ )
|
|
{
|
|
bool aAccepted = false;
|
|
INFO_LOGGER_F( "TeleManager_FileTransferHandler");
|
|
|
|
for (GList* p = pChannels; p; p = p->next)
|
|
{
|
|
TpChannel* pChannel = TP_CHANNEL(p->data);
|
|
|
|
SAL_INFO( "tubes", "TeleManager_FileTransferHandler: incoming dbus channel: "
|
|
<< tp_channel_get_identifier( pChannel));
|
|
|
|
if (TP_IS_FILE_TRANSFER_CHANNEL( pChannel))
|
|
{
|
|
SAL_INFO( "tubes", "accepting file transfer");
|
|
empathy_ft_handler_new_incoming( TP_FILE_TRANSFER_CHANNEL( pChannel),
|
|
lcl_IncomingHandlerReady, NULL);
|
|
aAccepted = true;
|
|
}
|
|
else
|
|
{
|
|
SAL_INFO( "tubes", "ignored");
|
|
}
|
|
}
|
|
|
|
if (aAccepted)
|
|
tp_handle_channels_context_accept( pContext);
|
|
else
|
|
{
|
|
GError *pError = g_error_new_literal( TP_ERRORS, TP_ERROR_CONFUSED,
|
|
"None of these channels were file transfers; "
|
|
"why did the Channel Dispatcher give them to us?");
|
|
tp_handle_channels_context_fail( pContext, pError);
|
|
g_clear_error (&pError);
|
|
}
|
|
}
|
|
|
|
// - TeleManagerImpl -
|
|
|
|
void TeleManagerImpl::ChannelReadyHandler(
|
|
GObject* pSourceObject,
|
|
GAsyncResult* pResult,
|
|
gpointer pUserData
|
|
)
|
|
{
|
|
INFO_LOGGER_F( "TeleManagerImpl::ChannelReadyHandler");
|
|
|
|
TeleConference* pConference = reinterpret_cast<TeleConference*>(pUserData);
|
|
SAL_WARN_IF( !pConference, "tubes", "TeleManagerImpl::ChannelReadyHandler: no conference");
|
|
if (!pConference)
|
|
return;
|
|
|
|
mbChannelReadyHandlerInvoked = true;
|
|
|
|
TpAccountChannelRequest* pChannelRequest = TP_ACCOUNT_CHANNEL_REQUEST( pSourceObject);
|
|
GError* pError = NULL;
|
|
TpChannel * pChannel = tp_account_channel_request_create_and_handle_channel_finish(
|
|
pChannelRequest, pResult, NULL, &pError);
|
|
if (!pChannel)
|
|
{
|
|
// "account isn't Enabled" means just that..
|
|
/* FIXME: detect and handle, domain=132, code=3 */
|
|
SAL_WARN( "tubes", "TeleManagerImpl::ChannelReadyHandler: no channel: " << pError->message);
|
|
g_error_free( pError);
|
|
return;
|
|
}
|
|
pConference->setChannel( tp_account_channel_request_get_account( pChannelRequest),
|
|
TP_DBUS_TUBE_CHANNEL (pChannel));
|
|
pConference->offerTube();
|
|
}
|
|
|
|
void TeleManagerImpl::AccountManagerReadyHandler(
|
|
GObject* pSourceObject,
|
|
GAsyncResult* pResult,
|
|
gpointer /*pUserData*/
|
|
)
|
|
{
|
|
INFO_LOGGER_F( "TeleManagerImpl::AccountManagerReadyHandler");
|
|
|
|
GError* pError = NULL;
|
|
gboolean bPrepared = tp_proxy_prepare_finish( pSourceObject, pResult, &pError);
|
|
SAL_WARN_IF( !bPrepared, "tubes", "TeleManagerImpl::AccountManagerReadyHandler: not prepared");
|
|
if (!bPrepared || pError)
|
|
{
|
|
SAL_WARN_IF( pError, "tubes", "TeleManagerImpl::AccountManagerReadyHandler: error: " << pError->message);
|
|
g_error_free( pError);
|
|
}
|
|
|
|
mbAccountManagerReady = bPrepared;
|
|
mbAccountManagerReadyHandlerInvoked = true;
|
|
}
|
|
|
|
// - TeleManager -
|
|
|
|
bool TeleManager::init( bool bListen )
|
|
{
|
|
if (createAccountManager())
|
|
{
|
|
if (bListen && !registerClients())
|
|
SAL_WARN( "tubes", "TeleManager::init: Could not register client handlers." );
|
|
|
|
return true;
|
|
}
|
|
else
|
|
SAL_WARN( "tubes", "TeleManager::init: Could not create AccountManager." );
|
|
|
|
return false;
|
|
}
|
|
|
|
void TeleManager::finalize()
|
|
{
|
|
delete pImpl;
|
|
}
|
|
|
|
bool TeleManager::createAccountManager()
|
|
{
|
|
INFO_LOGGER_F( "TeleManager::createAccountManager");
|
|
|
|
MutexGuard aGuard( GetMutex());
|
|
|
|
SAL_INFO_IF( pImpl->mpAccountManager, "tubes", "TeleManager::createAccountManager: already connected");
|
|
if (pImpl->mpAccountManager)
|
|
return true;
|
|
|
|
GError* pError = NULL;
|
|
TpDBusDaemon *pDBus = tp_dbus_daemon_dup( &pError);
|
|
SAL_WARN_IF( !pDBus, "tubes", "TeleManager::createAccountManager: no dbus daemon");
|
|
if (!pDBus || pError)
|
|
{
|
|
SAL_WARN_IF( pError, "tubes", "TeleManager::createAccountManager: dbus daemon error: " << pError->message);
|
|
g_error_free( pError);
|
|
return false;
|
|
}
|
|
|
|
pImpl->mpFactory = TP_SIMPLE_CLIENT_FACTORY( tp_automatic_client_factory_new( pDBus));
|
|
g_object_unref( pDBus);
|
|
SAL_WARN_IF( !pImpl->mpFactory, "tubes", "TeleManager::createAccountManager: no client factory");
|
|
if (!pImpl->mpFactory)
|
|
return false;
|
|
|
|
/* Tell the client factory (which creates and prepares proxy objects) to
|
|
* get the features we need ready before giving us any objects.
|
|
*/
|
|
/* We need every online account's connection object to be available... */
|
|
tp_simple_client_factory_add_account_features_varargs (pImpl->mpFactory,
|
|
TP_ACCOUNT_FEATURE_CONNECTION,
|
|
0);
|
|
/* ...and we want those connection objects to have the contact list
|
|
* available... */
|
|
tp_simple_client_factory_add_connection_features_varargs (pImpl->mpFactory,
|
|
TP_CONNECTION_FEATURE_CONTACT_LIST,
|
|
0);
|
|
/* ...and those contacts should have their alias and their capabilities
|
|
* available.
|
|
*/
|
|
tp_simple_client_factory_add_contact_features_varargs (pImpl->mpFactory,
|
|
TP_CONTACT_FEATURE_ALIAS,
|
|
TP_CONTACT_FEATURE_AVATAR_DATA,
|
|
TP_CONTACT_FEATURE_CAPABILITIES,
|
|
TP_CONTACT_FEATURE_PRESENCE,
|
|
TP_CONTACT_FEATURE_INVALID);
|
|
|
|
pImpl->mpAccountManager = tp_account_manager_new_with_factory (pImpl->mpFactory);
|
|
tp_account_manager_set_default (pImpl->mpAccountManager);
|
|
|
|
pImpl->mbAccountManagerReadyHandlerInvoked = false;
|
|
tp_proxy_prepare_async( pImpl->mpAccountManager, NULL, TeleManagerImpl::AccountManagerReadyHandler, NULL);
|
|
while (!pImpl->mbAccountManagerReadyHandlerInvoked)
|
|
g_main_context_iteration( NULL, TRUE);
|
|
|
|
return pImpl->mbAccountManagerReady;
|
|
}
|
|
|
|
bool TeleManager::registerClients()
|
|
{
|
|
INFO_LOGGER_F( "TeleManager::registerClients");
|
|
|
|
MutexGuard aGuard( GetMutex());
|
|
|
|
/* TODO: also check whether client could be registered and retry if not? */
|
|
SAL_INFO_IF( pImpl->mpClient && pImpl->mpFileTransferClient, "tubes", "TeleManager::registerClients: already registered");
|
|
if (pImpl->mpClient && pImpl->mpFileTransferClient)
|
|
return true;
|
|
|
|
pImpl->mpClient = tp_simple_handler_new_with_factory(
|
|
pImpl->mpFactory, // factory
|
|
FALSE, // bypass_approval
|
|
FALSE, // requests
|
|
getFullClientName().getStr(), // name
|
|
FALSE, // uniquify
|
|
TeleManager_DBusChannelHandler, // callback
|
|
NULL, // user_data
|
|
NULL // destroy
|
|
);
|
|
SAL_WARN_IF( !pImpl->mpClient, "tubes", "TeleManager::registerClients: no client");
|
|
if (!pImpl->mpClient)
|
|
return false;
|
|
|
|
// Setup client handler for buddy channels with our service.
|
|
tp_base_client_take_handler_filter( pImpl->mpClient,
|
|
tp_asv_new(
|
|
TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, TP_IFACE_CHANNEL_TYPE_DBUS_TUBE,
|
|
TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_CONTACT,
|
|
TP_PROP_CHANNEL_TYPE_DBUS_TUBE_SERVICE_NAME, G_TYPE_STRING, getFullServiceName().getStr(),
|
|
NULL));
|
|
|
|
/* TODO: setup filters for LibreOfficeCalc, LibreOfficeWriter, ... */
|
|
|
|
// Setup client handler for MUC channels with our service.
|
|
tp_base_client_take_handler_filter( pImpl->mpClient,
|
|
tp_asv_new(
|
|
TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, TP_IFACE_CHANNEL_TYPE_DBUS_TUBE,
|
|
TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_ROOM,
|
|
TP_PROP_CHANNEL_TYPE_DBUS_TUBE_SERVICE_NAME, G_TYPE_STRING, getFullServiceName().getStr(),
|
|
NULL));
|
|
|
|
GError* pError = NULL;
|
|
if (!tp_base_client_register( pImpl->mpClient, &pError))
|
|
{
|
|
SAL_WARN( "tubes", "TeleManager::registerClients: error registering client handler: " << pError->message);
|
|
g_error_free( pError);
|
|
return false;
|
|
}
|
|
|
|
SAL_INFO( "tubes", "TeleManager::registerClients: bus name: " << tp_base_client_get_bus_name( pImpl->mpClient));
|
|
SAL_INFO( "tubes", "TeleManager::registerClients: object path: " << tp_base_client_get_object_path( pImpl->mpClient));
|
|
|
|
/* Register a second "head" for incoming file transfers. This uses a more
|
|
* specific filter than Empathy's handler by matching on the file
|
|
* transfer's ServiceName property, and uses bypass_approval to ensure the
|
|
* user isn't prompted before the channel gets passed to us.
|
|
*/
|
|
pImpl->mpFileTransferClient = tp_simple_handler_new_with_factory (
|
|
pImpl->mpFactory, // factory
|
|
TRUE, // bypass_approval
|
|
FALSE, // requests
|
|
getFullClientName().getStr(), // name
|
|
TRUE, // uniquify to get a different bus name to the main client, above
|
|
TeleManager_FileTransferHandler, // callback
|
|
NULL, // user_data
|
|
NULL // destroy
|
|
);
|
|
tp_base_client_take_handler_filter( pImpl->mpFileTransferClient,
|
|
tp_asv_new(
|
|
TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, TP_IFACE_CHANNEL_TYPE_FILE_TRANSFER,
|
|
TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_CONTACT,
|
|
TP_PROP_CHANNEL_INTERFACE_FILE_TRANSFER_METADATA_SERVICE_NAME, G_TYPE_STRING, getFullServiceName().getStr(),
|
|
NULL));
|
|
|
|
if (!tp_base_client_register( pImpl->mpFileTransferClient, &pError))
|
|
{
|
|
/* This shouldn't fail if registering the main handler succeeded */
|
|
SAL_WARN( "tubes", "TeleManager::registerClients: error registering file transfer handler: " << pError->message);
|
|
g_error_free( pError);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
TeleConference* TeleManager::startDemoSession()
|
|
{
|
|
INFO_LOGGER_F( "TeleManager::startDemoSession");
|
|
|
|
TeleConference* pConference = new TeleConference( NULL, NULL, "demo" );
|
|
registerDemoConference( pConference );
|
|
|
|
return pConference;
|
|
}
|
|
|
|
/* TODO: factor out common code with startBuddySession() */
|
|
TeleConference* TeleManager::startGroupSession( TpAccount *pAccount,
|
|
const OUString& rUConferenceRoom,
|
|
const OUString& rUConferenceServer )
|
|
{
|
|
INFO_LOGGER_F( "TeleManager::startGroupSession");
|
|
|
|
OString aSessionId( TeleManager::createUuid());
|
|
|
|
/* FIXME: does this work at all _creating_ a MUC? */
|
|
// Use conference and server if given, else create conference.
|
|
OString aConferenceRoom( OUStringToOString( rUConferenceRoom, RTL_TEXTENCODING_UTF8));
|
|
OString aConferenceServer( OUStringToOString( rUConferenceServer, RTL_TEXTENCODING_UTF8));
|
|
OStringBuffer aBuf(64);
|
|
if (!aConferenceRoom.isEmpty() && !aConferenceServer.isEmpty())
|
|
aBuf.append( aConferenceRoom).append( '@').append( aConferenceServer);
|
|
else
|
|
{
|
|
aBuf.append( aSessionId);
|
|
if (!aConferenceServer.isEmpty())
|
|
aBuf.append( '@').append( aConferenceServer);
|
|
/* FIXME: else? bail out? we have only a session ID without server then */
|
|
}
|
|
OString aTarget( aBuf.makeStringAndClear());
|
|
|
|
SAL_INFO( "tubes", "TeleManager::startGroupSession: creating channel request from "
|
|
<< tp_account_get_path_suffix( pAccount ) << " to " << aTarget.getStr() );
|
|
|
|
// MUC request
|
|
GHashTable* pRequest = tp_asv_new(
|
|
TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, TP_IFACE_CHANNEL_TYPE_DBUS_TUBE,
|
|
TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, TP_TYPE_HANDLE, TP_HANDLE_TYPE_ROOM,
|
|
TP_PROP_CHANNEL_TARGET_ID, G_TYPE_STRING, aTarget.getStr(),
|
|
TP_PROP_CHANNEL_TYPE_DBUS_TUBE_SERVICE_NAME, G_TYPE_STRING, getFullServiceName().getStr(),
|
|
NULL);
|
|
|
|
TpAccountChannelRequest * pChannelRequest = tp_account_channel_request_new(
|
|
pAccount, pRequest, TP_USER_ACTION_TIME_NOT_USER_ACTION);
|
|
SAL_WARN_IF( !pChannelRequest, "tubes", "TeleManager::startGroupSession: no channel");
|
|
if (!pChannelRequest)
|
|
{
|
|
g_hash_table_unref( pRequest);
|
|
return NULL;
|
|
}
|
|
|
|
pImpl->mbChannelReadyHandlerInvoked = false;
|
|
|
|
TeleConference* pConference = new TeleConference( NULL, NULL, aSessionId );
|
|
|
|
tp_account_channel_request_create_and_handle_channel_async(
|
|
pChannelRequest, NULL, TeleManagerImpl::ChannelReadyHandler, pConference);
|
|
|
|
while (!pImpl->mbChannelReadyHandlerInvoked)
|
|
g_main_context_iteration( NULL, TRUE );
|
|
|
|
g_object_unref( pChannelRequest);
|
|
g_hash_table_unref( pRequest);
|
|
|
|
if (!pConference->isReady())
|
|
return NULL;
|
|
|
|
return pConference;
|
|
}
|
|
|
|
static void lcl_ensureLegacyChannel( TpAccount* pAccount, TpContact* pBuddy )
|
|
{
|
|
/* This is a workaround for a Telepathy bug.
|
|
* <https://bugs.libreoffice.org/show_bug.cgi?id=47760>. The first time you
|
|
* request a tube to a contact on an account, you actually get two channels
|
|
* back: the tube you asked for, along with a legacy Channel.Type.Tubes
|
|
* object. This breaks create_and_handle_channel_async(), which expects to
|
|
* only get one channel back.
|
|
*
|
|
* To work around this, we make sure the legacy Tubes channel already
|
|
* exists before we request the channel we actually want. We don't actually
|
|
* have to wait for this request to succeed - we fire it off and forget
|
|
* about it.
|
|
*/
|
|
GHashTable* pRequest = tp_asv_new(
|
|
TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, TP_IFACE_CHANNEL_TYPE_TUBES,
|
|
TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, TP_TYPE_HANDLE, TP_HANDLE_TYPE_CONTACT,
|
|
TP_PROP_CHANNEL_TARGET_ID, G_TYPE_STRING, tp_contact_get_identifier (pBuddy),
|
|
NULL);
|
|
TpAccountChannelRequest* pChannelRequest = tp_account_channel_request_new(
|
|
pAccount, pRequest, TP_USER_ACTION_TIME_NOT_USER_ACTION);
|
|
tp_account_channel_request_ensure_channel_async( pChannelRequest, NULL,
|
|
NULL, NULL, NULL );
|
|
g_object_unref( pChannelRequest );
|
|
g_hash_table_unref( pRequest );
|
|
}
|
|
|
|
/* TODO: factor out common code with startGroupSession() */
|
|
TeleConference* TeleManager::startBuddySession( TpAccount *pAccount, TpContact *pBuddy )
|
|
{
|
|
INFO_LOGGER_F( "TeleManager::startBuddySession");
|
|
|
|
lcl_ensureLegacyChannel( pAccount, pBuddy );
|
|
|
|
const char *pIdentifier = tp_contact_get_identifier( pBuddy);
|
|
SAL_INFO( "tubes", "TeleManager::startBuddySession: creating channel request from "
|
|
<< tp_account_get_path_suffix( pAccount)
|
|
<< " to " << pIdentifier);
|
|
|
|
GHashTable* pRequest = tp_asv_new(
|
|
TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, TP_IFACE_CHANNEL_TYPE_DBUS_TUBE,
|
|
TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, TP_TYPE_HANDLE, TP_HANDLE_TYPE_CONTACT,
|
|
TP_PROP_CHANNEL_TARGET_ID, G_TYPE_STRING, pIdentifier,
|
|
TP_PROP_CHANNEL_TYPE_DBUS_TUBE_SERVICE_NAME, G_TYPE_STRING, getFullServiceName().getStr(),
|
|
NULL);
|
|
|
|
TpAccountChannelRequest * pChannelRequest = tp_account_channel_request_new(
|
|
pAccount, pRequest, TP_USER_ACTION_TIME_NOT_USER_ACTION);
|
|
SAL_WARN_IF( !pChannelRequest, "tubes", "TeleManager::startBuddySession: no channel");
|
|
if (!pChannelRequest)
|
|
{
|
|
g_hash_table_unref( pRequest);
|
|
return NULL;
|
|
}
|
|
|
|
pImpl->mbChannelReadyHandlerInvoked = false;
|
|
|
|
TeleConference* pConference = new TeleConference( NULL, NULL, createUuid(), true );
|
|
|
|
tp_account_channel_request_create_and_handle_channel_async(
|
|
pChannelRequest, NULL, TeleManagerImpl::ChannelReadyHandler, pConference );
|
|
|
|
while (!pImpl->mbChannelReadyHandlerInvoked)
|
|
g_main_context_iteration( NULL, TRUE );
|
|
|
|
g_object_unref( pChannelRequest);
|
|
g_hash_table_unref( pRequest);
|
|
|
|
if (!pConference->isReady())
|
|
return NULL;
|
|
|
|
return pConference;
|
|
}
|
|
|
|
static bool tb_presence_is_online( const TpConnectionPresenceType& presence )
|
|
{
|
|
switch (presence)
|
|
{
|
|
case TP_CONNECTION_PRESENCE_TYPE_UNSET:
|
|
case TP_CONNECTION_PRESENCE_TYPE_OFFLINE:
|
|
return false;
|
|
case TP_CONNECTION_PRESENCE_TYPE_AVAILABLE:
|
|
case TP_CONNECTION_PRESENCE_TYPE_AWAY:
|
|
case TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY:
|
|
case TP_CONNECTION_PRESENCE_TYPE_HIDDEN:
|
|
case TP_CONNECTION_PRESENCE_TYPE_BUSY:
|
|
return true;
|
|
case TP_CONNECTION_PRESENCE_TYPE_UNKNOWN:
|
|
case TP_CONNECTION_PRESENCE_TYPE_ERROR:
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool tb_contact_is_online( TpContact *contact )
|
|
{
|
|
return tb_presence_is_online (tp_contact_get_presence_type (contact));
|
|
}
|
|
|
|
static void presence_changed_cb( TpContact* /* contact */,
|
|
guint /* type */,
|
|
gchar* /* status */,
|
|
gchar* /* message */,
|
|
gpointer /* pContactList*/ )
|
|
{
|
|
TeleManager::displayAllContacts();
|
|
}
|
|
|
|
AccountContactPairV TeleManager::getContacts()
|
|
{
|
|
AccountContactPairV pairs;
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
|
for (GList *accounts = tp_account_manager_get_valid_accounts (pImpl->mpAccountManager);
|
|
accounts != NULL; accounts = g_list_delete_link (accounts, accounts))
|
|
{
|
|
TpAccount *account = reinterpret_cast<TpAccount *>(accounts->data);
|
|
TpConnection *connection = tp_account_get_connection (account);
|
|
|
|
/* Verify account is online and received its contact list. If state is not
|
|
* SUCCESS this means we didn't received the roster from server yet and
|
|
* we would have to wait for the "notify:contact-list-state" signal. */
|
|
if (connection == NULL || tp_connection_get_contact_list_state (connection) !=
|
|
TP_CONTACT_LIST_STATE_SUCCESS)
|
|
continue;
|
|
|
|
TpContact *self = tp_connection_get_self_contact (connection);
|
|
GPtrArray *contacts = tp_connection_dup_contact_list (connection);
|
|
for (guint i = 0; i < contacts->len; i++)
|
|
{
|
|
TpContact *contact = reinterpret_cast<TpContact *>(g_ptr_array_index (contacts, i));
|
|
if (pImpl->maRegisteredContacts.find (contact) == pImpl->maRegisteredContacts.end())
|
|
{
|
|
pImpl->maRegisteredContacts.insert (contact);
|
|
g_signal_connect (contact, "presence-changed",
|
|
G_CALLBACK (presence_changed_cb), NULL );
|
|
}
|
|
if (contact != self && tb_contact_is_online (contact))
|
|
{
|
|
g_object_ref (account);
|
|
g_object_ref (contact);
|
|
|
|
AccountContactPair pair(account, contact);
|
|
pairs.push_back(pair);
|
|
}
|
|
}
|
|
g_ptr_array_unref (contacts);
|
|
}
|
|
#pragma GCC diagnostic pop
|
|
return pairs;
|
|
}
|
|
|
|
OString TeleManager::getFullClientName()
|
|
{
|
|
OStringBuffer aBuf(64);
|
|
aBuf.append( LIBO_CLIENT_SUFFIX ).append( pImpl->msNameSuffix);
|
|
return aBuf.makeStringAndClear();
|
|
}
|
|
|
|
OString TeleManager::getFullServiceName()
|
|
{
|
|
OStringBuffer aBuf(64);
|
|
aBuf.append( LIBO_DTUBE_SERVICE ).append( pImpl->msNameSuffix);
|
|
return aBuf.makeStringAndClear();
|
|
}
|
|
|
|
OString TeleManager::getFullObjectPath()
|
|
{
|
|
OStringBuffer aBuf(64);
|
|
aBuf.append( '/').append( LIBO_DTUBE_SERVICE ).append( pImpl->msNameSuffix);
|
|
OString aStr( aBuf.makeStringAndClear().replace( '.', '/'));
|
|
return aStr;
|
|
}
|
|
|
|
OString TeleManager::createUuid()
|
|
{
|
|
sal_uInt8 nId[16];
|
|
rtl_createUuid( nId, 0, sal_True);
|
|
char aBuf[33];
|
|
for (size_t i=0; i<16; ++i)
|
|
{
|
|
snprintf( aBuf+2*i, 3, "%02x", (unsigned char)nId[i]);
|
|
}
|
|
aBuf[32] = 0;
|
|
return OString( aBuf);
|
|
}
|
|
|
|
Mutex& TeleManager::GetMutex()
|
|
{
|
|
static Mutex* pMutex = NULL;
|
|
if (!pMutex)
|
|
{
|
|
MutexGuard aGuard( Mutex::getGlobalMutex());
|
|
if (!pMutex)
|
|
pMutex = new Mutex;
|
|
}
|
|
return *pMutex;
|
|
}
|
|
|
|
void TeleManager::addSuffixToNames( const char* pName )
|
|
{
|
|
pImpl->msNameSuffix = pName;
|
|
}
|
|
|
|
// - TeleManagerImpl -
|
|
|
|
TeleManagerImpl::TeleManagerImpl()
|
|
:
|
|
mpFactory( NULL),
|
|
mpClient( NULL),
|
|
mpFileTransferClient( NULL),
|
|
mpAccountManager( NULL)
|
|
{
|
|
#if !GLIB_CHECK_VERSION(2,36,0)
|
|
g_type_init();
|
|
#endif
|
|
}
|
|
|
|
TeleManagerImpl::~TeleManagerImpl()
|
|
{
|
|
// There may be unused conferences left opened, so close them.
|
|
// It should not make a problem to close already closed conference.
|
|
for (MapStringConference::iterator it = maAcceptedConferences.begin();
|
|
it != maAcceptedConferences.end(); ++it)
|
|
it->second->close();
|
|
if (mpClient)
|
|
{
|
|
tp_base_client_unregister( mpClient);
|
|
g_object_unref( mpClient);
|
|
}
|
|
if (mpFileTransferClient)
|
|
{
|
|
tp_base_client_unregister( mpFileTransferClient);
|
|
g_object_unref( mpFileTransferClient);
|
|
}
|
|
if (mpFactory)
|
|
g_object_unref( mpFactory);
|
|
if (mpAccountManager)
|
|
g_object_unref( mpAccountManager);
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|