diff options
author | Eike Rathke <erack@redhat.com> | 2012-03-16 22:14:54 +0100 |
---|---|---|
committer | Matúš Kukan <matus.kukan@gmail.com> | 2012-07-17 16:39:16 +0200 |
commit | a3a8b803f346681da50fc6d59bf2a3fba58d4da1 (patch) | |
tree | d3ff28747bb7e51a9a5efb5fd94d976bacd9d2f5 /tubes | |
parent | e80a6eb111da4a258286715ac27a5c10cb2ce07d (diff) |
implementing Telepathy Tubes interface
Diffstat (limited to 'tubes')
-rw-r--r-- | tubes/CppunitTest_tubes_test.mk | 45 | ||||
-rw-r--r-- | tubes/Library_tubes.mk | 63 | ||||
-rw-r--r-- | tubes/Makefile | 7 | ||||
-rw-r--r-- | tubes/Module_tubes.mk | 38 | ||||
-rw-r--r-- | tubes/README | 33 | ||||
-rw-r--r-- | tubes/inc/tubes/conference.hxx | 104 | ||||
-rw-r--r-- | tubes/inc/tubes/manager.hxx | 213 | ||||
-rw-r--r-- | tubes/inc/tubes/packet.hxx | 69 | ||||
-rw-r--r-- | tubes/inc/tubes/tubesdllapi.h | 16 | ||||
-rw-r--r-- | tubes/prj/build.lst | 2 | ||||
-rw-r--r-- | tubes/prj/d.lst | 0 | ||||
-rw-r--r-- | tubes/prj/makefile.mk | 1 | ||||
-rw-r--r-- | tubes/qa/test_manager.cxx | 188 | ||||
-rw-r--r-- | tubes/source/conference.cxx | 384 | ||||
-rw-r--r-- | tubes/source/manager.cxx | 668 | ||||
-rw-r--r-- | tubes/source/packet.cxx | 33 |
16 files changed, 1864 insertions, 0 deletions
diff --git a/tubes/CppunitTest_tubes_test.mk b/tubes/CppunitTest_tubes_test.mk new file mode 100644 index 000000000000..32810f9739f4 --- /dev/null +++ b/tubes/CppunitTest_tubes_test.mk @@ -0,0 +1,45 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# Version: MPL 1.1 / GPLv3+ / LGPLv3+ +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License or as specified alternatively below. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# Major Contributor(s): +# Copyright (C) 2012 Red Hat, Inc., Eike Rathke <erack@redhat.com> +# +# All Rights Reserved. +# +# For minor contributions see the git repository. +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 3 or later (the "GPLv3+"), or +# the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"), +# in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable +# instead of those above. + +$(eval $(call gb_CppunitTest_CppunitTest,tubes_test)) + +$(eval $(call gb_CppunitTest_add_exception_objects,tubes_test, \ + tubes/qa/test_manager \ +)) + +$(eval $(call gb_CppunitTest_add_linked_libs,tubes_test, \ + tubes \ + sal \ + $(gb_STDLIBS) \ +)) + +$(eval $(call gb_CppunitTest_set_include,tubes_test,\ + -I$(realpath $(SRCDIR)/tubes/inc) \ + $$(TELEPATHY_CFLAGS) \ + $$(INCLUDE) \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/tubes/Library_tubes.mk b/tubes/Library_tubes.mk new file mode 100644 index 000000000000..2ab261644932 --- /dev/null +++ b/tubes/Library_tubes.mk @@ -0,0 +1,63 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# Version: MPL 1.1 / GPLv3+ / LGPLv3+ +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License or as specified alternatively below. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# Major Contributor(s): +# Copyright (C) 2012 Red Hat, Inc., Eike Rathke <erack@redhat.com> +# +# All Rights Reserved. +# +# For minor contributions see the git repository. +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 3 or later (the "GPLv3+"), or +# the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"), +# in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable +# instead of those above. + +$(eval $(call gb_Library_Library,tubes)) + +$(eval $(call gb_Library_set_include,tubes,\ + -I$(realpath $(SRCDIR)/tubes/inc) \ + $$(INCLUDE) \ +)) + +$(eval $(call gb_Library_add_defs,tubes,\ + -DTUBES_DLLIMPLEMENTATION \ +)) + +$(eval $(call gb_Library_add_cxxflags,tubes,\ + $$(TELEPATHY_CFLAGS) \ +)) + +$(eval $(call gb_Library_add_libs,tubes,\ + $$(TELEPATHY_LIBS) \ +)) + +$(eval $(call gb_Library_add_linked_libs,tubes,\ + $(gb_STDLIBS) \ + sal \ + salhelper \ +)) + +$(eval $(call gb_Library_use_externals,tubes,\ + telepathy \ +)) + +$(eval $(call gb_Library_add_exception_objects,tubes,\ + tubes/source/conference \ + tubes/source/manager \ + tubes/source/packet \ +)) + +# vim:set shiftwidth=4 tabstop=4 noexpandtab: */ diff --git a/tubes/Makefile b/tubes/Makefile new file mode 100644 index 000000000000..ccb1c85a04da --- /dev/null +++ b/tubes/Makefile @@ -0,0 +1,7 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- + +module_directory:=$(dir $(realpath $(firstword $(MAKEFILE_LIST)))) + +include $(module_directory)/../solenv/gbuild/partial_build.mk + +# vim: set noet sw=4 ts=4: diff --git a/tubes/Module_tubes.mk b/tubes/Module_tubes.mk new file mode 100644 index 000000000000..ce810e1a12f3 --- /dev/null +++ b/tubes/Module_tubes.mk @@ -0,0 +1,38 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# Version: MPL 1.1 / GPLv3+ / LGPLv3+ +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License or as specified alternatively below. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# Major Contributor(s): +# Copyright (C) 2012 Red Hat, Inc., Eike Rathke <erack@redhat.com> +# +# All Rights Reserved. +# +# For minor contributions see the git repository. +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 3 or later (the "GPLv3+"), or +# the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"), +# in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable +# instead of those above. + +$(eval $(call gb_Module_Module,tubes)) + +$(eval $(call gb_Module_add_targets,tubes,\ + Library_tubes \ +)) + +$(eval $(call gb_Module_add_check_targets,tubes,\ + CppunitTest_tubes_test \ +)) + +# vim:set shiftwidth=4 tabstop=4 noexpandtab: */ diff --git a/tubes/README b/tubes/README new file mode 100644 index 000000000000..ca8764f87707 --- /dev/null +++ b/tubes/README @@ -0,0 +1,33 @@ +Interface to Telepathy Tubes. + +To enable configure LibO with --enable-telepathy + +Status 2012-03-16: + +* no LibO code depends on this module yet, so it is not built in a regular + build, even if configured with --enable-telepathy, so cd tubes and make here + * to enable the various SAL_INFO and SAL_WARN messages emitted during + cppunittest pass SAL_LOG=... and do a debug build + * SAL_LOG="+WARN+INFO.tubes" make -rs debug=true + * the cppunittest will currently fail anyway (even if it wouldn't for other + reasons), this is on purpose to be able to see the output as otherwise it + is silenced down ... :-( + +* for the cppunittest needed: + * a jabber daemon running on localhost.localdomain + * two accounts configured in Empathy + * libo1@localhost.localdomain + * libo2@localhost.localdomain + * libo1 and libo2 must be contacts/buddies of each other + * both accounts need to be online in Empathy + +* very nasty GMainLoop handling for cppunittest, MAYBE we could get rid of + that in a real LibO, this might be responsible for some ugly behaviour +* contact channels seem to be successfully setup +* the client's callback TeleManager_DBusChannelHandler setup with + TeleManager::connect() does not appear to get ever called +* hence the tube offer triggered by TeleManager_ChannelReadyHandler is never + accepted +* unsure if the uniquify setup with tp_simple_handler_new_with_factory() would + work at all, hence trying to have one instance un-uniquified, but to no + avail diff --git a/tubes/inc/tubes/conference.hxx b/tubes/inc/tubes/conference.hxx new file mode 100644 index 000000000000..db78cd5e80aa --- /dev/null +++ b/tubes/inc/tubes/conference.hxx @@ -0,0 +1,104 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * Version: MPL 1.1 / GPLv3+ / LGPLv3+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2012 Red Hat, Inc., Eike Rathke <erack@redhat.com> + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 3 or later (the "GPLv3+"), or + * the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"), + * in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable + * instead of those above. + */ + +#ifndef INCLUDED_TUBES_CONFERENCE_HXX +#define INCLUDED_TUBES_CONFERENCE_HXX + +#include <sal/config.h> +#include "tubes/packet.hxx" +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <telepathy-glib/telepathy-glib.h> +#include <dbus/dbus-glib-lowlevel.h> +#include <queue> + +typedef ::std::queue<TelePacket> TelePacketQueue; + +class TeleManager; + +/** Conference setup by TeleManager */ +class TeleConference : public boost::enable_shared_from_this<TeleConference> +{ +public: + + TeleConference( TeleManager* pManager, TpChannel* pChannel, const rtl::OString& rSessionId ); + ~TeleConference(); + + /// Close channel and call finalize() + void close(); + + /// Unrefs, unregisters from manager and calls dtor if last reference! + void finalize(); + + TeleManager* getManager() const { return mpManager; } + + /** @param rPacket + non-const on purpose, see TelePacket::getData() + */ + bool sendPacket( TelePacket& rPacket ) const; + + /** Queue incoming data as TelePacket */ + void queue( const char* pDBusSender, const char* pPacket, int nSize ); + + // --- following only to be called only by manager's callbacks --- + // TODO: make friends instead + + /// "buddy@jabber.example.org" or "room@conference.example.org" or "UUID" + void setTarget( const rtl::OString& rTarget ); + void setChannel( TpChannel* pChannel ); + TpChannel* getChannel() const { return mpChannel; } + bool offerTube(); + bool setTube( DBusConnection* pTube ); + bool acceptTube( const char* pAddress ); + + // Only for callbacks. + void setTubeOfferedHandlerInvoked( bool b ) { mbTubeOfferedHandlerInvoked = b; } + bool isTubeOfferedHandlerInvoked() const { return mbTubeOfferedHandlerInvoked; } + +private: + + rtl::OString maTarget; + rtl::OString maSessionId; + TeleManager* mpManager; + TpChannel* mpChannel; + DBusConnection* mpTube; + TelePacketQueue maPacketQueue; + + bool mbTubeOfferedHandlerInvoked : 1; + + // hide from the public + using boost::enable_shared_from_this<TeleConference>::shared_from_this; + +}; + + +typedef boost::shared_ptr<TeleConference> TeleConferencePtr; + +#endif // INCLUDED_TUBES_CONFERENCE_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/tubes/inc/tubes/manager.hxx b/tubes/inc/tubes/manager.hxx new file mode 100644 index 000000000000..b47455a78ea0 --- /dev/null +++ b/tubes/inc/tubes/manager.hxx @@ -0,0 +1,213 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * Version: MPL 1.1 / GPLv3+ / LGPLv3+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2012 Red Hat, Inc., Eike Rathke <erack@redhat.com> + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 3 or later (the "GPLv3+"), or + * the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"), + * in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable + * instead of those above. + */ + +#ifndef INCLUDED_TUBES_MANAGER_HXX +#define INCLUDED_TUBES_MANAGER_HXX + +#include <sal/config.h> +#include "tubes/tubesdllapi.h" +#include "tubes/conference.hxx" +#include "tubes/packet.hxx" +#include <rtl/ustring.hxx> +#include <salhelper/thread.hxx> +#include <rtl/ref.hxx> +#include <telepathy-glib/telepathy-glib.h> +#include <vector> + +// For testing purposes, we might need more in future. +#define LIBO_TUBES_DBUS_MSG_METHOD "LibOMsg" + +typedef ::std::vector<TeleConferencePtr> TeleConferenceVector; + + +/** Interface to Telepathy DBus Tubes. + + Fragile, not working yet. + + Accounts need to have been setup within Empathy already. + */ + +/* FIXME: selective dllprivate methods */ +class TUBES_DLLPUBLIC TeleManager +{ +public: + + enum AccountManagerStatus + { + AMS_UNINITIALIZED = 0, + AMS_INPREPARATION, + AMS_UNPREPARABLE, + AMS_PREPARED + }; + + /** Prepare tube manager with account and service to be offered/listened + to. + + @param rAccount + The account (JID) to use. This must be a valid JID that has been + setup with Empathy or another Telepathy client providing + Jabber/XMPP. + + @param rService + "WhatEver", is prepended with "...LibreOffice" + + @param bCreateOwnGMainLoop + Whether to create and iterate an own GMainLoop. For testing + purposes when no GMainLoop is available. + */ + TeleManager( const rtl::OUString& rAccount, const rtl::OUString& rService, bool bCreateOwnGMainLoop = false ); + ~TeleManager(); + + /** Prepare the Telepathy Account Manager. + + Invokes an async call that is not ready until meAccountManagerStatus is + set! Until that is AMS_PREPARED nothing else will work. + + TODO: this needs some signalling mechanism + */ + void prepareAccountManager(); + AccountManagerStatus getAccountManagerStatus() const + { + return meAccountManagerStatus; + } + + /** Start a group session in a MUC. + + @param rConferenceRoom + The MUC to be created/joined, e.g. "LibreOffice". If empty, the + conference's UUID is used. + + @param rConferenceServer + Server to create the MUC on, e.g. "conference.example.org". If + empty, only the conference's UUID is used and rConferenceRoom is + ignored, hopefully resulting in a local DBus tube. + */ + bool startGroupSession( const rtl::OUString& rConferenceRoom, + const rtl::OUString& rConferenceServer ); + + /** Start a session with a buddy. + + @param rBuddy + The buddy to be connected. + */ + bool startBuddySession( const rtl::OUString& rBuddy ); + + void unregisterConference( TeleConferencePtr pConference ); + bool connect(); + void disconnect(); + void acceptTube( TpChannel* pChannel, const char* pAddress ); + + /** Only the callback of prepareAccountManager() is to set this. */ + void setAccountManagerReady( bool bPrepared); + + /** Send data to all registered conferences. */ + bool sendPacket( const TelePacket& rPacket ) const; + + /// "org.freedesktop.Telepathy.Client.LibreOfficeWhatEver" + rtl::OString getFullServiceName() const; + + /// "/org/freedesktop/Telepathy/Client/LibreOfficeWhatEver" + rtl::OString getFullObjectPath() const; + + /// Only for use with MainLoopFlusher + GMainLoop* getMainLoop() const { return mpLoop; } + + GMainContext* getMainContext() const { return (mpLoop ? g_main_loop_get_context( mpLoop) : NULL); } + + static rtl::OString createUuid(); + + + // Only for callbacks. + void setChannelReadyHandlerInvoked( bool b ) { mbChannelReadyHandlerInvoked = b; } + bool isChannelReadyHandlerInvoked() const { return mbChannelReadyHandlerInvoked; } + void setAccountManagerReadyHandlerInvoked( bool b ) { mbAccountManagerReadyHandlerInvoked = b; } + bool isAccountManagerReadyHandlerInvoked() const { return mbAccountManagerReadyHandlerInvoked; } + + typedef bool (TeleManager::*CallBackInvokedFunc)() const; + /** Iterate our GMainLoop, blocking, until the callback is done. */ + void iterateLoop( CallBackInvokedFunc pFunc ); + + typedef bool (TeleConference::*ConferenceCallBackInvokedFunc)() const; + /** Iterate our GMainLoop, blocking, until the callback is done. */ + void iterateLoop( const TeleConference* pConference, ConferenceCallBackInvokedFunc pFunc ); + + /** Iterate our GMainLoop, non-blocking, until nothing pending. */ + void flushLoop() const; + +private: + + rtl::OString maAccountID; + rtl::OString maService; // the "WhatEver" part + TeleConferenceVector maConferences; + GMainLoop* mpLoop; + TpDBusDaemon* mpDBus; + TpAccountManager* mpAccountManager; + TpAccount* mpAccount; + TpConnection* mpConnection; + TpBaseClient* mpClient; + AccountManagerStatus meAccountManagerStatus; + + bool mbChannelReadyHandlerInvoked : 1; + bool mbAccountManagerReadyHandlerInvoked : 1; + + TpAccount* getMyAccount(); + +}; + + +/** The most ugly workaround for not having a GMainLoop running, i.e. in + cppunittest. + */ +class MainLoopFlusher +{ +public: + explicit MainLoopFlusher( const TeleManager* pManager ) + : + mpManager( pManager) + { + flush(); + } + + ~MainLoopFlusher() + { + flush(); + } + + void flush() const + { + mpManager->flushLoop(); + } + +private: + const TeleManager* mpManager; +}; + + +#endif // INCLUDED_TUBES_MANAGER_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/tubes/inc/tubes/packet.hxx b/tubes/inc/tubes/packet.hxx new file mode 100644 index 000000000000..f357782aff66 --- /dev/null +++ b/tubes/inc/tubes/packet.hxx @@ -0,0 +1,69 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * Version: MPL 1.1 / GPLv3+ / LGPLv3+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2012 Red Hat, Inc., Eike Rathke <erack@redhat.com> + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 3 or later (the "GPLv3+"), or + * the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"), + * in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable + * instead of those above. + */ + +#ifndef INCLUDED_TUBES_PACKET_HXX +#define INCLUDED_TUBES_PACKET_HXX + +#include <sal/config.h> +#include <rtl/string.hxx> +#include <rtl/byteseq.hxx> + + +class TelePacket +{ +public: + + TelePacket( const char* pSender, const char* pData, int nSize ) + : + maSender( pSender), + maData( reinterpret_cast<const sal_Int8*>(pData), static_cast<sal_Int32>(nSize) ) + { + } + + TelePacket( const TelePacket& r ) + : + maSender( r.maSender), + maData( r.maData) + { + } + + /** Underlying getArray() ensures reference count is exactly one, hence + this method is non-const! */ + const char* getData() { return reinterpret_cast<const char*>( maData.getArray()); } + sal_Int32 getSize() const { return maData.getLength(); } + +private: + + rtl::OString maSender; + rtl::ByteSequence maData; + +}; + +#endif // INCLUDED_TUBES_PACKET_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/tubes/inc/tubes/tubesdllapi.h b/tubes/inc/tubes/tubesdllapi.h new file mode 100644 index 000000000000..ea1b70770277 --- /dev/null +++ b/tubes/inc/tubes/tubesdllapi.h @@ -0,0 +1,16 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +#ifndef INCLUDED_TUBESDLLAPI_H +#define INCLUDED_TUBESDLLAPI_H + +#include "sal/types.h" + +#if defined(TUBES_DLLIMPLEMENTATION) +#define TUBES_DLLPUBLIC SAL_DLLPUBLIC_EXPORT +#else +#define TUBES_DLLPUBLIC SAL_DLLPUBLIC_IMPORT +#endif +#define TUBES_DLLPRIVATE SAL_DLLPRIVATE + +#endif /* INCLUDED_TUBESDLLAPI_H */ + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/tubes/prj/build.lst b/tubes/prj/build.lst new file mode 100644 index 000000000000..5995a896dfcf --- /dev/null +++ b/tubes/prj/build.lst @@ -0,0 +1,2 @@ +tubes tubes : solenv TELEPATHY:telepathy sal NULL +tubes tubes\prj nmake - all tubes_prj NULL diff --git a/tubes/prj/d.lst b/tubes/prj/d.lst new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/tubes/prj/d.lst diff --git a/tubes/prj/makefile.mk b/tubes/prj/makefile.mk new file mode 100644 index 000000000000..0997622e00f6 --- /dev/null +++ b/tubes/prj/makefile.mk @@ -0,0 +1 @@ +.INCLUDE : gbuildbridge.mk diff --git a/tubes/qa/test_manager.cxx b/tubes/qa/test_manager.cxx new file mode 100644 index 000000000000..acf3da64bd78 --- /dev/null +++ b/tubes/qa/test_manager.cxx @@ -0,0 +1,188 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * Version: MPL 1.1 / GPLv3+ / LGPLv3+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2012 Red Hat, Inc., Eike Rathke <erack@redhat.com> + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 3 or later (the "GPLv3+"), or + * the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"), + * in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable + * instead of those above. + */ + +#include <sal/precppunit.hxx> + +#include <tubes/manager.hxx> + +#include <cppunit/TestAssert.h> +#include <cppunit/TestFixture.h> +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/plugin/TestPlugIn.h> +#include <rtl/string.hxx> +#include <rtl/ustring.hxx> + +namespace { + +class TestTeleTubes: public CppUnit::TestFixture +{ +public: + + TestTeleTubes(); + ~TestTeleTubes(); + void testSetupManager1(); + void testSetupManager2(); + void testConnect1(); + void testConnect2(); + void testPrepareAccountManager1(); + void testPrepareAccountManager2(); + void testStartBuddySession1(); + void testStartBuddySession2(); + void testSendPacket(); + void testFlushLoops(); + void testDestroyManager1(); + void testDestroyManager2(); + void testFailAlways(); + + // Order is significant. + CPPUNIT_TEST_SUITE( TestTeleTubes ); + CPPUNIT_TEST( testSetupManager1 ); + CPPUNIT_TEST( testSetupManager2 ); + CPPUNIT_TEST( testConnect1 ); + CPPUNIT_TEST( testConnect2 ); + CPPUNIT_TEST( testPrepareAccountManager1 ); + CPPUNIT_TEST( testPrepareAccountManager2 ); + CPPUNIT_TEST( testStartBuddySession1 ); + CPPUNIT_TEST( testStartBuddySession2 ); + CPPUNIT_TEST( testSendPacket ); + CPPUNIT_TEST( testFlushLoops ); + CPPUNIT_TEST( testDestroyManager1 ); + CPPUNIT_TEST( testDestroyManager2 ); + CPPUNIT_TEST( testFailAlways ); // need failure to display SAL_LOG, comment out for real builds + CPPUNIT_TEST_SUITE_END(); + +private: + + void runSetupManager( TeleManager* pManager, const rtl::OUString& rBuddy ); +}; + +// static, not members, so they actually survive cppunit test iteration +static TeleManager* mpManager1 = NULL; +static TeleManager* mpManager2 = NULL; + +// XXX The accounts need to be setup in Empathy and a jabber daemon needs +// to be running on localhost.localdomain and both accounts need to be +// enabled and connected. +static rtl::OUString sAcc1( RTL_CONSTASCII_USTRINGPARAM( "libo1@localhost.localdomain")); +static rtl::OUString sAcc2( RTL_CONSTASCII_USTRINGPARAM( "libo2@localhost.localdomain")); +static rtl::OUString sService( RTL_CONSTASCII_USTRINGPARAM( "TeleTest")); + +TestTeleTubes::TestTeleTubes() +{ +} + +TestTeleTubes::~TestTeleTubes() +{ +} + +void TestTeleTubes::testSetupManager1() +{ + mpManager1 = new TeleManager( sAcc1, sService, true); +} + +void TestTeleTubes::testSetupManager2() +{ + mpManager2 = new TeleManager( sAcc2, sService); +} + +void TestTeleTubes::testPrepareAccountManager1() +{ + mpManager1->prepareAccountManager(); + TeleManager::AccountManagerStatus eStatus = mpManager1->getAccountManagerStatus(); + CPPUNIT_ASSERT( eStatus == TeleManager::AMS_PREPARED); +} + +void TestTeleTubes::testPrepareAccountManager2() +{ + mpManager2->prepareAccountManager(); + TeleManager::AccountManagerStatus eStatus = mpManager2->getAccountManagerStatus(); + CPPUNIT_ASSERT( eStatus == TeleManager::AMS_PREPARED); +} + +void TestTeleTubes::testStartBuddySession1() +{ + bool bStarted = mpManager1->startBuddySession( sAcc2); + CPPUNIT_ASSERT( bStarted == true); +} + +void TestTeleTubes::testStartBuddySession2() +{ + bool bStarted = mpManager2->startBuddySession( sAcc1); + CPPUNIT_ASSERT( bStarted == true); +} + +void TestTeleTubes::testConnect1() +{ + bool bConnected = mpManager1->connect(); + CPPUNIT_ASSERT( bConnected == true); +} + +void TestTeleTubes::testConnect2() +{ + bool bConnected = mpManager2->connect(); + CPPUNIT_ASSERT( bConnected == true); +} + +void TestTeleTubes::testSendPacket() +{ + TelePacket aPacket( "", RTL_CONSTASCII_STRINGPARAM( "from 1 to 2")); + bool bPacketSent = mpManager1->sendPacket( aPacket); + CPPUNIT_ASSERT( bPacketSent == true); +} + +void TestTeleTubes::testFlushLoops() +{ + mpManager1->flushLoop(); + mpManager2->flushLoop(); +} + +void TestTeleTubes::testDestroyManager1() +{ + delete mpManager1; + mpManager1 = NULL; +} + +void TestTeleTubes::testDestroyManager2() +{ + delete mpManager2; + mpManager2 = NULL; +} + +void TestTeleTubes::testFailAlways() +{ + CPPUNIT_ASSERT( false); +} + + +CPPUNIT_TEST_SUITE_REGISTRATION( TestTeleTubes); + +} + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/tubes/source/conference.cxx b/tubes/source/conference.cxx new file mode 100644 index 000000000000..3195df6bceab --- /dev/null +++ b/tubes/source/conference.cxx @@ -0,0 +1,384 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * Version: MPL 1.1 / GPLv3+ / LGPLv3+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2012 Red Hat, Inc., Eike Rathke <erack@redhat.com> + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 3 or later (the "GPLv3+"), or + * the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"), + * in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable + * instead of those above. + */ + +#include <tubes/conference.hxx> +#include <tubes/manager.hxx> + + +#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)) +#define INFO_LOGGER(s) InfoLogger aLogger(this,(s)) +#else +#define INFO_LOGGER_F(s) +#define INFO_LOGGER(s) +#endif // SAL_LOG_INFO + + +static DBusHandlerResult TeleConference_DBusMessageHandler( + DBusConnection* pConnection, + DBusMessage* pMessage, + void* pUserData + ) +{ + INFO_LOGGER_F( "TeleConference_DBusMessageHandler"); + + SAL_WARN_IF( !pConnection || !pMessage, "tubes", "TeleConference_DBusMessageHandler: unhandled"); + if (!pConnection || !pMessage) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + TeleConference* pConference = reinterpret_cast<TeleConference*>(pUserData); + SAL_WARN_IF( !pConference, "tubes", "TeleConference_DBusMessageHandler: no conference"); + if (!pConference) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + TeleManager* pManager = pConference->getManager(); + SAL_WARN_IF( !pManager, "tubes", "TeleConference_DBusMessageHandler: no manager"); + if (!pManager) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + if (dbus_message_is_method_call( pMessage, pManager->getFullServiceName().getStr(), LIBO_TUBES_DBUS_MSG_METHOD)) + { + const char* pSender = dbus_message_get_sender( pMessage); + + DBusError aDBusError; + dbus_error_init( &aDBusError); + const char* pPacketData = 0; + int nPacketSize = 0; + if (dbus_message_get_args( pMessage, &aDBusError, + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &pPacketData, &nPacketSize, + DBUS_TYPE_INVALID)) + { + SAL_INFO( "tubes", "TeleConference_DBusMessageHandler: received packet from sender " + << pSender << " with size " << nPacketSize); + pConference->queue( pSender, pPacketData, nPacketSize); + return DBUS_HANDLER_RESULT_HANDLED; + } + else + { + SAL_INFO( "tubes", "TeleConference_DBusMessageHandler: unhandled message from sender " + << pSender << " " << aDBusError.message); + dbus_error_free( &aDBusError); + } + } + else + { + SAL_INFO( "tubes", "TeleConference_DBusMessageHandler: unhandled method"); + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + + +static void TeleConference_ChannelCloseHandler( + TpChannel* /*proxy*/, + const GError* pError, + gpointer pUserData, + GObject* /*weak_object*/ + ) +{ + INFO_LOGGER_F( "TeleConference_ChannelCloseHandler"); + + TeleConference* pConference = reinterpret_cast<TeleConference*>(pUserData); + SAL_WARN_IF( !pConference, "tubes", "TeleConference_ChannelCloseHandler: no conference"); + if (!pConference) + return; + + if (pError) + { + SAL_WARN( "tubes", "TeleConference_ChannelCloseHandler: entered with error: " << pError->message); + pConference->finalize(); + return; + } + + pConference->finalize(); +} + + +static void TeleConference_TubeOfferedHandler( + TpChannel* pChannel, + const gchar* pOutAddress, + const GError* pError, + gpointer pUserData, + GObject* /*weak_object*/ + ) +{ + INFO_LOGGER_F( "TeleConference_TubeOfferedHandler"); + + // "can't find contact ... presence" means contact is not a contact. + /* FIXME: detect and handle */ + SAL_WARN_IF( pError, "tubes", "TeleConference_TubeOfferedHandler: entered with error: " << pError->message); + if (pError) + return; + + SAL_WARN_IF( !pOutAddress, "tubes", "TeleConference_TubeOfferedHandler: no out address"); + if (!pOutAddress) + return; + + TeleConference* pConference = reinterpret_cast<TeleConference*>(pUserData); + SAL_WARN_IF( !pConference, "tubes", "TeleConference_TubeOfferedHandler: no conference"); + if (!pConference) + return; + + SAL_WARN_IF( pChannel != pConference->getChannel(), "tubes", "TeleConference_TubeOfferedHandler: not my channel"); + if (pChannel != pConference->getChannel()) + return; + + TeleManager* pManager = pConference->getManager(); + SAL_WARN_IF( !pManager, "tubes", "TeleConference_TubeOfferedHandler: no manager"); + if (!pManager) + return; + + pConference->setTubeOfferedHandlerInvoked( true); + + DBusError aDBusError; + dbus_error_init( &aDBusError); + DBusConnection* pTube = dbus_connection_open_private( pOutAddress, &aDBusError); + if (!pTube) + { + SAL_WARN( "tubes", "TeleConference_TubeOfferedHandler: no dbus connection: " << aDBusError.message); + dbus_error_free( &aDBusError); + return; + } + + pConference->setTube( pTube); +} + + +TeleConference::TeleConference( TeleManager* pManager, TpChannel* pChannel, const rtl::OString& rSessionId ) + : + maSessionId( rSessionId ), + mpManager( pManager), + mpChannel( pChannel), + mpTube( NULL), + mbTubeOfferedHandlerInvoked( false) +{ + if (mpChannel) + g_object_ref( mpChannel); +} + + +TeleConference::~TeleConference() +{ + // Do nothing here, we're destructed from finalize() +} + + +void TeleConference::setTarget( const rtl::OString& rTarget ) +{ + maTarget = rTarget; +} + + +void TeleConference::setChannel( TpChannel* pChannel ) +{ + OSL_ENSURE( !mpChannel, "TeleConference::setChannel: already have channel"); + if (mpChannel) + g_object_unref( mpChannel); + mpChannel = pChannel; + if (mpChannel) + g_object_ref( mpChannel); +} + + +bool TeleConference::acceptTube( const char* pAddress ) +{ + INFO_LOGGER( "TeleConference::acceptTube"); + + SAL_WARN_IF( !pAddress, "tubes", "TeleConference::acceptTube: no address"); + if (!pAddress) + return false; + SAL_INFO( "tubes", "TeleConference::acceptTube: address: " << pAddress); + + SAL_WARN_IF( !mpChannel, "tubes", "TeleConference::acceptTube: no channel setup"); + SAL_WARN_IF( mpTube, "tubes", "TeleConference::acceptTube: already tubed"); + if (!mpChannel || mpTube) + return false; + + DBusError aDBusError; + dbus_error_init( &aDBusError); + mpTube = dbus_connection_open_private( pAddress, &aDBusError); + if (!mpTube) + { + SAL_WARN( "tubes", "TeleConference::acceptTube: no dbus connection: " << aDBusError.message); + dbus_error_free( &aDBusError); + return false; + } + + dbus_connection_setup_with_g_main( mpTube, mpManager->getMainContext()); + dbus_connection_add_filter( mpTube, TeleConference_DBusMessageHandler, this, NULL); + + return true; +} + + +bool TeleConference::offerTube() +{ + INFO_LOGGER( "TeleConference::offerTube"); + + OSL_ENSURE( mpChannel, "TeleConference::offerTube: no channel"); + if (!mpChannel) + return false; + + setTubeOfferedHandlerInvoked( false); + + // There must be a hash table with some content. + /* TODO: anything meaningful to go in here? */ + GHashTable* pParams = tp_asv_new( + "LibreOffice", G_TYPE_STRING, "Collaboration", + NULL); + + tp_cli_channel_type_dbus_tube_call_offer( + mpChannel, // proxy + -1, // timeout_ms + pParams, // in_parameters + TP_SOCKET_ACCESS_CONTROL_LOCALHOST, // in_access_control + TeleConference_TubeOfferedHandler, // callback + this, // user_data + NULL, // destroy + NULL); // weak_object + + mpManager->iterateLoop( this, &TeleConference::isTubeOfferedHandlerInvoked); + + g_hash_table_unref( pParams); + + return true; +} + + +bool TeleConference::setTube( DBusConnection* pTube ) +{ + INFO_LOGGER( "TeleConference::setTube"); + + OSL_ENSURE( !mpTube, "TeleConference::setTube: already tubed"); + mpTube = pTube; + + dbus_connection_setup_with_g_main( mpTube, mpManager->getMainContext()); + dbus_connection_add_filter( mpTube, TeleConference_DBusMessageHandler, this, NULL); + + /* TODO: anything else? */ + + return true; +} + + +void TeleConference::close() +{ + INFO_LOGGER( "TeleConference::close"); + + if (mpChannel) + tp_cli_channel_call_close( mpChannel, 5000, TeleConference_ChannelCloseHandler, this, NULL, NULL); + else + finalize(); +} + + +void TeleConference::finalize() +{ + INFO_LOGGER( "TeleConference::finalize"); + + if (mpChannel) + { + g_object_unref( mpChannel); + mpChannel = NULL; + } + + if (mpTube) + { + dbus_connection_remove_filter( mpTube, TeleConference_DBusMessageHandler, this); + dbus_connection_close( mpTube); + dbus_connection_unref( mpTube); + mpTube = NULL; + } + + TeleConferencePtr pThis( shared_from_this()); + mpManager->unregisterConference( pThis); + + //! *this gets destructed here! +} + + +bool TeleConference::sendPacket( TelePacket& rPacket ) const +{ + INFO_LOGGER( "TeleConference::sendPacket"); + + OSL_ENSURE( mpManager, "tubes: TeleConference::sendPacket: no TeleManager"); + SAL_WARN_IF( !mpTube, "tubes", "TeleConference::sendPacket: no tube"); + if (!(mpManager && mpTube)) + return false; + + DBusMessage* pMessage = dbus_message_new_method_call( + mpManager->getFullServiceName().getStr(), + mpManager->getFullObjectPath().getStr(), + mpManager->getFullServiceName().getStr(), + LIBO_TUBES_DBUS_MSG_METHOD); + SAL_WARN_IF( !pMessage, "tubes", "TeleConference::sendPacket: no DBusMessage"); + if (!pMessage) + return false; + + const char* pPacketData = rPacket.getData(); + dbus_message_append_args( pMessage, + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &pPacketData, rPacket.getSize(), + DBUS_TYPE_INVALID); + + SAL_INFO( "tubes", "TeleConference::sendPacket: from " << + dbus_message_get_sender( pMessage) << " to " << dbus_message_get_destination( pMessage)); + + bool bSent = dbus_connection_send( mpTube, pMessage, NULL); + SAL_WARN_IF( !bSent, "tubes", "TeleConference::sendPacket: not sent"); + dbus_message_unref( pMessage); + return bSent; +} + + +void TeleConference::queue( const char* pDBusSender, const char* pPacketData, int nPacketSize ) +{ + INFO_LOGGER( "TeleConference::queue"); + + maPacketQueue.push( TelePacket( pDBusSender, pPacketData, nPacketSize)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/tubes/source/manager.cxx b/tubes/source/manager.cxx new file mode 100644 index 000000000000..ba9185258469 --- /dev/null +++ b/tubes/source/manager.cxx @@ -0,0 +1,668 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * Version: MPL 1.1 / GPLv3+ / LGPLv3+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2012 Red Hat, Inc., Eike Rathke <erack@redhat.com> + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 3 or later (the "GPLv3+"), or + * the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"), + * in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable + * instead of those above. + */ + +#include "tubes/manager.hxx" +#include <rtl/strbuf.hxx> +#include <rtl/uuid.h> + + +#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)) +#define INFO_LOGGER(s) InfoLogger aLogger(this,(s)) +#else +#define INFO_LOGGER_F(s) +#define INFO_LOGGER(s) +#endif // SAL_LOG_INFO + + +using namespace rtl; + +// To form "org.freedesktop.Telepathy.Client.LibreOfficeWhatEver" (bus name) +// or "/org/freedesktop/Telepathy/Client/LibreOfficeWhatEver" (object path) +#define LIBO_TP_NAME_PREFIX "LibreOffice" + + +static void TeleManager_DBusTubeAcceptHandler( + TpChannel* pChannel, + const char* pAddress, + const GError* pError, + gpointer pUserData, + GObject* /*weak_object*/) +{ + INFO_LOGGER_F( "TeleManager_DBusTubeAcceptHandler"); + + SAL_WARN_IF( pError, "tubes", "TeleManager_DBusTubeAcceptHandler: entered with error: " << pError->message); + if (pError) + return; + + TeleManager* pManager = reinterpret_cast<TeleManager*>(pUserData); + SAL_WARN_IF( !pManager, "tubes", "TeleManager_DBusTubeAcceptHandler: no manager"); + if (!pManager) + return; + + pManager->acceptTube( pChannel, pAddress); +} + + +static void TeleManager_DBusChannelHandler( + TpSimpleHandler* /*handler*/, + TpAccount* /*account*/, + TpConnection* /*connection*/, + GList* pChannels, + GList* /*requests_satisfied*/, + gint64 /*user_action_time*/, + TpHandleChannelsContext* pContext, + gpointer pUserData) +{ + INFO_LOGGER_F( "TeleManager_DBusChannelHandler"); + + TeleManager* pManager = reinterpret_cast<TeleManager*>(pUserData); + SAL_WARN_IF( !pManager, "tubes", "TeleManager_DBusChannelHandler: no manager"); + if (!pManager) + return; + + 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_channel_get_channel_type_id( pChannel) == TP_IFACE_QUARK_CHANNEL_TYPE_DBUS_TUBE) + { + SAL_INFO( "tubes", "accepting"); + tp_cli_channel_type_dbus_tube_call_accept( pChannel, -1, + TP_SOCKET_ACCESS_CONTROL_LOCALHOST, + TeleManager_DBusTubeAcceptHandler, pUserData, NULL, NULL); + } + else + { + SAL_INFO( "tubes", "ignored"); + } + } + + tp_handle_channels_context_accept( pContext); +} + + +static void TeleManager_ChannelReadyHandler( + GObject* pSourceObject, + GAsyncResult* pResult, + gpointer pUserData + ) +{ + INFO_LOGGER_F( "TeleManager_ChannelReadyHandler"); + + TeleConference* pConference = reinterpret_cast<TeleConference*>(pUserData); + SAL_WARN_IF( !pConference, "tubes", "TeleManager_ChannelReadyHandler: no conference"); + if (!pConference) + return; + + TeleManager* pManager = pConference->getManager(); + SAL_WARN_IF( !pManager, "tubes", "TeleManager_ChannelReadyHandler: no manager"); + if (!pManager) + return; + + pManager->setChannelReadyHandlerInvoked( true); + + GError* pError = NULL; + TpChannel * pChannel = tp_account_channel_request_create_and_handle_channel_finish( + TP_ACCOUNT_CHANNEL_REQUEST( pSourceObject), pResult, NULL, &pError); + if (!pChannel) + { + // "account isn't Enabled" means just that.. + /* FIXME: detect and handle, domain=132, code=3 */ + SAL_WARN( "tubes", "TeleManager_ChannelReadyHandler: no channel: " << pError->message); + g_error_free( pError); + return; + } + + pConference->setChannel( pChannel); + pConference->offerTube(); +} + + +static void TeleManager_AccountManagerReadyHandler( + GObject* pSourceObject, + GAsyncResult* pResult, + gpointer pUserData + ) +{ + INFO_LOGGER_F( "TeleManager_AccountManagerReadyHandler"); + + TeleManager* pManager = reinterpret_cast<TeleManager*>(pUserData); + SAL_WARN_IF( !pManager, "tubes", "TeleManager_AccountManagerReadyHandler: no manager"); + if (!pManager) + return; + + pManager->setAccountManagerReadyHandlerInvoked( true); + + GError* pError = NULL; + gboolean bPrepared = tp_proxy_prepare_finish( pSourceObject, pResult, &pError); + SAL_WARN_IF( !bPrepared, "tubes", "TeleManager_AccountManagerReadyHandler: not prepared"); + if (!bPrepared || pError) + { + SAL_WARN_IF( pError, "tubes", "TeleManager_AccountManagerReadyHandler: error: " << pError->message); + g_error_free( pError); + } + + pManager->setAccountManagerReady( bPrepared); +} + + +TeleManager::TeleManager( const rtl::OUString& rAccount, const rtl::OUString& rService, bool bCreateOwnGMainLoop ) + : + maAccountID( OUStringToOString( rAccount, RTL_TEXTENCODING_UTF8)), + mpLoop( NULL), + mpDBus( NULL), + mpAccountManager( NULL), + mpAccount( NULL), + mpConnection( NULL), + mpClient( NULL), + meAccountManagerStatus( AMS_UNINITIALIZED), + mbChannelReadyHandlerInvoked( false), + mbAccountManagerReadyHandlerInvoked( false) +{ + OStringBuffer aBuf(64); + aBuf.append( RTL_CONSTASCII_STRINGPARAM( LIBO_TP_NAME_PREFIX)).append( + OUStringToOString( rService, RTL_TEXTENCODING_UTF8)); + maService = aBuf.makeStringAndClear(); + // Ensure a clean name suitable for object paths. + maService.replace( '.', '_'); + + // The glib object types need to be initialized, else we aren't going + // anywhere. + g_type_init(); + + // We need a main loop, else no callbacks. + /* TODO: could the loop be run in another thread? */ + if (bCreateOwnGMainLoop) + mpLoop = g_main_loop_new( NULL, FALSE); +} + + +TeleManager::~TeleManager() +{ + disconnect(); + + if (mpDBus) + g_object_unref( mpDBus); + if (mpAccountManager) + g_object_unref( mpAccountManager); + if (mpAccount) + g_object_unref( mpAccount); + if (mpConnection) + g_object_unref( mpConnection); + if (mpClient) + g_object_unref( mpClient); + + if (mpLoop) + g_main_loop_unref( mpLoop); +} + + +bool TeleManager::connect() +{ + INFO_LOGGER( "TeleManager::connect"); + + MainLoopFlusher aFlusher( this); + + SAL_WARN_IF( mpDBus || mpClient, "tubes", "TeleManager::connect: already connected"); + if (mpDBus || mpClient) + return false; + + GError* pError = NULL; + mpDBus = tp_dbus_daemon_dup( &pError); + SAL_WARN_IF( !mpDBus, "tubes", "TeleManager::connect: no dbus daemon"); + if (!mpDBus || pError) + { + SAL_WARN_IF( pError, "tubes", "TeleManager::connect: dbus daemon error: " << pError->message); + g_error_free( pError); + return false; + } + + TpSimpleClientFactory* pFactory = tp_simple_client_factory_new( mpDBus); + SAL_WARN_IF( !pFactory, "tubes", "TeleManager::connect: no client factory"); + if (!pFactory) + return false; + + /* TODO: does that still work with uniquify? Or is the service not + * recognized anymore? Without uniquify it is not possible to register two + * instances of clients on the same DBus. */ + + /* FIXME: testing, first real, all others uniquified */ + static gboolean bUniquify = FALSE; + + mpClient = tp_simple_handler_new_with_factory( + pFactory, // factory + TRUE, // bypass_approval + FALSE, // requests + maService.getStr(), // name + bUniquify, // uniquify + TeleManager_DBusChannelHandler, // callback + this, // user_data + NULL // destroy + ); + bUniquify = TRUE; + SAL_WARN_IF( !mpClient, "tubes", "TeleManager::connect: no client"); + if (!mpClient) + return false; + + // Setup client handler for buddy channels with our service. + tp_base_client_take_handler_filter( 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)); + +/* FIXME: once we can handle MUCs, this is additional to buddy channels! */ +#if 0 + // Setup client handler for MUC channels with our service. + tp_base_client_take_handler_filter( 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)); +#endif + + if (!tp_base_client_register( mpClient, &pError)) + { + SAL_WARN( "tubes", "TeleManager::connect: error registering client handler: " << pError->message); + g_error_free( pError); + return false; + } + + SAL_INFO( "tubes", "TeleManager::connect: bus name: " << tp_base_client_get_bus_name( mpClient)); + SAL_INFO( "tubes", "TeleManager::connect: object path: " << tp_base_client_get_object_path( mpClient)); + + return true; +} + + +/* TODO: factor out common code with startBuddySession() */ +bool TeleManager::startGroupSession( const rtl::OUString& rUConferenceRoom, const rtl::OUString& rUConferenceServer ) +{ + INFO_LOGGER( "TeleManager::startGroupSession"); + + MainLoopFlusher aFlusher( this); + + if (!getMyAccount()) + return false; + + OString aSessionId( TeleManager::createUuid()); + + TeleConferencePtr pConference( new TeleConference( this, NULL, aSessionId)); + maConferences.push_back( pConference); + + /* TODO: associate the document with this session and conference */ + + /* 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()); + pConference->setTarget( aTarget); + + SAL_INFO( "tubes", "TeleManager::startGroupSession: creating channel request from " + << maAccountID.getStr() << " 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( + mpAccount, pRequest, TP_USER_ACTION_TIME_NOT_USER_ACTION); + g_hash_table_unref( pRequest); + SAL_WARN_IF( !pChannelRequest, "tubes", "TeleManager::startGroupSession: no channel"); + if (!pChannelRequest) + return false; + + setChannelReadyHandlerInvoked( false); + + tp_account_channel_request_create_and_handle_channel_async( + pChannelRequest, NULL, TeleManager_ChannelReadyHandler, pConference.get()); + + iterateLoop( &TeleManager::isChannelReadyHandlerInvoked); + + return pConference->getChannel() != NULL; +} + + +/* TODO: factor out common code with startGroupSession() */ +bool TeleManager::startBuddySession( const rtl::OUString& rBuddy ) +{ + INFO_LOGGER( "TeleManager::startBuddySession"); + + MainLoopFlusher aFlusher( this); + + if (!getMyAccount()) + return false; + + OString aSessionId( TeleManager::createUuid()); + + TeleConferencePtr pConference( new TeleConference( this, NULL, aSessionId)); + maConferences.push_back( pConference); + + /* TODO: associate the document with this session and conference */ + + OString aTarget( OUStringToOString( rBuddy, RTL_TEXTENCODING_UTF8)); + pConference->setTarget( aTarget); + + SAL_INFO( "tubes", "TeleManager::startBuddySession: creating channel request from " + << maAccountID.getStr() << " to " << aTarget.getStr()); + + 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, aTarget.getStr(), + TP_PROP_CHANNEL_TYPE_DBUS_TUBE_SERVICE_NAME, G_TYPE_STRING, getFullServiceName().getStr(), + NULL); + + TpAccountChannelRequest * pChannelRequest = tp_account_channel_request_new( + mpAccount, 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 false; + } + + setChannelReadyHandlerInvoked( false); + + tp_account_channel_request_create_and_handle_channel_async( + pChannelRequest, NULL, TeleManager_ChannelReadyHandler, pConference.get()); + + iterateLoop( &TeleManager::isChannelReadyHandlerInvoked); + + g_object_unref( pChannelRequest); + g_hash_table_unref( pRequest); + + return pConference->getChannel() != NULL; +} + + +void TeleManager::prepareAccountManager() +{ + INFO_LOGGER( "TeleManager::prepareAccountManager"); + + MainLoopFlusher aFlusher( this); + + SAL_WARN_IF( meAccountManagerStatus == AMS_INPREPARATION, "tubes", + "TeleManager::prepareAccountManager: already in preparation"); + if (meAccountManagerStatus == AMS_INPREPARATION) + return; + + SAL_WARN_IF( meAccountManagerStatus != AMS_UNINITIALIZED, "tubes", + "TeleManager::prepareAccountManager: yet another attempt"); + + if (!mpAccountManager) + { + mpAccountManager = tp_account_manager_dup(); + SAL_WARN_IF( !mpAccountManager, "tubes", "TeleManager::prepareAccountManager: no account manager"); + if (!mpAccountManager) + return; + g_object_ref( mpAccountManager); + } + + meAccountManagerStatus = AMS_INPREPARATION; + setAccountManagerReadyHandlerInvoked( false); + + tp_proxy_prepare_async( mpAccountManager, NULL, TeleManager_AccountManagerReadyHandler, this); + + iterateLoop( &TeleManager::isAccountManagerReadyHandlerInvoked); +} + + +TpAccount* TeleManager::getMyAccount() +{ + INFO_LOGGER( "TeleManager::getMyAccount"); + + MainLoopFlusher aFlusher( this); + + if (mpAccount) + return mpAccount; + + SAL_WARN_IF( meAccountManagerStatus != AMS_PREPARED, "tubes", + "TeleManager::getMyAccount: Account Manager not prepared"); + if (meAccountManagerStatus != AMS_PREPARED) + return NULL; + + GList* pAccounts = tp_account_manager_get_valid_accounts( mpAccountManager); + SAL_WARN_IF( !pAccounts, "tubes", "TeleManager::getMyAccount: no valid accounts"); + if (!pAccounts) + return NULL; + + // Find our account to use. + for (GList* pA = pAccounts; pA; pA = pA->next) + { + TpAccount* pAcc = TP_ACCOUNT( pA->data); + const GHashTable* pPar = tp_account_get_parameters( pAcc); + const gchar* pID = tp_asv_get_string( pPar, "account"); + SAL_WARN_IF( !pID, "tubes", "TeleManager::getMyAccount: account without account??"); + if (pID && maAccountID == pID) + { + mpAccount = pAcc; + break; // for + } + } + + g_list_free( pAccounts); + SAL_WARN_IF( !mpAccount, "tubes", "TeleManager::getMyAccount: no account"); + if (!mpAccount) + return NULL; + + g_object_ref( mpAccount); + return mpAccount; +} + + +bool TeleManager::sendPacket( const TelePacket& rPacket ) const +{ + INFO_LOGGER( "TeleManager::sendPacket"); + + MainLoopFlusher aFlusher( this); + + bool bToSend = false; + bool bSent = true; + // Access to data ByteStream array forces reference count of one, provide + // non-const instance here before passing it down to each conference. + TelePacket aPacket( rPacket); + for (TeleConferenceVector::const_iterator it = maConferences.begin(); it != maConferences.end(); ++it) + { + bToSend = true; + if (!(*it)->sendPacket( aPacket)) + bSent = false; + } + return bSent && bToSend; +} + + +void TeleManager::unregisterConference( TeleConferencePtr pConference ) +{ + TeleConferenceVector::iterator it = ::std::find( maConferences.begin(), maConferences.end(), pConference); + if (it != maConferences.end()) + maConferences.erase( it); +} + + +void TeleManager::disconnect() +{ + INFO_LOGGER( "TeleManager::disconnect"); + + //! No MainLoopFlusher here! + + if (!mpClient) + return; + + tp_base_client_unregister( mpClient); + mpClient = NULL; + + size_t nSize = maConferences.size(); + for (size_t i=0; i < nSize; /*nop*/) + { + maConferences[i]->close(); + // close() may remove the conference from the vector and move following + // elements to this position (hence we don't use an iterator here), + // adjust accordingly. + size_t n = maConferences.size(); + if (n < nSize) + nSize = n; + else + ++i; + } +} + + +void TeleManager::acceptTube( TpChannel* pChannel, const char* pAddress ) +{ + INFO_LOGGER( "TeleManager::acceptTube"); + + SAL_INFO( "tubes", "TeleManager::acceptTube: address " << pAddress); + + MainLoopFlusher aFlusher( this); + + SAL_WARN_IF( !pChannel || !pAddress, "tubes", "TeleManager::acceptTube: no channel or no address"); + if (!pChannel || !pAddress) + return; + + TeleConferencePtr pConference( new TeleConference( this, pChannel, "")); + maConferences.push_back( pConference); + pConference->acceptTube( pAddress); +} + + +void TeleManager::setAccountManagerReady( bool bPrepared) +{ + meAccountManagerStatus = (bPrepared ? AMS_PREPARED : AMS_UNPREPARABLE); +} + + +rtl::OString TeleManager::getFullServiceName() const +{ + OStringBuffer aBuf(64); + aBuf.append( RTL_CONSTASCII_STRINGPARAM( TP_CLIENT_BUS_NAME_BASE)).append( maService); + return aBuf.makeStringAndClear(); +} + + +rtl::OString TeleManager::getFullObjectPath() const +{ + OStringBuffer aBuf(64); + aBuf.append( RTL_CONSTASCII_STRINGPARAM( TP_CLIENT_OBJECT_PATH_BASE)).append( maService); + return aBuf.makeStringAndClear(); +} + + +void TeleManager::iterateLoop( CallBackInvokedFunc pFunc ) +{ + GMainContext* pContext = (mpLoop ? g_main_loop_get_context( mpLoop) : NULL); + while (!(this->*pFunc)()) + { + g_main_context_iteration( pContext, TRUE); + SAL_INFO( "tubes.loop", "TeleManager::iterateLoop"); + } +} + + +void TeleManager::iterateLoop( const TeleConference* pConference, ConferenceCallBackInvokedFunc pFunc ) +{ + GMainContext* pContext = (mpLoop ? g_main_loop_get_context( mpLoop) : NULL); + while (!(pConference->*pFunc)()) + { + g_main_context_iteration( pContext, TRUE); + SAL_INFO( "tubes.loop", "TeleManager::iterateLoop conference"); + } +} + + +void TeleManager::flushLoop() const +{ + if (mpLoop) + { + GMainContext* pContext = g_main_loop_get_context( mpLoop); + while (g_main_context_iteration( pContext, FALSE)) + { + SAL_INFO( "tubes.loop", "TeleManager::flushLoop"); + } + } +} + + +// static +rtl::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 rtl::OString( aBuf); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/tubes/source/packet.cxx b/tubes/source/packet.cxx new file mode 100644 index 000000000000..279398603270 --- /dev/null +++ b/tubes/source/packet.cxx @@ -0,0 +1,33 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * Version: MPL 1.1 / GPLv3+ / LGPLv3+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2012 Red Hat, Inc., Eike Rathke <erack@redhat.com> + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 3 or later (the "GPLv3+"), or + * the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"), + * in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable + * instead of those above. + */ + +#include <tubes/packet.hxx> + + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |