From f487e366fb7488f053fef90514b22ac170e6609d Mon Sep 17 00:00:00 2001 From: Barrett17 Date: Wed, 24 Jun 2015 16:08:12 +0000 Subject: [PATCH] Add remaining part of libgloox. --- libs/libgloox/adhocplugin.h | 61 +++ libs/libgloox/atomicrefcount.cpp | 93 ++++ libs/libgloox/atomicrefcount.h | 75 ++++ libs/libgloox/carbons.cpp | 103 +++++ libs/libgloox/carbons.h | 185 ++++++++ libs/libgloox/forward.cpp | 85 ++++ libs/libgloox/forward.h | 104 +++++ libs/libgloox/iodata.cpp | 226 ++++++++++ libs/libgloox/iodata.h | 186 ++++++++ libs/libgloox/jinglecontent.cpp | 104 +++++ libs/libgloox/jinglecontent.h | 138 ++++++ libs/libgloox/jinglefiletransfer.cpp | 173 ++++++++ libs/libgloox/jinglefiletransfer.h | 134 ++++++ libs/libgloox/jingleiceudp.cpp | 113 +++++ libs/libgloox/jingleiceudp.h | 141 ++++++ libs/libgloox/jingleplugin.h | 176 ++++++++ libs/libgloox/jinglepluginfactory.cpp | 91 ++++ libs/libgloox/jinglepluginfactory.h | 82 ++++ libs/libgloox/jinglesession.cpp | 501 +++++++++++++++++++++ libs/libgloox/jinglesession.h | 575 +++++++++++++++++++++++++ libs/libgloox/jinglesessionhandler.h | 74 ++++ libs/libgloox/jinglesessionmanager.cpp | 104 +++++ libs/libgloox/jinglesessionmanager.h | 117 +++++ libs/libgloox/linklocal.h | 79 ++++ libs/libgloox/linklocalclient.cpp | 219 ++++++++++ libs/libgloox/linklocalclient.h | 125 ++++++ libs/libgloox/linklocalhandler.h | 66 +++ libs/libgloox/linklocalmanager.cpp | 255 +++++++++++ libs/libgloox/linklocalmanager.h | 356 +++++++++++++++ libs/libgloox/mucinvitationhandler.cpp | 27 ++ libs/libgloox/version.rc | 31 ++ 31 files changed, 4799 insertions(+) create mode 100644 libs/libgloox/adhocplugin.h create mode 100644 libs/libgloox/atomicrefcount.cpp create mode 100644 libs/libgloox/atomicrefcount.h create mode 100644 libs/libgloox/carbons.cpp create mode 100644 libs/libgloox/carbons.h create mode 100644 libs/libgloox/forward.cpp create mode 100644 libs/libgloox/forward.h create mode 100644 libs/libgloox/iodata.cpp create mode 100644 libs/libgloox/iodata.h create mode 100644 libs/libgloox/jinglecontent.cpp create mode 100644 libs/libgloox/jinglecontent.h create mode 100644 libs/libgloox/jinglefiletransfer.cpp create mode 100644 libs/libgloox/jinglefiletransfer.h create mode 100644 libs/libgloox/jingleiceudp.cpp create mode 100644 libs/libgloox/jingleiceudp.h create mode 100644 libs/libgloox/jingleplugin.h create mode 100644 libs/libgloox/jinglepluginfactory.cpp create mode 100644 libs/libgloox/jinglepluginfactory.h create mode 100644 libs/libgloox/jinglesession.cpp create mode 100644 libs/libgloox/jinglesession.h create mode 100644 libs/libgloox/jinglesessionhandler.h create mode 100644 libs/libgloox/jinglesessionmanager.cpp create mode 100644 libs/libgloox/jinglesessionmanager.h create mode 100644 libs/libgloox/linklocal.h create mode 100644 libs/libgloox/linklocalclient.cpp create mode 100644 libs/libgloox/linklocalclient.h create mode 100644 libs/libgloox/linklocalhandler.h create mode 100644 libs/libgloox/linklocalmanager.cpp create mode 100644 libs/libgloox/linklocalmanager.h create mode 100644 libs/libgloox/mucinvitationhandler.cpp create mode 100644 libs/libgloox/version.rc diff --git a/libs/libgloox/adhocplugin.h b/libs/libgloox/adhocplugin.h new file mode 100644 index 0000000..893af76 --- /dev/null +++ b/libs/libgloox/adhocplugin.h @@ -0,0 +1,61 @@ +/* + Copyright (c) 2015 by Jakob Schröter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#ifndef ADHOCPLUGIN_H__ +#define ADHOCPLUGIN_H__ + +#include "gloox.h" +#include "stanzaextension.h" + +namespace gloox +{ + + class Tag; + + /** + * @brief A base class for Adhoc Command plugins (DataForm, IO Data, ...). + * + * This is just a common base class for abstractions of protocols that can be embedded into Adhoc Commands. + * You should not need to use this class directly unless you're extending Adhoc Commands further. + * + * This class exists purely as an additional abstraction layer, to limit the type of objects that can be + * added to an Adhoc Command. + * + * @author Jakob Schröter + * @since 1.0.13 + */ + class GLOOX_API AdhocPlugin : public StanzaExtension + { + public: + + /** + * + */ + AdhocPlugin( int type ) : StanzaExtension( type ) {} + + /** + * Virtual destructor. + */ + virtual ~AdhocPlugin() {} + + /** + * Converts to @b true if the plugin is valid, @b false otherwise. + */ + virtual operator bool() const = 0; + + }; + +} + +#endif // ADHOCPLUGIN_H__ diff --git a/libs/libgloox/atomicrefcount.cpp b/libs/libgloox/atomicrefcount.cpp new file mode 100644 index 0000000..9d1ca0f --- /dev/null +++ b/libs/libgloox/atomicrefcount.cpp @@ -0,0 +1,93 @@ +/* + Copyright (c) 2007-2015 by Jakob Schröter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "atomicrefcount.h" + +#include "config.h" + +#if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) +# include +#elif defined( __APPLE__ ) +# include +#elif defined( HAVE_GCC_ATOMIC_BUILTINS ) + // Use intrinsic functions - no #include required. +#else +# include "mutexguard.h" +#endif + +#ifdef _WIN32_WCE +# include +#endif + +namespace gloox +{ + + namespace util + { + AtomicRefCount::AtomicRefCount() + : m_count( 0 ) + { + } + + int AtomicRefCount::increment() + { +#if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) + return (int) ::InterlockedIncrement( (volatile LONG*)&m_count ); +#elif defined( __APPLE__ ) + return (int) OSAtomicIncrement32Barrier( (volatile int32_t*)&m_count ); +#elif defined( HAVE_GCC_ATOMIC_BUILTINS ) + // Use the gcc intrinsic for atomic increment if supported. + return (int) __sync_add_and_fetch( &m_count, 1 ); +#else + // Fallback to using a lock + MutexGuard m( m_lock ); + return ++m_count; +#endif + } + + int AtomicRefCount::decrement() + { +#if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) + return (int) ::InterlockedDecrement( (volatile LONG*)&m_count ); +#elif defined( __APPLE__ ) + return (int) OSAtomicDecrement32Barrier( (volatile int32_t*)&m_count ); +#elif defined( HAVE_GCC_ATOMIC_BUILTINS ) + // Use the gcc intrinsic for atomic decrement if supported. + return (int) __sync_sub_and_fetch( &m_count, 1 ); +#else + // Fallback to using a lock + MutexGuard m( m_lock ); + return --m_count; +#endif + } + + void AtomicRefCount::reset() + { +#if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) + ::InterlockedExchange( (volatile LONG*)&m_count, (volatile LONG)0 ); +#elif defined( __APPLE__ ) + OSAtomicAnd32Barrier( (uint32_t)0, (volatile uint32_t*)&m_count ); +#elif defined( HAVE_GCC_ATOMIC_BUILTINS ) + // Use the gcc intrinsic for atomic decrement if supported. + __sync_fetch_and_and( &m_count, 0 ); +#else + // Fallback to using a lock + MutexGuard m( m_lock ); + m_count = 0; +#endif + } + + } + +} + diff --git a/libs/libgloox/atomicrefcount.h b/libs/libgloox/atomicrefcount.h new file mode 100644 index 0000000..cebac9a --- /dev/null +++ b/libs/libgloox/atomicrefcount.h @@ -0,0 +1,75 @@ +/* + Copyright (c) 2007-2015 by Jakob Schröter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef ATOMICREFCOUNT_H__ +#define ATOMICREFCOUNT_H__ + +#include "macros.h" +#include "mutex.h" + +namespace gloox +{ + + namespace util + { + /** + * @brief A simple implementation of a thread safe 32-bit + * reference count. Native functions are used where possible. + * When not available, a mutex is used for locking and unlocking. + * + * @author Daniel Bowen + * @author Jakob Schröter + * @since 1.0.1 + */ + class GLOOX_API AtomicRefCount + { + public: + /** + * Contructs a new atomic reference count. + */ + AtomicRefCount(); + + /** + * Increments the reference count, and returns the new value. + * @return The new value. + */ + int increment(); + + /** + * Decrements the reference count, and returns the new value. + * @return The new value. + */ + int decrement(); + + /** + * Resets the reference count to zero. + * @since 1.0.4 + */ + void reset(); + + private: + AtomicRefCount& operator=( const AtomicRefCount& ); + + volatile int m_count; + + // The mutex is only used if a native function is unavailable. + Mutex m_lock; + + }; + + } + +} + +#endif // ATOMICREFCOUNT_H__ + diff --git a/libs/libgloox/carbons.cpp b/libs/libgloox/carbons.cpp new file mode 100644 index 0000000..acb38f6 --- /dev/null +++ b/libs/libgloox/carbons.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2013-2015 by Jakob Schröter + * This file is part of the gloox library. http://camaya.net/gloox + * + * This software is distributed under a license. The full license + * agreement can be found in the file LICENSE in this distribution. + * This software may not be copied, modified, sold or distributed + * other than expressed in the named license agreement. + * + * This software is distributed without any warranty. + */ + + +#include "carbons.h" + +#include "forward.h" +#include "util.h" + +namespace gloox +{ + /* chat state type values */ + static const char* typeValues [] = { + "received", + "sent", + "enable", + "disable", + "private" + }; + + Carbons::Carbons( Carbons::Type type ) + : StanzaExtension( ExtCarbons ), m_forward( 0 ), m_type( type ) + { + } + + Carbons::Carbons( const Tag* tag ) + : StanzaExtension( ExtCarbons ), m_forward( 0 ), m_type( Invalid ) + { + if( !tag ) + return; + + const std::string& name = tag->name(); + m_type = (Type)util::lookup( name, typeValues ); + + switch( m_type ) + { + case Sent: + case Received: + { + Tag* f = tag->findChild( "forwarded", XMLNS, XMLNS_STANZA_FORWARDING ); + if( f ) + m_forward = new Forward( f ); + break; + } + default: + break; + } + } + + Carbons::~Carbons() + { + delete m_forward; + } + + const std::string& Carbons::filterString() const + { + static const std::string filter = "/message/*[@xmlns='" + XMLNS_MESSAGE_CARBONS + "']"; + return filter; + } + + Stanza* Carbons::embeddedStanza() const + { + if( !m_forward || m_type == Invalid ) + return 0; + + return m_forward->embeddedStanza(); + } + + Tag* Carbons::embeddedTag() const + { + if( !m_forward || m_type == Invalid ) + return 0; + + return m_forward->embeddedTag(); + } + + Tag* Carbons::tag() const + { + if( m_type == Invalid ) + return 0; + + Tag* t = new Tag( util::lookup( m_type, typeValues ), XMLNS, XMLNS_MESSAGE_CARBONS ); + if( m_forward && ( m_type == Received || m_type == Sent ) ) + t->addChild( m_forward->tag() ); + + return t; + } + + StanzaExtension* Carbons::clone() const + { + return 0; // TODO + } + +} diff --git a/libs/libgloox/carbons.h b/libs/libgloox/carbons.h new file mode 100644 index 0000000..e32817a --- /dev/null +++ b/libs/libgloox/carbons.h @@ -0,0 +1,185 @@ +/* + Copyright (c) 2013-2015 by Jakob Schröter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef CARBONS_H__ +#define CARBONS_H__ + +#include "macros.h" + +#include "stanzaextension.h" +#include "tag.h" + +namespace gloox +{ + + class Forward; + + /** + * @brief An implementation of Message Carbons (@xep{0280}) as a StanzaExtension. + * + * @section enable Enable Mesage Carbons + * + * Before using Message Carbons you have to check your server for support of the extension. + * You can do so using Disco::getDiscoInfo(). You can check the result (in DiscoHandler::handleDiscoInfo()) + * for a feature of @c XMLNS_MESSAGE_CARBONS (use Disco::Info::hasFeature()). + * + * If the feature exists, you can enable Message Carbons with the server. + * + * @code + * Client cb( ... ); + * // ... + * + * // setup + * cb.registerStanzaExtension( new Forward() ); // required for Message Carbons support + * cb.registerStanzaExtension( new Carbons() ); + * // ... + * + * // enable Message Carbons + * IQ iq( IQ::Set, JID() ); // empty JID + * iq.addExtension( new Carbons( Carbons::Enable ) ); + * cb.send( iq, MyIqHandler, 1 ); // myIqHandler will be notified of the result with the given context ('1' in this case). + * @endcode + * + * @note Once enabled, the server will automatically send all received and sent messages @b of @b type @c Chat to all other Carbons-enabled resources of + * the current account. You have to make sure that you actually send messages of type @c Chat. The default is currently @c Normal. + * + * @section disable Disable Message Carbons + * + * Once enabled, you can easily disable Message carbons. The code is almost identical to the code used to enable the feature, + * except that you use a Carbons::Type of Carbons::Disable when you add the Carbons extension to the IQ: + * @code + * iq.addExtension( new Carbons( Carbons::Disable ) ); + * @endcode + * + * @section private Prevent carbon copies for a single message + * + * To disable carbon copies for a single message, add a Carbons extension of type Private: + * + * @code + * Message msg( Message::Chat, ... ); + * // ... + * msg.addExtension( new Carbons( Carbons::Private ) ); + * @endcode + * + * The server will not copy this message to your other connected resources. + * + * @section access Access received carbon copies + * + * When receiving a message (sent by either another connected client of the current user, or by a 3rd party), a carbon copy will + * have the following characteristics: + * @li The message's @c from attribute will be the @b bare JID of the @b receiving entity. + * @li The message's @c from attribute will be the @b full JID of the @b receiving entity. + * @li The message contains a Carbons StanzaExtension. This extension contains the original message with the @b original + * @c from/to attributes. + * + * Some sample code: + * @code + * bool Myclass::handleMessage( const Message& msg, MessageSession* ) + * { + * if( msg.hasEmbeddedStanza() ) // optional, saves some processing time when there is no Carbons extension + * { + * const Carbons* carbon = msg.findExtension( ExtCarbons ); + * if( carbon && carbon->embeddedStanza() ) + * { + * Message* embeddedMessage = static_cast( carbon->embeddedStanza() ); + * } + * } + * } + * @endcode + * + * You can also determine whether a carbon was sent by a 3rd party or a different client of the current user by checking the return value of Carbons::type(). + * @code + * Carbons* c = msg.findExtension<...>( ... ); + * // check that c is valid + * + * if( c->type() == Carbons::Received ) + * // Message was sent by a 3rd party + * else if( c->type() == Carbons::Sent ) + * // Message was sent by a different client of the current user + * @endcode + * + * XEP Version: 0.8 + * + * @author Jakob Schröter + * @since 1.0.5 + */ + class GLOOX_API Carbons : public StanzaExtension + { + public: + /** + * The types of Message Carbons stanza extensions. + */ + enum Type + { + Received, /**< Indicates that the message received has been sent by a third party. */ + Sent, /**< Indicates that the message received has been sent by one of the user's own resources. */ + Enable, /**< Indicates that the sender wishes to enable carbon copies. */ + Disable, /**< Indicates that the sender wishes to disable carbon copies. */ + Private, /**< Indicates that the sender does not want carbon copies to be sent for this message. */ + Invalid /**< Invalid type. */ + }; + + /** + * Constructs a new Carbons instance of the given type. + * You should only use the @c Enable, @c Disable and @c Private types. + * @param type The Carbons type to create. + */ + Carbons( Type type ); + + /** + * Constructs a new Carbons instance from the given tag. + * @param tag The Tag to create the Carbons instance from. + */ + Carbons( const Tag* tag = 0 ); + + /** + * Virtual destructor. + */ + virtual ~Carbons(); + + /** + * Returns the current instance's type. + * @return The intance's type. + */ + Type type() const { return m_type; } + + // reimplemented from StanzaExtension + virtual Stanza* embeddedStanza() const; + + // reimplemented from StanzaExtension + virtual Tag* embeddedTag() const; + + // reimplemented from StanzaExtension + virtual const std::string& filterString() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* tag ) const + { + return new Carbons( tag ); + } + + // reimplemented from StanzaExtension + virtual Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const; + + private: + Forward* m_forward; + Type m_type; + + }; + +} + +#endif // CARBONS_H__ diff --git a/libs/libgloox/forward.cpp b/libs/libgloox/forward.cpp new file mode 100644 index 0000000..9406b0e --- /dev/null +++ b/libs/libgloox/forward.cpp @@ -0,0 +1,85 @@ +/* + Copyright (c) 2013-2015 by Jakob Schröter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "forward.h" + +#include "delayeddelivery.h" +#include "message.h" +#include "stanza.h" +#include "util.h" + +namespace gloox +{ + + Forward::Forward( Stanza *stanza, DelayedDelivery *delay ) + : StanzaExtension( ExtForward ), + m_stanza( stanza ), m_tag( 0 ), m_delay( delay ) + { + } + + Forward::Forward( const Tag* tag ) + : StanzaExtension( ExtForward ), + m_stanza( 0 ), m_tag( 0 ), m_delay( 0 ) + { + if( !tag || !( tag->name() == "forwarded" && tag->hasAttribute( XMLNS, XMLNS_STANZA_FORWARDING ) ) ) + return; + + m_delay = new DelayedDelivery( tag->findChild( "delay", XMLNS, XMLNS_DELAY ) ); + + Tag* m = tag->findChild( "message" ); + if( !m ) + return; + + m_tag = m->clone(); + m_stanza = new Message( m ); + } + + Forward::~Forward() + { + delete m_delay; + delete m_stanza; + delete m_tag; + } + + const std::string& Forward::filterString() const + { + static const std::string filter = "/message/forwarded[@xmlns='" + XMLNS_STANZA_FORWARDING + "']" + "|/iq/forwarded[@xmlns='" + XMLNS_STANZA_FORWARDING + "']" + "|/presence/forwarded[@xmlns='" + XMLNS_STANZA_FORWARDING + "']"; + return filter; + } + + Tag* Forward::tag() const + { + if( !m_stanza ) + return 0; + + Tag* f = new Tag( "forwarded" ); + f->setXmlns( XMLNS_STANZA_FORWARDING ); + if( m_delay ) + f->addChild( m_delay->tag() ); + if( m_stanza ) + f->addChild( m_stanza->tag() ); + + return f; + } + + StanzaExtension* Forward::clone() const + { + if( !m_tag || !m_delay ) + return 0; + + return new Forward( new Message( m_tag ), static_cast( m_delay->clone() ) ); + } + +} diff --git a/libs/libgloox/forward.h b/libs/libgloox/forward.h new file mode 100644 index 0000000..dfcb087 --- /dev/null +++ b/libs/libgloox/forward.h @@ -0,0 +1,104 @@ +/* + Copyright (c) 2013-2015 by Jakob Schröter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef FORWARD_H__ +#define FORWARD_H__ + +#include "stanzaextension.h" + +#include + +namespace gloox +{ + + class DelayedDelivery; + class Stanza; + + /** + * @brief This is an implementation of Stanza Forwarding (@xep{0297}) as a StanzaExtension. + * + * @note At this point, Forward can only hold forwarded Messages, not IQ or Presence. + * However, Forward can be used inside any type of stanza (<message>, <iq>, + * or <presence>). + * + * XEP-Version: 0.5 + * + * @author Jakob Schröter + * @author Fernando Sanchez + * @since 1.0.5 + */ + class GLOOX_API Forward : public StanzaExtension + { + public: + + /** + * Creates a forwarding StanzaExtension, embedding the given Stanza and DelayedDelivery objects. + * @param stanza The forwarded Stanza. This Forward instance will own the Stanza object. + * @param delay The date/time the forwarded stanza was received at by the forwarder. This + * Forward instance will own the DelayedDelivery object. + */ + Forward( Stanza* stanza, DelayedDelivery* delay ); + + /** + * Creates a forwarding Stanza from the given Tag. The original Tag will be ripped off. + * If a valid Stanza is conatined (as a child) in the Tag it will be parsed, too. + * It can then be accessed through embeddedStanza(). The Tag that the Stanza was built from + * is available through embeddedTag(). + * @param tag The Tag to parse. + */ + Forward( const Tag* tag = 0 ); + + /** + * Virtual destructor. + */ + virtual ~Forward(); + + /** + * This function returns a pointer to a DelayedDelivery StanzaExtension which indicates + * when the forwarder originally received the forwarded stanza. + * + * @return A pointer to a DelayedDelivery object. May be 0. + */ + const DelayedDelivery* when() const { return m_delay; } + + // reimplemented from StanzaExtension + virtual Stanza* embeddedStanza() const { return m_stanza; } + + // reimplemented from StanzaExtension + virtual Tag* embeddedTag() const { return m_tag; } + + // reimplemented from StanzaExtension + virtual Tag* tag() const; + + // reimplemented from StanzaExtension + const std::string& filterString() const; + + // reimplemented from StanzaExtension + StanzaExtension* newInstance( const Tag* tag ) const + { + return new Forward( tag ); + } + + // reimplemented from StanzaExtension + StanzaExtension* clone() const; + + private: + Stanza* m_stanza; + Tag* m_tag; + DelayedDelivery* m_delay; + + }; + +} + +#endif // FORWARD_H__ diff --git a/libs/libgloox/iodata.cpp b/libs/libgloox/iodata.cpp new file mode 100644 index 0000000..eeeb0ee --- /dev/null +++ b/libs/libgloox/iodata.cpp @@ -0,0 +1,226 @@ +/* + Copyright (c) 2015 by Jakob Schröter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "iodata.h" + +#include "util.h" + +namespace gloox +{ + + static const char* ioTypes[] = { + "io-schemata-get", + "input", + "getStatus", + "getOutput", + "io-schemata-result", + "output", + "error", + "status" + }; + + static inline IOData::Type ioType( const std::string& type ) + { + return (IOData::Type)util::lookup( type, ioTypes ); + } + + IOData::IOData( Type type ) + : AdhocPlugin( ExtIOData ), + m_in( 0 ), m_out( 0 ), m_error( 0 ), + m_type( type ) + { + m_status.elapsed = -1; + m_status.remaining = -1; + m_status.percentage = -1; + } + + IOData::IOData( const Tag* tag ) + : AdhocPlugin( ExtIOData ), + m_in( 0 ), m_out( 0 ), m_error( 0 ), + m_type( TypeInvalid ) + { + if( !tag || !( tag->name() == "iodata" && tag->hasAttribute( XMLNS, XMLNS_IODATA ) ) ) + return; + + m_status.elapsed = -1; + m_status.remaining = -1; + m_status.percentage = -1; + + m_type = ioType( tag->findAttribute( "type" ) ); + Tag* m = 0; + switch( m_type ) + { + case TypeInput: + m = tag->findChild( "in" ); + if( m ) + m_in = m->clone(); + break; + case TypeIoSchemataResult: + m = tag->findChild( "desc" ); + if( m ) + m_desc = m->cdata(); + + m = tag->findChild( "out" ); + if( m ) + m_out = m->clone(); + + m = tag->findChild( "in" ); + if( m ) + m_in = m->clone(); + break; + case TypeOutput: + m = tag->findChild( "out" ); + if( m ) + m_out = m->clone(); + break; + case TypeError: + m = tag->findChild( "error" ); + if( m ) + m_error = m->clone(); + break; + case TypeStatus: + m = tag->findChild( "status" ); + if( m ) + { + Tag* t = m->findChild( "elapsed" ); + if( t ) + m_status.elapsed = atoi( t->cdata().c_str() ); + + t = m->findChild( "remaining" ); + if( t ) + m_status.remaining = atoi( t->cdata().c_str() ); + + t = m->findChild( "percentage" ); + if( t ) + m_status.percentage = atoi( t->cdata().c_str() ); + + t = m->findChild( "information" ); + if( t ) + m_status.info = t->cdata(); + } + break; + case TypeIoSchemataGet: + case TypeGetStatus: + case TypeGetOutput: + default: + break; + } + + } + + IOData::~IOData() + { + delete m_in; + delete m_out; + delete m_error; + } + + Tag* IOData::tag() const + { + if( m_type == TypeInvalid ) + return 0; + + Tag* i = new Tag( "iodata" ); + i->setXmlns( XMLNS_IODATA ); + i->addAttribute( "type", util::lookup( m_type, ioTypes ) ); + + Tag* t = 0; + switch( m_type ) + { + case TypeInput: + i->addChild( m_in ); + break; + case TypeIoSchemataResult: + i->addChild( m_in ); + i->addChild( m_out ); + new Tag( i, "desc", m_desc ); + break; + case TypeOutput: + i->addChild( m_out ); + break; + case TypeError: + i->addChild( m_error ); + break; + case TypeStatus: + t = new Tag( i, "status" ); + if( m_status.elapsed >= 0 ) + new Tag( t, "elapsed", util::int2string( m_status.elapsed ) ); + if( m_status.remaining >= 0 ) + new Tag( t, "remaining", util::int2string( m_status.remaining ) ); + if( m_status.percentage >= 0 ) + new Tag( t, "percentage", util::int2string( m_status.percentage ) ); + if( m_status.info.length() ) + new Tag( t, "information", m_status.info ); + break; + case TypeIoSchemataGet: + case TypeGetStatus: + case TypeGetOutput: + default: + break; + } + + return i; + } + + IOData* IOData::clone() const + { + IOData* i = new IOData( m_type ); + i->m_status = m_status; + i->m_desc = m_desc; + + if( m_in ) + i->m_in = m_in->clone(); + if( m_out ) + i->m_out = m_out->clone(); + if( m_error ) + i->m_error = m_error->clone(); + + return i; + } + + void IOData::setIn( Tag* in ) + { + if( !in ) + return; + + delete m_in; + + m_in = new Tag( "in" ); + m_in->addChild( in ); + } + + void IOData::setOut( Tag* out ) + { + if( !out ) + return; + + delete m_out; + + m_out = new Tag( "out" ); + m_out->addChild( out ); + } + + void IOData::setError( Tag* error ) + { + if( !error ) + return; + + delete m_error; + + m_error = new Tag( "error" ); + m_error->addChild( error ); + } + + +} + diff --git a/libs/libgloox/iodata.h b/libs/libgloox/iodata.h new file mode 100644 index 0000000..fa824e0 --- /dev/null +++ b/libs/libgloox/iodata.h @@ -0,0 +1,186 @@ +/* + Copyright (c) 2015 by Jakob Schröter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef IODATA_H__ +#define IODATA_H__ + +#include "adhocplugin.h" + +#include "gloox.h" +#include "tag.h" + +#include + +namespace gloox +{ + + /** + * @brief This is an abstraction of the IO Data specification @xep{0153}. + * + * This abstraction can be used to implement IO Data on top of Data Forms. + * + * XEP version: 0.1 + * @author Jakob Schröter + * @since 1.0.13 + */ + class GLOOX_API IOData : public AdhocPlugin + { + public: + /** + * The IO Data transaction types. + */ + enum Type + { + TypeIoSchemataGet, /** To request the schemata of input and output. */ + TypeInput, /** To submit the input. */ + TypeGetStatus, /** To request the status of the procedure. */ + TypeGetOutput, /** To request the output. */ + TypeIoSchemataResult, /** To return the schemata of input and output. */ + TypeOutput, /** To submit the output. */ + TypeError, /** To submit additional error information. */ + TypeStatus, /** To indicate the current status of the procedure. */ + TypeInvalid /** Invalid type. */ + }; + + struct Status + { + int elapsed; /** Aan integer value of the time in milliseconds that + * elapsed since the procedure was invoked. */ + int remaining; /** An integer value of the (estimated) time in milliseconds + * till the procedure will finish. */ + int percentage; /** The percentage of the procedure that is finished. */ + std::string info; /** Describes the current status of the procedure. */ + }; + + /** + * Constructs a new IO Data object of the given type. + * @param type The transaction type. + */ + IOData( Type type ); + + /** + * Constructs a new IO Data object by parsing the given Tag. + * @param tag The Tag to parse. This should be a <iodata> tag with the correct namespace and child elements. + */ + IOData( const Tag* tag ); + + /** + * Virtual destructor. + */ + virtual ~IOData(); + + /** + * Returns the IO Data object's type. + * @return The IO Data object's type. + */ + Type type() const { return m_type; } + + /** + * Returns the 'input' tag, if the transaction type is either @c input or @c io-schemata-result. + * @return The 'input' tag, including the encapsulating <in>. + * @note The IOData instance will still own the tag and delete it. Clone it if you need it later. + */ + const Tag* in() const { return m_in; } + + /** + * Sets the 'input' tag. If an 'input' tag was previosuly set, it is deleted before the new one is set. + * @param in The new 'input' tag. + * @note The @c in tag will be owned by this IOData instance. Clone it if you need it somewhere else. + */ + void setIn( Tag* in ); + + /** + * Returns the 'output' tag, if the transaction type is either @c output or @c io-schemata-result. + * @return The 'output' tag, including the encapsulating <out>. + * @note The IOData instance will still own the tag and delete it. Clone it if you need it later. + */ + const Tag* out() const { return m_out; } + + /** + * Sets the 'output' tag. If an 'output' tag was previosuly set, it is deleted before the new one is set. + * @param out The new 'output' tag. + * @note The @c out tag will be owned by this IOData instance. Clone it if you need it somewhere else. + */ + void setOut( Tag* out ); + + /** + * Returns the 'error' tag, if the transaction type is either @c error or @c io-schemata-result. + * @return The 'error' tag, including the encapsulating <error>. + * @note The IOData instance will still own the tag and delete it. Clone it if you need it later. + */ + const Tag* error() const { return m_error; } + + /** + * Sets the 'error' tag. If an 'error' tag was previosuly set, it is deleted before the new one is set. + * @param out The new 'error' tag. + * @note The @c error tag will be owned by this IOData instance. Clone it if you need it somewhere else. + */ + void setError( Tag* error ); + + /** + * Sets the Schema description. Only used/valid if type is @c io-schemata-result. + * @param desc The schema description. + */ + void setDesc( const std::string& desc ) { m_desc = desc; } + + /** + * Returns the schema description, if any. Usually only valid if transaction type is @c io-schema-result. + * @return The schema description. + */ + const std::string& desc() const { return m_desc; } + + /** + * Sets the status of the procedure. Only used/valid if transaction type is @c status. + * @param status The status of the procedure. + */ + void setStatus( Status status ) { m_status = status; } + + /** + * Returns the status of the procedure. Only used/valid if transaction type is @c status. + * @return The status of the procedure. + */ + Status status() const { return m_status; } + + // reimplemented from AdhocPlugin/StanzaExtension + virtual Tag* tag() const; + + // reimplemented from AdhocPlugin/StanzaExtension + virtual IOData* clone() const; + + // reimplemented from AdhocPlugin/StanzaExtension + virtual const std::string& filterString() const { return EmptyString; } + + // reimplemented from AdhocPlugin/StanzaExtension + virtual StanzaExtension* newInstance( const Tag* /*tag*/ ) const { return 0; } + + /** + * Converts to @b true if the IOData is valid, @b false otherwise. + */ + operator bool() const { return m_type != TypeInvalid; } + + private: + Tag* m_in; + Tag* m_out; + Tag* m_error; + + std::string m_desc; + + Status m_status; + + Type m_type; + + }; + +} + +#endif // IODATA_H__ diff --git a/libs/libgloox/jinglecontent.cpp b/libs/libgloox/jinglecontent.cpp new file mode 100644 index 0000000..c69e1aa --- /dev/null +++ b/libs/libgloox/jinglecontent.cpp @@ -0,0 +1,104 @@ +/* + Copyright (c) 2008-2015 by Jakob Schröter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "jinglecontent.h" +#include "jinglepluginfactory.h" +#include "util.h" + +namespace gloox +{ + + namespace Jingle + { + + static const char* creatorValues [] = { + "initiator", + "responder" + }; + + static inline Content::Creator creatorType( const std::string& type ) + { + return (Content::Creator)util::lookup( type, creatorValues ); + } + + static const char* sendersValues [] = { + "initiator", + "responder", + "both", + "none" + }; + + static inline Content::Senders sendersType( const std::string& type ) + { + return (Content::Senders)util::lookup( type, sendersValues ); + } + + Content::Content( const std::string& name, const PluginList& plugins, Creator creator, + Senders senders, const std::string& disposition ) + : Plugin( PluginContent ), m_creator( creator ), m_disposition( disposition ), + m_name( name ), m_senders( senders ) + { + m_plugins = plugins; + } + + Content::Content( const Tag* tag, PluginFactory* factory ) + : Plugin( PluginContent ) + { + if( !tag || tag->name() != "content" ) + return; + + m_name = tag->findAttribute( "name" ); + m_creator = (Creator)util::lookup( tag->findAttribute( "creator" ), creatorValues ); + m_senders = (Senders)util::lookup( tag->findAttribute( "senders" ), sendersValues ); + m_disposition = tag->findAttribute( "disposition" ); + + if( factory ) + factory->addPlugins( *this, tag ); + } + + Content::~Content() + { + } + + const std::string& Content::filterString() const + { + static const std::string filter = "jingle/content"; + return filter; + } + + Tag* Content::tag() const + { + if( m_creator == InvalidCreator || m_name.empty() ) + return 0; + + Tag* t = new Tag( "content" ); + t->addAttribute( "creator", util::lookup( m_creator, creatorValues ) ); + t->addAttribute( "disposition", m_disposition ); + t->addAttribute( "name", m_name ); + t->addAttribute( "senders", util::lookup( m_senders, sendersValues ) ); + + PluginList::const_iterator it = m_plugins.begin(); + for( ; it != m_plugins.end(); ++it ) + t->addChild( (*it)->tag() ); + + return t; + } + + Plugin* Content::clone() const + { + return 0; + } + + } + +} diff --git a/libs/libgloox/jinglecontent.h b/libs/libgloox/jinglecontent.h new file mode 100644 index 0000000..c04d786 --- /dev/null +++ b/libs/libgloox/jinglecontent.h @@ -0,0 +1,138 @@ +/* + Copyright (c) 2008-2015 by Jakob Schröter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef JINGLECONTENT_H__ +#define JINGLECONTENT_H__ + + +#include "jingleplugin.h" +#include "tag.h" + +#include + +namespace gloox +{ + + namespace Jingle + { + + class PluginFactory; + + /** + * @brief An abstraction of a Jingle Content Type. This is part of Jingle (@xep{0166}). + * + * See @link gloox::Jingle::Session Jingle::Session @endlink for more info on Jingle. + * + * XEP Version: 1.1 + * + * @author Jakob Schröter + * @since 1.0.5 + */ + class GLOOX_API Content : public Plugin + { + public: + /** + * The original creator of the content type. + */ + enum Creator + { + CInitiator, /**< The creator is the initiator of the session. */ + CResponder, /**< The creator is the responder. */ + InvalidCreator /**< Invalid value. */ + }; + + /** + * The parties in the session that will be generating content. + */ + enum Senders + { + SInitiator, /**< The initiator generates/sends content. */ + SResponder, /**< The responder generates/sends content. */ + SBoth, /**< Both parties generate/send content( default). */ + SNone, /**< No party generates/sends content. */ + InvalidSender /**< Invalid value. */ + }; + + /** + * Creates a new Content wrapper. + * @param name A unique name for the content type. + * @param plugins A list of application formats, transport methods, security preconditions, ... + * @param creator Which party originally generated the content type; the defined values are "SInitiator" and "SResponder". + * @param senders Which parties in the session will be generating content. + * @param disposition How the content definition is to be interpreted by the recipient. The meaning of this attribute + * matches the "Content-Disposition" header as defined in RFC 2183 and applied to SIP by RFC 3261. + */ + Content( const std::string& name, const PluginList& plugins, Creator creator = CInitiator, + Senders senders = SBoth, const std::string& disposition = "session" ); + + /** + * Creates a new Content object from the given tag. + * @param tag The Tag to parse. + * @param factory A PluginFactory instance to use for embedding plugins. + */ + Content( const Tag* tag = 0, PluginFactory* factory = 0 ); + + /** + * Returns the content's creator. + * @return The content's creator. + */ + Creator creator() const { return m_creator; } + + /** + * Returns the senders. + * @return The senders. + */ + Senders senders() const { return m_senders; } + + /** + * Returns the disposition. + * @return The disposition. + */ + const std::string& disposition() const { return m_disposition; } + + /** + * Returns the content name. + * @return The content name. + */ + const std::string& name() const { return m_name; } + + /** + * Virtual destructor. + */ + virtual ~Content(); + + // reimplemented from Plugin + virtual const std::string& filterString() const; + + // reimplemented from Plugin + virtual Tag* tag() const; + + // reimplemented from Plugin + virtual Plugin* newInstance( const Tag* tag ) const { return new Content( tag, m_factory ); } + + // reimplemented from Plugin + virtual Plugin* clone() const; + + private: + Creator m_creator; + std::string m_disposition; + std::string m_name; + Senders m_senders; + + }; + + } + +} + +#endif // JINGLECONTENT_H__ diff --git a/libs/libgloox/jinglefiletransfer.cpp b/libs/libgloox/jinglefiletransfer.cpp new file mode 100644 index 0000000..13b4a46 --- /dev/null +++ b/libs/libgloox/jinglefiletransfer.cpp @@ -0,0 +1,173 @@ +/* + Copyright (c) 2013-2015 by Jakob Schröter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "jinglefiletransfer.h" + +#include "tag.h" +#include "gloox.h" +#include "util.h" + +#include + +namespace gloox +{ + + namespace Jingle + { + + static const char* typeValues [] = { + "offer", + "request", + "checksum", + "abort", + "received" + }; + + FileTransfer::FileTransfer( Type type, const FileList& files ) + : Plugin( PluginFileTransfer ), m_type( type ), m_files( files ) + { + } + + FileTransfer::FileTransfer( const Tag* tag ) + : Plugin( PluginFileTransfer ), m_type( Invalid ) + { + if( !tag || tag->xmlns() != XMLNS_JINGLE_FILE_TRANSFER ) + return; + + std::string name = tag->name(); + if( name == "description" ) + { + const Tag* c = tag->findTag( "description/offer|description/request" ); + if( c ) + { + parseFileList( c->findChildren( "file" ) ); + name = c->name(); + } + } + else if( name == "checksum" || name == "abort" || name == "received" ) + { + parseFileList( tag->findChildren( "file" ) ); + } + + m_type = (Type)util::lookup( name, typeValues ); + } + + void FileTransfer::parseFileList( const TagList& files ) + { + TagList::const_iterator it = files.begin(); + for( ; it != files.end(); ++it ) + { + File f; + Tag *t = (*it)->findChild( "name" ); + f.name = t ? t->cdata() : EmptyString; + t = (*it)->findChild( "desc" ); + f.desc = t ? t->cdata() : EmptyString; + t = (*it)->findChild( "date" ); + f.date = t ? t->cdata() : EmptyString; + t = (*it)->findChild( "size" ); + f.size = t ? atoi( t->cdata().c_str() ) : -1; + t = (*it)->findChild( "range" ); + if( t ) + { + f.range = true; + f.offset = t->hasAttribute( "offset" ) ? atoi( t->findAttribute( "offset" ).c_str() ) : -1; + } + t = (*it)->findChild( "hash", XMLNS, XMLNS_HASHES ); + if( t ) + { + f.hash_algo = t->findAttribute( "algo" ); + f.hash = t->cdata(); + } + m_files.push_back( f ); + } + } + + const StringList FileTransfer::features() const + { + StringList sl; + sl.push_back( XMLNS_JINGLE_FILE_TRANSFER ); + return sl; + } + + const std::string& FileTransfer::filterString() const + { + static const std::string filter = "content/description[@xmlns='" + XMLNS_JINGLE_FILE_TRANSFER + "']" + "|jingle/abort[@xmlns='" + XMLNS_JINGLE_FILE_TRANSFER + "']" + "|jingle/received[@xmlns='" + XMLNS_JINGLE_FILE_TRANSFER + "']" + "|jingle/checksum[@xmlns='" + XMLNS_JINGLE_FILE_TRANSFER + "']"; + return filter; + } + + Plugin* FileTransfer::newInstance( const Tag* tag ) const + { + return new FileTransfer( tag ); + } + + Tag* FileTransfer::tag() const + { + if( m_type == Invalid ) + return 0; + + Tag* r = 0; + + switch( m_type ) + { + case Offer: + case Request: + { + r = new Tag( "description", XMLNS, XMLNS_JINGLE_FILE_TRANSFER ); + Tag* o = new Tag( r, util::lookup( m_type, typeValues ) ); + FileList::const_iterator it = m_files.begin(); + for( ; it != m_files.end(); ++it ) + { + Tag* f = new Tag( o, "file" ); + new Tag( f, "date", (*it).date ); + new Tag( f, "name", (*it).name ); + new Tag( f, "desc", (*it).desc ); + new Tag( f, "size", util::long2string( (*it).size ) ); + Tag* h = new Tag( f, "hash", XMLNS, XMLNS_HASHES ); + h->addAttribute( "algo", (*it).hash_algo ); + h->setCData( (*it).hash ); + if( (*it).range ) + new Tag( f, "range", "offset", (*it).offset ? util::long2string( (*it).offset ) : EmptyString ); + } + break; + } + case Abort: + case Checksum: + case Received: + { + r = new Tag( util::lookup( m_type, typeValues ), XMLNS, XMLNS_JINGLE_FILE_TRANSFER ); + FileList::const_iterator it = m_files.begin(); + Tag* f = new Tag( r, "file" ); + new Tag( f, "date", (*it).date ); + new Tag( f, "name", (*it).name ); + new Tag( f, "desc", (*it).desc ); + new Tag( f, "size", util::long2string( (*it).size ) ); + Tag* h = new Tag( f, "hash", XMLNS, XMLNS_HASHES ); + h->addAttribute( "algo", (*it).hash_algo ); + h->setCData( (*it).hash ); + if( (*it).range ) + new Tag( f, "range" ); + break; + } + default: + break; + } + + return r; + } + + } + +} diff --git a/libs/libgloox/jinglefiletransfer.h b/libs/libgloox/jinglefiletransfer.h new file mode 100644 index 0000000..b43d21a --- /dev/null +++ b/libs/libgloox/jinglefiletransfer.h @@ -0,0 +1,134 @@ +/* + Copyright (c) 2013-2015 by Jakob Schröter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef JINGLEFILETRANSFER_H__ +#define JINGLEFILETRANSFER_H__ + +#include "jingleplugin.h" +#include "tag.h" + +#include +#include + +namespace gloox +{ + + namespace Jingle + { + + /** + * @brief An abstraction of the signaling part of Jingle File Transfer (@xep{0234}), implemented as a Jingle::Plugin. + * + * XEP Version: 0.15 + * + * @author Jakob Schröter + * @since 1.0.7 + */ + class GLOOX_API FileTransfer : public Plugin + { + public: + + /** + * The type of a FileTransfer instance. + */ + enum Type + { + Offer, /**< Signifies a file transfer offer (send). */ + Request, /**< Signifies a file request (pull). */ + Checksum, /**< Used to send a file's checksum. */ + Abort, /**< used to abort a running transfer. */ + Received, /**< Signifies a successful file transfer. */ + Invalid /**< Invalid type. */ + }; + + /** + * A struct holding information about a file. + */ + struct File + { + std::string name; /**< The file's name. */ + std::string date; /**< The file's (creation?) date */ + std::string desc; /**< A description. */ + std::string hash; /**< The file's cehcksum. */ + std::string hash_algo; /**< The algorithm used to calculate the checksum */ + long int size; /**< The filesize in Bytes. */ + bool range; /**< Signifies that an offset transfer is possible. */ + long int offset; /**< An (optional) offset. */ + }; + + /** A list of file information structs. */ + typedef std::list FileList; + + /** + * Creates a new instance. + * @param type The type of the object. + * @param files A list of files to offer, request, acknowledge, ... Most of + * the time this list will contain only one file. + */ + FileTransfer( Type type, const FileList& files ); + + /** + * Creates a new instance from the given Tag + * @param tag The Tag to parse. + */ + FileTransfer( const Tag* tag = 0 ); + + /** + * Virtual destructor. + */ + virtual ~FileTransfer() {} + + /** + * Returns the type. + * @return The type. + */ + Type type() const { return m_type; } + + /** + * Returns a list of embedded file infos. + * @return A list of embedded file infos. + */ + const FileList& files() const { return m_files; } + + // reimplemented from Plugin + virtual const StringList features() const; + + // reimplemented from Plugin + virtual const std::string& filterString() const; + + // reimplemented from Plugin + virtual Tag* tag() const; + + // reimplemented from Plugin + virtual Plugin* newInstance( const Tag* tag ) const; + + // reimplemented from Plugin + virtual Plugin* clone() const + { + return new FileTransfer( *this ); + } + + private: + + void parseFileList( const TagList& files ); + + Type m_type; + FileList m_files; + + }; + + } + +} + +#endif // JINGLEFILETRANSFER_H__ diff --git a/libs/libgloox/jingleiceudp.cpp b/libs/libgloox/jingleiceudp.cpp new file mode 100644 index 0000000..eee9f3d --- /dev/null +++ b/libs/libgloox/jingleiceudp.cpp @@ -0,0 +1,113 @@ +/* + Copyright (c) 2013-2015 by Jakob Schröter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "jingleiceudp.h" + +#include "tag.h" +#include "gloox.h" + +namespace gloox +{ + + namespace Jingle + { + + static const char* typeValues [] = { + "host", + "prflx", + "relay", + "srflx" + }; + + ICEUDP::ICEUDP( const std::string& pwd, const std::string& ufrag, CandidateList& candidates ) + : Plugin( PluginICEUDP ), m_pwd( pwd ), m_ufrag( ufrag), m_candidates( candidates ) + { + } + + ICEUDP::ICEUDP( const Tag* tag ) + : Plugin( PluginICEUDP ) + { + if( !tag || tag->name() != "transport" || tag->xmlns() != XMLNS_JINGLE_ICE_UDP ) + return; + + m_pwd = tag->findAttribute( "pwd" ); + m_ufrag = tag->findAttribute( "ufrag" ); + const TagList candidates = tag->findChildren( "candidate" ); + TagList::const_iterator it = candidates.begin(); + for( ; it != candidates.end(); ++it ) + { + Candidate c; + c.component = (*it)->findAttribute( "component" ); + c.foundation = (*it)->findAttribute( "foundation" ); + c.generation = (*it)->findAttribute( "generation" ); + c.id = (*it)->findAttribute( "id" ); + c.ip = (*it)->findAttribute( "ip" ); + c.network = (*it)->findAttribute( "network" ); + c.port = atoi( (*it)->findAttribute( "port" ).c_str() ); + c.priority = atoi( (*it)->findAttribute( "priority" ).c_str() ); + c.protocol = (*it)->findAttribute( "protocol" ); + c.rel_addr = (*it)->findAttribute( "rel-addr" ); + c.rel_port = atoi( (*it)->findAttribute( "rel-port" ).c_str() ); + c.type = (Type)util::lookup( (*it)->findAttribute( "type" ), typeValues ); + m_candidates.push_back( c ); + } + } + + const StringList ICEUDP::features() const + { + StringList sl; + sl.push_back( XMLNS_JINGLE_ICE_UDP ); + return sl; + } + + const std::string& ICEUDP::filterString() const + { + static const std::string filter = "content/transport[@xmlns='" + XMLNS_JINGLE_ICE_UDP + "']"; + return filter; + } + + Plugin* ICEUDP::newInstance( const Tag* tag ) const + { + return new ICEUDP( tag ); + } + + Tag* ICEUDP::tag() const + { + Tag* t = new Tag( "transport", XMLNS, XMLNS_JINGLE_ICE_UDP ); + t->addAttribute( "pwd", m_pwd ); + t->addAttribute( "ufrag", m_ufrag ); + + CandidateList::const_iterator it = m_candidates.begin(); + for( ; it != m_candidates.end(); ++it ) + { + Tag* c = new Tag( t, "candidate" ); + c->addAttribute( "component", (*it).component ); + c->addAttribute( "foundation", (*it).foundation ); + c->addAttribute( "generation", (*it).generation ); + c->addAttribute( "id", (*it).id ); + c->addAttribute( "ip", (*it).ip ); + c->addAttribute( "network", (*it).network ); + c->addAttribute( "port", (*it).port ); + c->addAttribute( "priority", (*it).priority ); + c->addAttribute( "protocol", (*it).protocol ); + c->addAttribute( "rel-addr", (*it).rel_addr ); + c->addAttribute( "rel-port", (*it).rel_port ); + c->addAttribute( "type", util::lookup( (*it).type, typeValues ) ); + } + + return t; + } + + } + +} diff --git a/libs/libgloox/jingleiceudp.h b/libs/libgloox/jingleiceudp.h new file mode 100644 index 0000000..7d2d3fc --- /dev/null +++ b/libs/libgloox/jingleiceudp.h @@ -0,0 +1,141 @@ +/* + Copyright (c) 2013-2015 by Jakob Schröter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef JINGLEICEUDP_H__ +#define JINGLEICEUDP_H__ + +#include "jingleplugin.h" + +#include +#include + +namespace gloox +{ + + class Tag; + + namespace Jingle + { + + /** + * @brief An abstraction of the signaling part of Jingle ICE-UDP Transport Method (@xep{0176}). + * + * XEP Version: 1.0 + * + * @author Jakob Schröter + * @since 1.0.7 + */ + class GLOOX_API ICEUDP : public Plugin + { + public: + /** + * Describes the candidate type. + */ + enum Type + { + Host, /**< A host candidate. */ + PeerReflexive, /**< A peer reflexive candidate. */ + Relayed, /**< A relayed candidate. */ + ServerReflexive /**< A server reflexive candidate. */ + }; + + /** + * Describes a single transport candidate. + */ + struct Candidate + { + std::string component; /**< A Component ID as defined in ICE-CORE. */ + std::string foundation; /**< A Foundation as defined in ICE-CORE.*/ + std::string generation; /**< An index, starting at 0, that enables the parties to keep track of + updates to the candidate throughout the life of the session. */ + std::string id; /**< A unique identifier for the candidate. */ + std::string ip; /**< The IP address for the candidate transport mechanism. */ + std::string network; /**< An index, starting at 0, referencing which network this candidate is on for a given peer. */ + int port; /**< The port at the candidate IP address. */ + int priority; /**< A Priority as defined in ICE-CORE. */ + std::string protocol; /**< The protocol to be used. Should be @b udp. */ + std::string rel_addr; /**< A related address as defined in ICE-CORE. */ + int rel_port; /**< A related port as defined in ICE-CORE. */ + Type type; /**< A Candidate Type as defined in ICE-CORE. */ + }; + + /** A list of transport candidates. */ + typedef std::list CandidateList; + + /** + * Constructs a new instance. + * @param pwd The @c pwd value. + * @param ufrag The @c ufrag value. + * @param candidates A list of connection candidates. + */ + ICEUDP( const std::string& pwd, const std::string& ufrag, CandidateList& candidates ); + + /** + * Constructs a new instance from the given tag. + * @param tag The Tag to parse. + */ + ICEUDP( const Tag* tag = 0 ); + + /** + * Virtual destructor. + */ + virtual ~ICEUDP() {} + + /** + * Returns the @c pwd value. + * @return The @c pwd value. + */ + const std::string& pwd() const { return m_pwd; } + + /** + * Returns the @c ufrag value. + * @return The @c ufrag value. + */ + const std::string& ufrag() const { return m_ufrag; } + + /** + * Returns the list of connection candidates. + * @return The list of connection candidates. + */ + const CandidateList& candidates() const { return m_candidates; } + + // reimplemented from Plugin + virtual const StringList features() const; + + // reimplemented from Plugin + virtual const std::string& filterString() const; + + // reimplemented from Plugin + virtual Tag* tag() const; + + // reimplemented from Plugin + virtual Plugin* newInstance( const Tag* tag ) const; + + // reimplemented from Plugin + virtual Plugin* clone() const + { + return new ICEUDP( *this ); + } + + private: + std::string m_pwd; + std::string m_ufrag; + CandidateList m_candidates; + + }; + + } + +} + +#endif // JINGLEICEUDP_H__ diff --git a/libs/libgloox/jingleplugin.h b/libs/libgloox/jingleplugin.h new file mode 100644 index 0000000..94ba2aa --- /dev/null +++ b/libs/libgloox/jingleplugin.h @@ -0,0 +1,176 @@ +/* + Copyright (c) 2008-2015 by Jakob Schröter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef JINGLEPLUGIN_H__ +#define JINGLEPLUGIN_H__ + +#include "macros.h" +#include "util.h" + +#include +#include + +namespace gloox +{ + + class Tag; + + namespace Jingle + { + + class Plugin; + class PluginFactory; + + enum JinglePluginType + { + PluginNone, /**< Invalid plugin type. */ + PluginContent, /**< A plugin abstracting a <content> element. May contain further plugins. */ + PluginFileTransfer, /**< A plugin for File Transfer. */ + PluginICEUDP, /**< A plugin for ICE UDP transport negotiation. */ + PluginReason, /**< An abstraction of a Jingle (@xep{0166}) session terminate reason. */ + PluginUser /**< User-supplied plugins must use IDs above this. Do + * not hard-code PluginUser's value anywhere, it is subject + * to change. */ + }; + + /** + * A list of Jingle plugins. + */ + typedef std::list PluginList; + + /** + * @brief An abstraction of a Jingle plugin. This is part of Jingle (@xep{0166} et al.) + * + * This is the base class for Content and all other pluggable Jingle-related containers, e.g. + * session information, such as the 'ringing' info in Jingle Audio, or Jingle DTMF, etc. + * + * A Plugin abstracts the XML that gets sent and received as part of a Jingle session negotiation. + * + * XEP Version: 1.1 + * + * @author Jakob Schröter + * @since 1.0.5 + */ + class GLOOX_API Plugin + { + + friend class PluginFactory; + + public: + /** + * Simple initializer. + */ + Plugin( JinglePluginType type ) : m_factory( 0 ), m_pluginType( type ) {} + + /** + * Virtual destructor. + */ + virtual ~Plugin() { util::clearList( m_plugins ) ; } + + /** + * Adds another Plugin as child. + * @param plugin A plugin to be embedded. Will be owned by this instance and deleted in the destructor. + */ + void addPlugin( const Plugin* plugin ) { if( plugin ) m_plugins.push_back( plugin ); } + + /** + * Finds a Jingle::Plugin of a particular type. + * @param type JinglePluginType to search for. + * @return A pointer to the first Jingle::Plugin of the given type, or 0 if none was found. + */ + const Plugin* findPlugin( int type ) const + { + PluginList::const_iterator it = m_plugins.begin(); + for( ; it != m_plugins.end() && (*it)->pluginType() != type; ++it ) ; + return it != m_plugins.end() ? (*it) : 0; + } + + /** + * Finds a Jingle::Plugin of a particular type. + * Example: + * @code + * const MyPlugin* c = plugin.findPlugin( PluginMyPlugin ); + * @endcode + * @param type The plugin type to look for. + * @return The static_cast' type, or 0 if none was found. + */ + template< class T > + inline const T* findPlugin( int type ) const + { + return static_cast( findPlugin( type ) ); + } + + /** + * Returns a reference to a list of embedded plugins. + * @return A reference to a list of embedded plugins. + */ + const PluginList& plugins() const { return m_plugins; } + + /** + * Reimplement this function if your plugin wants to add anything to the list of + * features announced via Disco. + * @return A list of additional feature strings. + */ + virtual const StringList features() const { return StringList(); } + + /** + * Returns an XPath expression that describes a path to child elements of a + * jingle element that the plugin handles. + * The result should be a single Tag. + * + * @return The plugin's filter string. + */ + virtual const std::string& filterString() const = 0; + + /** + * Returns a Tag representation of the plugin. + * @return A Tag representation of the plugin. + */ + virtual Tag* tag() const = 0; + + /** + * Returns a new instance of the same plugin type, + * based on the Tag provided. + * @param tag The Tag to parse and create a new instance from. + * @return The new plugin instance. + */ + virtual Plugin* newInstance( const Tag* tag ) const = 0; + + /** + * Creates an identical deep copy of the current instance. + * @return An identical deep copy of the current instance. + */ + virtual Plugin* clone() const = 0; + + /** + * Returns the plugin type. + * @return The plugin type. + */ + JinglePluginType pluginType() const { return m_pluginType; } + + protected: + PluginList m_plugins; + PluginFactory* m_factory; + + private: + void setFactory( PluginFactory* factory ) { m_factory = factory; } + + JinglePluginType m_pluginType; + + }; + + } + +} + +#endif // JINGLEPLUGIN_H__ diff --git a/libs/libgloox/jinglepluginfactory.cpp b/libs/libgloox/jinglepluginfactory.cpp new file mode 100644 index 0000000..510516f --- /dev/null +++ b/libs/libgloox/jinglepluginfactory.cpp @@ -0,0 +1,91 @@ +/* + Copyright (c) 2013-2015 by Jakob Schröter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "jinglepluginfactory.h" +#include "tag.h" +#include "util.h" + +namespace gloox +{ + + namespace Jingle + { + + PluginFactory::PluginFactory() + { + } + + PluginFactory::~PluginFactory() + { + util::clearList( m_plugins ); + } + + void PluginFactory::registerPlugin( Plugin* plugin ) + { + if( !plugin ) + return; + + plugin->setFactory( this ); + m_plugins.push_back( plugin ); + } + + void PluginFactory::addPlugins( Plugin& plugin, const Tag* tag ) + { + if( !tag ) + return; + + ConstTagList::const_iterator it; + + PluginList::const_iterator itp = m_plugins.begin(); + for( ; itp != m_plugins.end(); ++itp ) + { + const ConstTagList& match = tag->findTagList( (*itp)->filterString() ); + it = match.begin(); + for( ; it != match.end(); ++it ) + { + Plugin* pl = (*itp)->newInstance( (*it) ); + if( pl ) + { + plugin.addPlugin( pl ); + } + } + } + } + + void PluginFactory::addPlugins( Session::Jingle& jingle, const Tag* tag ) + { + if( !tag ) + return; + + ConstTagList::const_iterator it; + + PluginList::const_iterator itp = m_plugins.begin(); + for( ; itp != m_plugins.end(); ++itp ) + { + const ConstTagList& match = tag->findTagList( (*itp)->filterString() ); + it = match.begin(); + for( ; it != match.end(); ++it ) + { + Plugin* pl = (*itp)->newInstance( (*it) ); + if( pl ) + { + jingle.addPlugin( pl ); + } + } + } + } + + + } + +} diff --git a/libs/libgloox/jinglepluginfactory.h b/libs/libgloox/jinglepluginfactory.h new file mode 100644 index 0000000..4d279c4 --- /dev/null +++ b/libs/libgloox/jinglepluginfactory.h @@ -0,0 +1,82 @@ +/* + Copyright (c) 2013-2015 by Jakob Schröter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef JINGLEPLUGINFACTORY_H__ +#define JINGLEPLUGINFACTORY_H__ + +#include "jingleplugin.h" +#include "jinglesession.h" + +namespace gloox +{ + + class Tag; + + namespace Jingle + { + + /** + * @brief A factory for which creates Plugin instances based on Tags. This is part of Jingle (@xep{0166}). + * + * Used by Jingle::SessionManager. You should not need to use this class directly. + * + * @author Jakob Schröter + * @since 1.0.7 + */ + class PluginFactory + { + friend class SessionManager; + + public: + /** + * Virtual destructor. + */ + virtual ~PluginFactory(); + + /** + * Registers an empty Plugin as a template with the factory. + * @param plugin The plugin to register. + */ + void registerPlugin( Plugin* plugin ); + + /** + * Based on the template plugins' filter string, this function checks the supplied tag for + * supported extensions and adds them as new plugins to the supplied Plugin instance. + * @param plugin The Plugin-derived object that will have the newly created plugins embedded. + * @param tag The Tag to check for supported extensions. + */ + void addPlugins( Plugin& plugin, const Tag* tag ); + + /** + * Based on the template plugins' filter string, this function checks the supplied tag for + * supported extensions and adds them as new plugins to the supplied Jingle instance. + * @param jingle The Jingle object that will have the newly created plugins embedded. + * @param tag The Tag to check for supported extensions. + */ + void addPlugins( Session::Jingle& jingle, const Tag* tag ); + + private: + /** + * Creates a new instance. + */ + PluginFactory(); + + PluginList m_plugins; + + }; + + } + +} + +#endif // JINGLEPLUGINFACTORY_H__ diff --git a/libs/libgloox/jinglesession.cpp b/libs/libgloox/jinglesession.cpp new file mode 100644 index 0000000..c53858f --- /dev/null +++ b/libs/libgloox/jinglesession.cpp @@ -0,0 +1,501 @@ +/* + Copyright (c) 2007-2015 by Jakob Schröter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "jinglesession.h" + +#include "clientbase.h" +#include "error.h" +#include "jinglecontent.h" +#include "jinglesessionhandler.h" +#include "tag.h" +#include "util.h" + +namespace gloox +{ + + namespace Jingle + { + + static const char* actionValues [] = { + "content-accept", + "content-add", + "content-modify", + "content-reject", + "content-remove", + "description-info", + "security-info", + "session-accept", + "session-info", + "session-initiate", + "session-terminate", + "transport-accept", + "transport-info", + "transport-reject", + "transport-replace" + }; + + static inline Action actionType( const std::string& type ) + { + return (Action)util::lookup( type, actionValues ); + } + + // ---- Session::Reason ---- + static const char* reasonValues [] = { + "alternative-session", + "busy", + "cancel", + "connectivity-error", + "decline", + "expired", + "failed-application", + "failed-transport", + "general-error", + "gone", + "incompatible-parameters", + "media-error", + "security-error", + "success", + "timeout", + "unsupported-applications", + "unsupported-transports" + }; + + static inline Session::Reason::Reasons reasonType( const std::string& type ) + { + return (Session::Reason::Reasons)util::lookup( type, reasonValues ); + } + + Session::Reason::Reason( Reasons reason, + const std::string& sid, + const std::string& text) + : Plugin( PluginReason ), m_reason( reason ), m_sid( sid ), m_text( text ) + { + } + + Session::Reason::Reason( const Tag* tag ) + : Plugin( PluginReason ) + { + if( !tag || tag->name() != "reason" ) + return; + + const TagList& l = tag->children(); + TagList::const_iterator it = l.begin(); + for( ; it != l.end(); ++it ) + { + if( (*it)->name() == "text" ) + m_text = (*it)->cdata(); + else if( (*it)->xmlns() == XMLNS_JINGLE ) + m_reason = reasonType( (*it)->name() ); + } + } + + Session::Reason::~Reason() + { + } + + const std::string& Session::Reason::filterString() const + { + static const std::string filter = "jingle/reason"; + return filter; + } + + Tag* Session::Reason::tag() const + { + if( m_reason == InvalidReason ) + return 0; + + Tag* t = new Tag( "reason" ); + Tag* r = new Tag( t, util::lookup( m_reason, reasonValues ) ); + if( m_reason == AlternativeSession && !m_sid.empty() ) + new Tag( r, "sid", m_sid ); + + if( !m_text.empty() ) + new Tag( t, "text", m_text ); + + return t; + } + + Plugin* Session::Reason::clone() const + { + return new Reason( *this ); + } + // ---- ~Session::Reason ---- + + // ---- Session::Jingle ---- + Session::Jingle::Jingle( Action action, const JID& initiator, const JID& responder, + const PluginList& plugins, const std::string& sid ) + : StanzaExtension( ExtJingle ), m_action( action ), m_sid( sid ), + m_initiator( initiator ), m_responder( responder ), m_plugins( plugins ), m_tag( 0 ) + { + } + +#ifdef JINGLE_TEST + Session::Jingle::Jingle( Action action, const JID& initiator, const JID& responder, + const Plugin* plugin, const std::string& sid ) + : StanzaExtension( ExtJingle ), m_action( action ), m_sid( sid ), + m_initiator( initiator ), m_responder( responder ), m_tag( 0 ) + { + if( plugin ) + m_plugins.push_back( plugin ); + } +#endif + + Session::Jingle::Jingle( const Tag* tag ) + : StanzaExtension( ExtJingle ), m_action( InvalidAction ), m_tag( 0 ) + { + if( !tag || tag->name() != "jingle" ) + return; + + m_action = actionType( tag->findAttribute( "action" ) ); + m_initiator.setJID( tag->findAttribute( "initiator" ) ); + m_responder.setJID( tag->findAttribute( "responder" ) ); + m_sid = tag->findAttribute( "sid" ); + + m_tag = tag->clone(); + } + +// Session::Jingle::Jingle( const Jingle& right ) +// : StanzaExtension( ExtJingle ), m_action( right.m_action ), +// m_sid( right.m_sid ), m_initiator( right.m_initiator ), +// m_responder( right.m_responder ) +// { +// PluginList::const_iterator it = right.m_plugins.begin(); +// for( ; it != right.m_plugins.end(); ++it ) +// m_plugins.push_back( (*it)->clone() ); +// } + + Session::Jingle::~Jingle() + { + util::clearList( m_plugins ); + } + + const std::string& Session::Jingle::filterString() const + { + static const std::string filter = "/iq/jingle[@xmlns='" + XMLNS_JINGLE + "']"; + return filter; + } + + Tag* Session::Jingle::tag() const + { + if( m_action == InvalidAction || m_sid.empty() ) + return 0; + + Tag* t = new Tag( "jingle" ); + t->setXmlns( XMLNS_JINGLE ); + t->addAttribute( "action", util::lookup( m_action, actionValues ) ); + + if( m_initiator && m_action == SessionInitiate ) + t->addAttribute( "initiator", m_initiator.full() ); + + if( m_responder && m_action == SessionAccept ) + t->addAttribute( "responder", m_responder.full() ); + + t->addAttribute( "sid", m_sid ); + + PluginList::const_iterator it = m_plugins.begin(); + for( ; it != m_plugins.end(); ++it ) + t->addChild( (*it)->tag() ); + + return t; + } + + StanzaExtension* Session::Jingle::clone() const + { + return new Jingle( *this ); + } + // ---- ~Session::Jingle ---- + + // ---- Session ---- + Session::Session( ClientBase* parent, const JID& callee, SessionHandler* jsh ) + : m_parent( parent ), m_state( Ended ), m_remote( callee ), + m_handler( jsh ), m_valid( false ) + { + if( !m_parent || !m_handler || !m_remote ) + return; + + m_initiator = m_parent->jid(); + m_sid = m_parent->getID(); + + m_valid = true; + } + + Session::Session( ClientBase* parent, const JID& callee, const Session::Jingle* jingle, SessionHandler* jsh ) + : m_parent( parent ), m_state( Ended ), m_handler( jsh ), m_valid( false ) + { + if( !m_parent || !m_handler || !callee /*|| jingle->action() != SessionInitiate*/ ) + return; + + m_remote = callee; + m_sid = jingle->sid(); + + m_valid = true; + } + + Session::~Session() + { + if( m_parent ) + m_parent->removeIDHandler( this ); + } + + bool Session::contentAccept( const Content* content ) + { + if( m_state < Pending ) + return false; + + return doAction( ContentAccept, content ); + } + + bool Session::contentAdd( const Content* content ) + { + if( m_state < Pending ) + return false; + + return doAction( ContentAdd, content ); + } + + bool Session::contentAdd( const PluginList& contents ) + { + if( m_state < Pending ) + return false; + + return doAction( ContentAdd, contents ); + } + + bool Session::contentModify( const Content* content ) + { + if( m_state < Pending ) + return false; + + return doAction( ContentModify, content ); + } + + bool Session::contentReject( const Content* content ) + { + if( m_state < Pending ) + return false; + + return doAction( ContentReject, content ); + } + + bool Session::contentRemove( const Content* content ) + { + if( m_state < Pending ) + return false; + + return doAction( ContentRemove, content ); + } + + bool Session::descriptionInfo( const Plugin* info ) + { + if( m_state < Pending ) + return false; + + return doAction( DescriptionInfo, info ); + } + + bool Session::securityInfo( const Plugin* info ) + { + if( m_state < Pending ) + return false; + + return doAction( SecurityInfo, info ); + } + + bool Session::sessionAccept( const Content* content ) + { + if( !content || m_state > Pending ) + return false; + + m_state = Active; + return doAction( SessionAccept, content ); + } + + bool Session::sessionAccept( const PluginList& plugins ) + { + if( plugins.empty() || m_state != Pending ) + return false; + + m_state = Active; + return doAction( SessionAccept, plugins ); + } + + bool Session::sessionInfo( const Plugin* info ) + { + if( m_state < Pending ) + return false; + + return doAction( SessionInfo, info ); + } + + bool Session::sessionInitiate( const Content* content ) + { + if( !content || !m_initiator || m_state >= Pending ) + return false; + + m_state = Pending; + return doAction( SessionInitiate, content ); + } + + bool Session::sessionInitiate( const PluginList& plugins ) + { + if( plugins.empty() || !m_initiator || m_state >= Pending ) + return false; + + m_state = Pending; + return doAction( SessionInitiate, plugins ); + } + + bool Session::sessionTerminate( Session::Reason* reason ) + { + if( m_state < Pending /*|| !m_initiator*/ ) + return false; + + m_state = Ended; + + return doAction( SessionTerminate, reason ); + } + + bool Session::transportAccept( const Content* content ) + { + if( m_state < Pending ) + return false; + + return doAction( TransportAccept, content ); + } + + bool Session::transportInfo( const Plugin* info ) + { + if( m_state < Pending ) + return false; + + return doAction( TransportInfo, info ); + } + + bool Session::transportReject( const Content* content ) + { + if( m_state < Pending ) + return false; + + return doAction( TransportReject, content ); + } + + bool Session::transportReplace( const Content* content ) + { + if( m_state < Pending ) + return false; + + return doAction( TransportReplace, content ); + } + + bool Session::doAction( Action action, const Plugin* plugin ) + { + PluginList pl; + pl.push_back( plugin ); + return doAction( action, pl ); + } + + bool Session::doAction( Action action, const PluginList& plugins ) + { + if( !m_valid || !m_parent ) + return false; + + IQ init( IQ::Set, m_remote, m_parent->getID() ); + init.addExtension( new Jingle( action, m_initiator, m_responder, plugins, m_sid ) ); + m_parent->send( init, this, action ); + + return true; + } + + bool Session::handleIq( const IQ& iq ) + { + const Jingle* j = iq.findExtension( ExtJingle ); + if( !j || j->sid() != m_sid || !m_handler || !m_parent ) + return false; + + switch( j->action() ) + { + case SessionAccept: + m_state = Active; + m_responder = j->responder(); + break; + case SessionInitiate: + m_state = Pending; + m_initiator = j->initiator(); + if( !m_responder ) + m_responder = m_parent->jid(); + break; + case SessionTerminate: + m_state = Ended; + break; + default: + break; + } + + IQ re( IQ::Result, iq.from(), iq.id() ); + m_parent->send( re ); + + m_handler->handleSessionAction( j->action(), this, j ); + + return true; + } + + void Session::handleIqID( const IQ& iq, int context ) + { + if( iq.subtype() == IQ::Error ) + { + + const Error* e = iq.findExtension( ExtError ); + m_handler->handleSessionActionError( (Action)context, this, e ); + + switch( context ) + { + case ContentAccept: + break; + case ContentAdd: + break; + case ContentModify: + break; + case ContentReject: + break; + case ContentRemove: + break; + case DescriptionInfo: + break; + case SessionAccept: + break; + case SessionInfo: + break; + case SessionInitiate: + m_state = Ended; + break; + case SessionTerminate: + break; + case TransportAccept: + break; + case TransportInfo: + break; + case TransportReject: + break; + case TransportReplace: + break; + case InvalidAction: + break; + default: + break; + } + } + } + + } + +} diff --git a/libs/libgloox/jinglesession.h b/libs/libgloox/jinglesession.h new file mode 100644 index 0000000..719505f --- /dev/null +++ b/libs/libgloox/jinglesession.h @@ -0,0 +1,575 @@ +/* + Copyright (c) 2007-2015 by Jakob Schröter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef JINGLESESSION_H__ +#define JINGLESESSION_H__ + +#include "stanzaextension.h" +#include "tag.h" +#include "iqhandler.h" +#include "jingleplugin.h" + +#include + +namespace gloox +{ + + class ClientBase; + + /** + * @brief The namespace containing Jingle-related (@xep{0166} et. al.) classes. + * + * See @link gloox::Jingle::SessionManager SessionManager @endlink for more information + * about Jingle in gloox. + * + * @author Jakob Schröter + * @since 1.0.5 + */ + namespace Jingle + { + + class Description; + class Transport; + class SessionHandler; + class Content; + + /** + * Jingle Session actions. + */ + enum Action + { + ContentAccept, /**< Accept a content-add action received from another party. */ + ContentAdd, /**< Add one or more new content definitions to the session. */ + ContentModify, /**< Change the directionality of media sending. */ + ContentReject, /**< Reject a content-add action received from another party. */ + ContentRemove, /**< Remove one or more content definitions from the session. */ + DescriptionInfo, /**< Exchange information about parameters for an application type. */ + SecurityInfo, /**< Send information related to establishment or maintenance of security preconditions. */ + SessionAccept, /**< Definitively accept a session negotiation. */ + SessionInfo, /**< Send session-level information, such as a ping or a ringing message. */ + SessionInitiate, /**< Request negotiation of a new Jingle session. */ + SessionTerminate, /**< End an existing session. */ + TransportAccept, /**< Accept a transport-replace action received from another party. */ + TransportInfo, /**< Exchange transport candidates. */ + TransportReject, /**< Reject a transport-replace action received from another party. */ + TransportReplace, /**< Redefine a transport method or replace it with a different method. */ + InvalidAction /**< Invalid action. */ + }; + + /** + * @brief This is an implementation of a Jingle Session (@xep{0166}). + * + * See @link gloox::Jingle::SessionManager Jingle::SessionManager @endlink for info on how to use + * Jingle in gloox. + * + * XEP Version: 1.1 + * + * @author Jakob Schröter + * @since 1.0.5 + */ + class GLOOX_API Session : public IqHandler + { + + friend class SessionManager; + + public: + /** + * Session state. + */ + enum State + { + Ended, /**< The session has ended or was not active yet. */ + Pending, /**< The session has been initiated but has not yet been accepted by the remote party. */ + Active /**< The session is active. */ + }; + + /** + * @brief An abstraction of a Jingle (@xep{0166}) session terminate reason. + * + * XEP Version: 1.1 + * + * @author Jakob Schröter + * @since 1.0.5 + */ + class GLOOX_API Reason : public Plugin + { + public: + /** + * Defined reasons for terminating a Jingle Session. + */ + enum Reasons + { + AlternativeSession, /**< An alternative session exists that should be used. */ + Busy, /**< The terminating party is busy. */ + Cancel, /**< The session has been canceled. */ + ConnectivityError, /**< Connectivity error. */ + Decline, /**< The terminating party formally declines the request. */ + Expired, /**< The session has expired. */ + FailedApplication, /**< Application type setup failed. */ + FailedTransport, /**< Transport setup has failed. */ + GeneralError, /**< General error. */ + Gone, /**< Participant went away. */ + IncompatibleParameters, /**< Offered or negotiated application type parameters not supported. */ + MediaError, /**< Media error. */ + SecurityError, /**< Security error. */ + Success, /**< Session terminated after successful call. */ + Timeout, /**< A timeout occured. */ + UnsupportedApplications, /**< The terminating party does not support any of the offered application formats. */ + UnsupportedTransports, /**< The terminating party does not support any of the offered transport methods. */ + InvalidReason /**< Invalid reason. */ + }; + + /** + * Constructor. + * @param reason The reason for the termination of the session. + * @param sid An optional session ID (only used if reason is AlternativeSession). + * @param text An optional human-readable text explaining the reason for the session termination. + */ + Reason( Reasons reason, const std::string& sid = EmptyString, + const std::string& text = EmptyString ); + + /** + * Constructs a new element by parsing the given Tag. + * @param tag A tag to parse. + */ + Reason( const Tag* tag = 0 ); + + /** + * Virtual destructor. + */ + virtual ~Reason(); + + /** + * Returns the reason for the session termination. + * @return The reason for the session termination. + */ + Reasons reason() const { return m_reason; } + + /** + * Returns the session ID of the alternate session, if given (only applicable + * if reason() returns AlternativeSession). + * @return The session ID of the alternative session, or an empty string. + */ + const std::string& sid() const { return m_sid; } + + /** + * Returns the content of an optional, human-readable + * <text> element. + * @return An optional text describing the reason for the terminate action. + */ + const std::string& text() const { return m_text; } + + // reimplemented from Plugin + virtual const std::string& filterString() const; + + // reimplemented from Plugin + virtual Tag* tag() const; + + // reimplemented from Plugin + virtual Plugin* newInstance( const Tag* tag ) const { return new Reason( tag ); } + + // reimplemented from Plugin + virtual Plugin* clone() const; + + private: + Reasons m_reason; + std::string m_sid; + std::string m_text; + + }; + + /** + * @brief This is an abstraction of Jingle's (@xep{0166}) <jingle> element as a StanzaExtension. + * + * XEP Version: 1.1 + * @author Jakob Schröter + * @since 1.0.5 + */ + class Jingle : public StanzaExtension + { + + friend class Session; + + public: + /** + * Constructs a new object from the given Tag. + * @param tag The Tag to parse. + */ + Jingle( const Tag* tag = 0 ); + + /** + * Virtual Destructor. + */ + virtual ~Jingle(); + + /** + * Returns the session ID. + * @return The session ID. + */ + const std::string& sid() const { return m_sid; } + + /** + * Returns the 'session initiator'. This will usually be empty for any action other than 'session-initiate'. + * @return The 'session initiator'. + */ + const JID& initiator() const { return m_initiator; } + + /** + * Returns the 'session responder'. This will usually be empty for any action other than 'session-accept'. + * @return The 'session responder'. + */ + const JID& responder() const { return m_responder; } + + /** + * Returns this Jingle's action. + * @return The action. + */ + Action action() const { return m_action; } + + /** + * Adds a Plugin as child. + * @param plugin A plugin to be embedded. Will be owned by this instance and deleted in the destructor. + */ + void addPlugin( const Plugin* plugin ) { if( plugin ) m_plugins.push_back( plugin ); } + + /** + * Returns a reference to a list of embedded plugins. + * @return A reference to a list of embedded plugins. + */ + const PluginList& plugins() const { return m_plugins; } + + /** + * Returns the tag to build plugins from. + * @return The tag to build plugins from. + */ + Tag* embeddedTag() const { return m_tag; } + + // reimplemented from StanzaExtension + virtual const std::string& filterString() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* newInstance( const Tag* tag ) const + { + return new Jingle( tag ); + } + + // reimplemented from StanzaExtension + virtual Tag* tag() const; + + // reimplemented from StanzaExtension + virtual StanzaExtension* clone() const; + +#ifdef JINGLE_TEST + public: +#else + private: +#endif + /** + * Constructs a new object and fills it according to the parameters. + * @param action The Action to carry out. + * @param initiator The full JID of the initiator of the session flow. Will only be used for the SessionInitiate action. + * @param responder The full JID of the responder. Will only be used for the SessionAccept action. + * @param plugins A list of contents (plugins) for the <jingle> + * element. Usually, these will be Content objects, but can be any Plugin-derived objects. + * These objects will be owned and deleted by this Jingle instance. + * @param sid The session ID: + */ + Jingle( Action action, const JID& initiator, const JID& responder, + const PluginList& plugins, const std::string& sid ); + +#ifdef JINGLE_TEST + /** + * Constructs a new object and fills it according to the parameters. + * @param action The Action to carry out. + * @param initiator The full JID of the initiator of the session flow. Will only be used for the SessionInitiate action. + * @param responder The full JID of the responder. Will only be used for the SessionAccept action. + * @param plugin A single content (plugin) for the <jingle> + * element. Usually, this will be a Content object, but can be any Plugin-derived object. + * This object will be owned and deleted by this Jingle instance. + * @param sid The session ID: + */ + Jingle( Action action, const JID& initiator, const JID& responder, + const Plugin* plugin, const std::string& sid ); + #endif + +// /** +// * Copy constructor. +// * @param right The instance to copy. +// */ +// Jingle( const Jingle& right ); + + Action m_action; + std::string m_sid; + JID m_initiator; + JID m_responder; + PluginList m_plugins; + Tag* m_tag; + + }; + + /** + * Virtual Destructor. + */ + virtual ~Session(); + + /** + * Explicitely sets a new session initiator. The initiator defaults to the initiating entity's JID. + * Normally, you should not need to use this function. + * @param initiator The new initiator. + */ + void setInitiator( const JID& initiator ) { m_initiator = initiator; } + + /** + * Returns the session's initiator. + * @return The session's initiator. + */ + const JID& initiator() const { return m_initiator; } + + /** + * Returns the session's responder. This will only return something useful after the 'session-accept' action has been + * sent/received. + * @return The session's responder. + */ + const JID& responder() const { return m_responder; } + + /** + * Explicitely sets the 'session responder'. By default, the associated ClientBase's jid() will be used. + * You can change this here. + * @note Changing the session responder only affects the 'session-accept' action; it will have no effect after + * that action has been executed or if the local entity is the session initiator. + * @param jid The session responder's full JID. + */ + void setResponder( const JID& jid ) { m_responder = jid; } + + /** + * Explicitely sets a new handler for the session. + * @param handler The new handler. + */ + void setHandler( SessionHandler* handler ) { m_handler = handler; } + + /** + * Sends a 'content-accept' notification. + * @param content The accepted content. + * This object will be owned and deleted by this Session instance. + * @return @b False if a prerequisite is not met, @b true otherwise. + */ + bool contentAccept( const Content* content ); + + /** + * Sends a 'content-add' request. + * @param content The proposed content to be added. + * This object will be owned and deleted by this Session instance. + * @return @b False if a prerequisite is not met, @b true otherwise. + */ + bool contentAdd( const Content* content ); + + /** + * Sends a 'content-add' request. + * @param contents A list of proposed content to be added. + * These objects will be owned and deleted by this Session instance. + * @return @b False if a prerequisite is not met, @b true otherwise. + */ + bool contentAdd( const PluginList& contents ); + + /** + * Sends a 'content-modify' request. + * @param content The proposed content type to be modified. + * This object will be owned and deleted by this Session instance. + * @return @b False if a prerequisite is not met, @b true otherwise. + */ + bool contentModify( const Content* content ); + + /** + * Sends a 'content-reject' reply. + * @param content The rejected content. + * This object will be owned and deleted by this Session instance. + * @return @b False if a prerequisite is not met, @b true otherwise. + */ + bool contentReject( const Content* content ); + + /** + * Sends a 'content-remove' request. + * @param content The content type to be removed. + * This object will be owned and deleted by this Session instance. + * @return @b False if a prerequisite is not met, @b true otherwise. + */ + bool contentRemove( const Content* content ); + + /** + * Sends a 'description-info' notice. + * @param info The payload. + * This object will be owned and deleted by this Session instance. + * @return @b False if a prerequisite is not met, @b true otherwise. + */ + bool descriptionInfo( const Plugin* info ); + + /** + * Sends a 'security-info' notice. + * @param info A security pre-condition. + * This object will be owned and deleted by this Session instance. + * @return @b False if a prerequisite is not met, @b true otherwise. + */ + bool securityInfo( const Plugin* info ); + + /** + * Accepts an incoming session with the given content. + * @param content A pair of application description and transport method wrapped in a Content that describes + * the accepted session parameters. + * This object will be owned and deleted by this Session instance. + * @return @b False if a prerequisite is not met, @b true otherwise. + */ + bool sessionAccept( const Content* content ); + + /** + * Accepts an incoming session with the given list of contents. + * @param content A list of Content objects that describe the accepted session parameters. + * These objects will be owned and deleted by this Session instance. + * @return @b False if a prerequisite is not met, @b true otherwise. + */ + bool sessionAccept( const PluginList& plugins ); + + /** + * Sends a 'session-info' notice. + * @param info The payload. + * This object will be owned and deleted by this Session instance. + * @return @b False if a prerequisite is not met, @b true otherwise. + */ + bool sessionInfo( const Plugin* info ); + + /** + * Initiates a session with a remote entity. + * @param content A Content object. You may use initiate( const PluginList& contents ) for more than one Content. + * This object will be owned and deleted by this Session instance. + * @return @b False if a prerequisite is not met, @b true otherwise. + */ + bool sessionInitiate( const Content* content ); + + /** + * Initiates a session with a remote entity. + * @param plugins A list of Content objects. It is important to pass a (list of) Content objects here. + * Even though e.g. Jingle::ICEUDP are Plugin-derived, too, using anything other than Content here will result + * in erroneous behaviour at best. You may use sessionInitiate( const Content* content ) for just one Content. + * These objects will be owned and deleted by this Session instance. + * @return @b False if a prerequisite is not met, @b true otherwise. + */ + bool sessionInitiate( const PluginList& plugins ); + + /** + * Terminates the current session, if it is at least in Pending state, with the given reason. The sid parameter + * is ignored unless the reason is AlternativeSession. + * @param reason The reason for terminating the session. + * This object will be owned and deleted by this Session instance. + * @return @b False if a prerequisite is not met, @b true otherwise. + */ + bool sessionTerminate( Session::Reason* reason ); + + /** + * Sends a 'transport-accept' reply. + * @param content The accepted transport wrapped in a Content. + * This object will be owned and deleted by this Session instance. + * @return @b False if a prerequisite is not met, @b true otherwise. + */ + bool transportAccept( const Content* content ); + + /** + * Sends a 'transport-info' notice. + * @param info The payload. + * This object will be owned and deleted by this Session instance. + * @return @b False if a prerequisite is not met, @b true otherwise. + */ + bool transportInfo( const Plugin* info ); + + /** + * Sends a 'transport-reject' reply. + * @param content The rejected transport wrapped in a Content. + * This object will be owned and deleted by this Session instance. + * @return @b False if a prerequisite is not met, @b true otherwise. + */ + bool transportReject( const Content* content ); + + /** + * Sends a 'transport-replace' request. + * @param content The proposed transport to be replaced wrapped in a Content. + * This object will be owned and deleted by this Session instance. + * @return @b False if a prerequisite is not met, @b true otherwise. + */ + bool transportReplace( const Content* content ); + + /** + * Returns the session's state. + * @return The session's state. + */ + State state() const { return m_state; } + + /** + * Sets the session's ID. This will be initialized to a random value (or taken from an incoming session request) + * by default. You should not need to set the session ID manually. + * @param sid The session's id. + */ + void setSID( const std::string& sid ) { m_sid = sid; } + + /** + * Returns the session's ID. + * @return The session's ID. + */ + const std::string& sid() const { return m_sid; } + + // reimplemented from IqHandler + virtual bool handleIq( const IQ& iq ); + + // reimplemented from IqHandler + virtual void handleIqID( const IQ& iq, int context ); + +#ifdef JINGLE_TEST + public: +#else + private: +#endif + /** + * Creates a new Jingle Session. + * @param parent The ClientBase to use for communication. + * @param callee The remote end of the session. + * @param jsh The handler to receive events and results. + */ + Session( ClientBase* parent, const JID& callee, SessionHandler* jsh ); + + /** + * Creates a new Session from the incoming Jingle object. + * This is a NOOP for Jingles that have an action() different from SessionInitiate. + * @param parent The ClientBase to use for communication. + * @param callee The remote entity. + * @param jingle The Jingle object to init the Session from. + * @param jsh The handler to receive events and results. + */ + Session( ClientBase* parent, const JID& callee, const Session::Jingle* jingle, + SessionHandler* jsh ); + + bool doAction( Action action, const Plugin* plugin ); + bool doAction( Action action, const PluginList& plugin ); + + ClientBase* m_parent; + State m_state; + JID m_remote; + JID m_initiator; + JID m_responder; + SessionHandler* m_handler; + std::string m_sid; + bool m_valid; + + }; + + } + +} + +#endif // JINGLESESSION_H__ diff --git a/libs/libgloox/jinglesessionhandler.h b/libs/libgloox/jinglesessionhandler.h new file mode 100644 index 0000000..e655652 --- /dev/null +++ b/libs/libgloox/jinglesessionhandler.h @@ -0,0 +1,74 @@ +/* + Copyright (c) 2008-2015 by Jakob Schröter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef JINGLESESSIONHANDLER_H__ +#define JINGLESESSIONHANDLER_H__ + +#include "macros.h" +#include "jinglesession.h" + +namespace gloox +{ + + namespace Jingle + { + + /** + * @brief A Jingle session handler. + * + * @author Jakob Schröter + * @since 1.0.5 + */ + class GLOOX_API SessionHandler + { + public: + /** + * Virtual destructor. + */ + virtual ~SessionHandler() {} + + /** + * This function is called when the remote party requests an action to be taken. + * @param action The requested action. A convenience parameter, identical to jingle->action(). + * @param session The affected session. + * @param jingle The complete Jingle. + * @note Note that an action can cause a session state change. You may check using session->state(). + * @note Also note that you have to reply to most actions, usually with the *Accept or *Reject counterpart, + * using the similarly-named functions that Session offers. + */ + virtual void handleSessionAction( Action action, Session* session, const Session::Jingle* jingle ) = 0; + + /** + * This function is called when a request to a remote entity returns an error. + * @param action The Action that failed. + * @param session The affected session. + * @param error The error. May be 0 in special cases. + * @note Note that an action can cause a session state change. You may check using session->state(). + */ + virtual void handleSessionActionError( Action action, Session* session, const Error* error ) = 0; + + /** + * This function is called if a remote entity wants to establish a Jingle session. + * @param session The new Jingle session. + * @note Note that you have to explicitely accept or reject the session by calling either of session->sessionAccept() and + * session->sessionTerminate(), respectively. + */ + virtual void handleIncomingSession( Session* session ) = 0; + + }; + + } + +} + +#endif // JINGLESESSIONHANDLER_H__ diff --git a/libs/libgloox/jinglesessionmanager.cpp b/libs/libgloox/jinglesessionmanager.cpp new file mode 100644 index 0000000..8311f01 --- /dev/null +++ b/libs/libgloox/jinglesessionmanager.cpp @@ -0,0 +1,104 @@ +/* + Copyright (c) 2013-2015 by Jakob Schröter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#include "jinglesessionmanager.h" + +#include "clientbase.h" +#include "jinglesession.h" +#include "jinglesessionhandler.h" +#include "disco.h" +#include "util.h" + +namespace gloox +{ + + namespace Jingle + { + + SessionManager::SessionManager( ClientBase* parent, SessionHandler* sh ) + : m_parent( parent ), m_handler( sh ) + { + if( m_parent ) + { + m_parent->registerStanzaExtension( new Session::Jingle() ); + m_parent->registerIqHandler( this, ExtJingle ); + m_parent->disco()->addFeature( XMLNS_JINGLE ); + } + } + + SessionManager::~SessionManager() + { + util::clearList( m_sessions ); + } + + void SessionManager::registerPlugin( Plugin* plugin ) + { + if( !plugin ) + return; + + m_factory.registerPlugin( plugin ); + if( m_parent ) + { + StringList features = plugin->features(); + StringList::const_iterator it = features.begin(); + for( ; it != features.end(); ++it ) + m_parent->disco()->addFeature( (*it) ); + } + } + + Session* SessionManager::createSession( const JID& callee, SessionHandler* handler ) + { + if( !( handler || m_handler ) || !callee ) + return 0; + + Session* sess = new Session( m_parent, callee, handler ? handler : m_handler ); + m_sessions.push_back( sess ); + return sess; + } + + void SessionManager::discardSession( Session* session ) + { + if( !session ) + return; + + m_sessions.remove( session ); + delete session; + } + + bool SessionManager::handleIq( const IQ& iq ) + { + const Session::Jingle* j = iq.findExtension( ExtJingle ); + if( !j ) + return false; + + m_factory.addPlugins( const_cast( *j ), j->embeddedTag() ); + + SessionList::iterator it = m_sessions.begin(); + for( ; it != m_sessions.end() && (*it)->sid() != j->sid(); ++it ) ; + if( it == m_sessions.end() ) + { + Session* s = new Session( m_parent, iq.from(), j, m_handler ); + m_sessions.push_back( s ); + m_handler->handleIncomingSession( s ); + s->handleIq( iq ); + } + else + { + (*it)->handleIq( iq ); + } + return true; + } + + } + +} diff --git a/libs/libgloox/jinglesessionmanager.h b/libs/libgloox/jinglesessionmanager.h new file mode 100644 index 0000000..fd388ba --- /dev/null +++ b/libs/libgloox/jinglesessionmanager.h @@ -0,0 +1,117 @@ +/* + Copyright (c) 2013-2015 by Jakob Schröter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef JINGLESESSIONMANAGER_H__ +#define JINGLESESSIONMANAGER_H__ + +#include "macros.h" +#include "iqhandler.h" +#include "jinglepluginfactory.h" + +#include + +namespace gloox +{ + + class ClientBase; + + namespace Jingle + { + + class Session; + class SessionHandler; + + /** + * @brief The SessionManager is responsible for creating and destroying Jingle sessions, as well as for delegating incoming + * IQs to their respective sessions. This is part of Jingle (@xep{0166}). + * + * @note The classes in the Jingle namespace implement the signaling part of Jingle only. + * Establishing connections to a remote entity or transfering data outside the XMPP channel + * is out of scope of gloox. + * + * To use Jingle with gloox you should first instantiate a Jingle::SessionManager. The SessionManager will + * let you create new Jingle sessions and notify the respective handler about incoming Jingle session requests. + * It will also announce generic Jingle support via Disco. You have to register any + * @link gloox::Jingle::Plugin Jingle plugins @endlink you want to use using registerPlugin(). + * These will automatically announce any additional features via Disco. + * + * Use createSession() to create a new Session. + * + * Implement SessionHandler::handleIncomingSession() to receive incoming session requests. + * + * Use discardSession() to get rid of a session. Do not delete a session manually. + * + * There is no limit to the number of concurrent sessions. + * + * @author Jakob Schröter + * @since 1.0.5 + */ + class GLOOX_API SessionManager : public IqHandler + { + public: + /** + * Creates new instance. There should be only one SessionManager per ClientBase. + * @param parent A ClientBase instance used for sending and receiving. + * @param sh A session handler that will be notified about incoming session requests. + * Only handleIncomingSession() will be called in that handler. + */ + SessionManager( ClientBase* parent, SessionHandler* sh ); + + /** + * Virtual destructor. + */ + virtual ~SessionManager(); + + /** + * Registers an empty Plugin as a template with the manager. + * @param plugin The plugin to register. + */ + void registerPlugin( Plugin* plugin ); + + /** + * Lets you create a new Jingle session. + * @param callee The remote entity's JID. + * @param handler The handler responsible for handling events assicoated with the new session. + * @return The new session. + * @note You should not delete a session yourself. Instead, pass it to discardSession(). + */ + Session* createSession( const JID& callee, SessionHandler* handler ); + + /** + * Removes a given session from the nternal queue and deletes it. + * @param session The session to delete. + */ + void discardSession( Session* session ); + + + // reimplemented from IqHandler + virtual bool handleIq( const IQ& iq ); + + // reimplemented from IqHandler + virtual void handleIqID( const IQ& /*iq*/, int /*context*/ ) {} + + private: + typedef std::list SessionList; + + SessionList m_sessions; + ClientBase* m_parent; + SessionHandler* m_handler; + PluginFactory m_factory; + + }; + + } + +} + +#endif // JINGLESESSIONMANAGER_H__ diff --git a/libs/libgloox/linklocal.h b/libs/libgloox/linklocal.h new file mode 100644 index 0000000..c508288 --- /dev/null +++ b/libs/libgloox/linklocal.h @@ -0,0 +1,79 @@ +/* + Copyright (c) 2012-2015 by Jakob Schröter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#ifndef LINKLOCAL_H__ +#define LINKLOCAL_H__ + +#include "config.h" + +#ifdef HAVE_MDNS + +#include +#include + +namespace gloox +{ + + /** + * @brief Namespace holding all the Link-local-related structures and definitions. + * + * See @ref gloox::LinkLocal::Manager for more information on how to implement + * link-local messaging. + */ + namespace LinkLocal + { + + class Client; + + /** + * Used in conjunction with Service to indicate whether a service has been added (newly advertised) or removed. + */ + enum Flag + { + AddService, /**< A service has been added. */ + RemoveService /**< A service has been removed. */ + }; + + /** + * @brief An abstraction of the parameters of a single link-local service. + * + * @author Jakob Schröter + * @since 1.0.x + */ + struct Service + { + friend class Manager; + + private: + Service( Flag _flag, const std::string& _service, const std::string& _regtype, const std::string& _domain, int _interface ) + : flag( _flag ), service( _service ), regtype( _regtype ), domain( _domain ), iface( _interface ) {} + + public: + Flag flag; + std::string service; + std::string regtype; + std::string domain; + int iface; + }; + + /** + * A list of services. + */ + typedef std::list ServiceList; + + } + +} + +#endif // HAVE_MDNS + +#endif // LINKLOCAL_H__ diff --git a/libs/libgloox/linklocalclient.cpp b/libs/libgloox/linklocalclient.cpp new file mode 100644 index 0000000..237f6b5 --- /dev/null +++ b/libs/libgloox/linklocalclient.cpp @@ -0,0 +1,219 @@ +/* + Copyright (c) 2012-2015 by Jakob Schröter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#include "linklocalclient.h" + +#ifdef HAVE_MDNS + +#include "gloox.h" +#include "tag.h" +#include "util.h" +#include "connectiontcpclient.h" + +#include + +#if ( !defined( _WIN32 ) && !defined( _WIN32_WCE ) ) || defined( __SYMBIAN32__ ) +# include +#endif + +#if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) +# include +#elif defined( _WIN32_WCE ) +# include +#endif + +namespace gloox +{ + + namespace LinkLocal + { + + Client::Client( const JID& jid ) + : gloox::Client( jid, EmptyString ), m_qRef( 0 ), m_rRef( 0 ), m_currentRef( 0 ), + m_interface( 0 ), m_port( 0 ), m_streamSent( false ) + { + } + + Client::~Client() + { + } + + bool Client::connect() + { + return ClientBase::connect( false ); + } + + bool Client::connect( const std::string& service, const std::string& type, + const std::string& domain, int iface ) + { + m_interface = interface; + return resolve( service, type, domain ); + } + + ConnectionError Client::recv( int timeout ) + { + if( m_connection && m_connection->state() == StateConnected ) + return ClientBase::recv( timeout ); + else + { + if( !m_currentRef ) + return ConnNoError; + + struct timeval tv; + + fd_set fds; + FD_ZERO( &fds ); + // the following causes a C4127 warning in VC++ Express 2008 and possibly other versions. + // however, the reason for the warning can't be fixed in gloox. + FD_SET( DNSServiceRefSockFD( m_currentRef ), &fds ); + + tv.tv_sec = timeout / 1000000; + tv.tv_usec = timeout % 1000000; + + if( select( FD_SETSIZE, &fds, 0, 0, timeout == -1 ? 0 : &tv ) > 0 ) + { + if( FD_ISSET( DNSServiceRefSockFD( m_currentRef ), &fds ) != 0 ) + DNSServiceProcessResult( m_currentRef ); + } + + return ConnNoError; + } + } + + + bool Client::resolve( const std::string& service, const std::string& type, + const std::string& domain ) + { + m_to = service; + m_rRef = 0; + DNSServiceErrorType e = DNSServiceResolve( &m_rRef, 0, m_interface, service.c_str(), type.c_str(), + domain.c_str(), (DNSServiceResolveReply)&handleResolveReply, this ); + if( e != kDNSServiceErr_NoError ) + { + DNSServiceRefDeallocate( m_rRef ); + m_rRef = 0; + return false; + } + m_currentRef = m_rRef; + + return true; + } + + void Client::handleResolveReply( DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, + DNSServiceErrorType errorCode, const char* fullname, const char* hosttarget, + uint16_t port, uint16_t txtLen, const unsigned char* txtRecord, void* context ) + { + if( !context || errorCode != kDNSServiceErr_NoError ) + return; + + // printf("Client::handleResolveReply susccessful, querying %s\n", hosttarget ); + + static_cast( context )->query( hosttarget, ntohs( port ) ); + } + + bool Client::query( const std::string& hostname, int port ) + { + m_port = port; + m_qRef = 0; + DNSServiceErrorType e = DNSServiceQueryRecord( &m_qRef, 0, m_interface, hostname.c_str(), kDNSServiceType_A, + kDNSServiceClass_IN, (DNSServiceQueryRecordReply)&handleQueryReply, this ); + if( e != kDNSServiceErr_NoError ) + { + // printf( "Client::query() failed\n" ); + DNSServiceRefDeallocate( m_qRef ); + m_qRef = 0; + return false; + } + m_currentRef = m_qRef; + + return true; + } + + void Client::handleQueryReply( DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, + DNSServiceErrorType errorCode, const char *fullname, uint16_t rrtype, + uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, + void *context ) + { + // printf("Client::handleQueryReply returned\n" ); + + if( !context || errorCode != kDNSServiceErr_NoError ) + return; + + const unsigned char* rd = static_cast( rdata ); + std::string addr = util::int2string( rd[0] ); + addr += '.'; + addr += util::int2string( rd[1] ); + addr += '.'; + addr += util::int2string( rd[2] ); + addr += '.'; + addr += util::int2string( rd[3] ); + // printf( "host %s is at %s\n", fullname, addr.c_str() ); + static_cast( context )->handleQuery( addr ); + } + + void Client::handleQuery( const std::string& addr ) + { + if( m_rRef ) + { + DNSServiceRefDeallocate( m_rRef ); + m_rRef = 0; + } + + ConnectionTCPClient* connection = new ConnectionTCPClient( this, logInstance(), addr, m_port ); + // printf( "LinkLocal::Client: connecting to %s:%d\n", addr.c_str(), m_port ); + ConnectionError e = connection->connect(); + if( e != ConnNoError ) + { + // printf( "connection error: %d\n", e ); + delete connection; + } + } + + void Client::handleConnect( const ConnectionBase* connection ) + { + if( m_qRef ) + { + DNSServiceRefDeallocate( m_qRef ); + m_qRef = 0; + m_currentRef = 0; + } + + // printf( "LinkLocal::Client::handleConnect()!!!\n" ); + ConnectionBase* cb = const_cast( connection ); + gloox::Client::setConnectionImpl( cb ); + gloox::Client::connect( false ); + sendStart( m_to ); + } + + void Client::handleStartNode( const Tag* start ) + { + // printf( "LinkLocal::Client::handleStartNode()\n" ); + if( start && !m_streamSent ) + sendStart( start->findAttribute( "from" ) ); + } + + void Client::sendStart( const std::string& to ) + { + m_streamSent = true; + std::string s = ""; + send( s ); + } + + } + +} + +#endif // HAVE_MDNS diff --git a/libs/libgloox/linklocalclient.h b/libs/libgloox/linklocalclient.h new file mode 100644 index 0000000..027c11a --- /dev/null +++ b/libs/libgloox/linklocalclient.h @@ -0,0 +1,125 @@ +/* + Copyright (c) 2012-2015 by Jakob Schröter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + +#ifndef LINKLOCALCLIENT_H__ +#define LINKLOCALCLIENT_H__ + +#include "config.h" + +#ifdef HAVE_MDNS + +#include "client.h" +#include "jid.h" + +#include + +#include + +namespace gloox +{ + + class Tag; + + namespace LinkLocal + { + + /** + * @brief An implementation of a link-local client. + * + * See @ref gloox::LinkLocal::Manager for more information on how to implement + * link-local messaging. + * + * @author Jakob Schröter + * @since 1.0.x + */ + class Client : public gloox::Client + { + public: + /** + * Constructs a new instance. + * @param jid The local JID to use. + */ + Client( const JID& jid ); + + /** + * Virtual destructor. + */ + virtual ~Client(); + + /** + * Internally sets up an already connected connection. + * @note Use this function only on a Client instance that you created for an @b incoming connection. + */ + bool connect(); + + /** + * Starts resolving the given service. Use values from Handler::handleBrowseReply(). + * @param service The service to connect to. + * @param type The service type. + * @param domain The service's domain. + * @param interface The network interface the service was found on. May be 0 to try + * to resolve the service on all available interfaces. + * @return @b True if resolving the service could be started successfully, @b false otherwise. + * @note Use this function only for @b outgoing connections. + */ + bool connect( const std::string& service, const std::string& type, const std::string& domain, int iface = 0 ); + + /** + * Call this periodically to receive data from the underlying socket. + * @param timeout An optional timeout in microseconds. Default of -1 means blocking + * until data was available. + * @return The state of the underlying connection. + */ + virtual ConnectionError recv( int timeout = -1 ); + + // reimplemented from ConnectionDataHandler, overwriting ClientBase::handleConnect() + virtual void handleConnect( const ConnectionBase* connection ); + + protected: + // reimplemented from ClientBase + virtual void handleStartNode( const Tag* start ); + + private: + static void handleResolveReply( DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, + DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, + uint16_t port, uint16_t txtLen, const unsigned char *txtRecord, void *context ); + static void handleQueryReply( DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, + DNSServiceErrorType errorCode, const char *fullname, uint16_t rrtype, + uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, + void *context ); + + bool resolve( const std::string& serviceName, const std::string& regtype, const std::string& replyDomain ); + bool query( const std::string& hostname, int port ); + void handleQuery( const std::string& addr ); + void sendStart( const std::string& to ); + + DNSServiceRef m_qRef; + DNSServiceRef m_rRef; + DNSServiceRef m_currentRef; + + std::string m_to; + + int m_interface; + int m_port; + + bool m_streamSent; + + }; + + } + +} + +#endif // HAVE_MDNS + +#endif // LINKLOCALCLIENT_H__ diff --git a/libs/libgloox/linklocalhandler.h b/libs/libgloox/linklocalhandler.h new file mode 100644 index 0000000..c82684d --- /dev/null +++ b/libs/libgloox/linklocalhandler.h @@ -0,0 +1,66 @@ +/* + Copyright (c) 2012-2015 by Jakob Schröter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#ifndef LINKLOCALHANDLER_H__ +#define LINKLOCALHANDLER_H__ + +#ifdef HAVE_MDNS + +#include "linklocal.h" +#include "macros.h" +#include "gloox.h" + +#include + +namespace gloox +{ + + namespace LinkLocal + { + +// class Client; + + /** + * @brief A base class that gets informed about advertised or removed XMPP services on the local network. + * + * See @ref gloox::LinkLocal::Manager for more information on how to implement + * link-local messaging. + * + * @author Jakob Schröter + * @since 1.0.x + */ + class GLOOX_API Handler + { + public: + /** + * Reimplement this function to be notified about services available on (or removed from) + * the local network. + * @param services A list of services. + * @note Make a copy of the service list as the list will not be valid after the function + * returned. + */ + virtual void handleBrowseReply( const ServiceList& services ) = 0; + +// /** +// * +// */ +// virtual void handleClient( Client* client ) = 0; + + }; + + } + +} + +#endif // HAVE_MDNS + +#endif // LINKLOCALHANDLER_H__ diff --git a/libs/libgloox/linklocalmanager.cpp b/libs/libgloox/linklocalmanager.cpp new file mode 100644 index 0000000..353f332 --- /dev/null +++ b/libs/libgloox/linklocalmanager.cpp @@ -0,0 +1,255 @@ +/* + Copyright (c) 2012-2015 by Jakob Schröter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#include "linklocalmanager.h" + +#ifdef HAVE_MDNS + +#include "linklocalhandler.h" +#include "connectiontcpclient.h" +#include "jid.h" +#include "util.h" + +#include + +#if ( !defined( _WIN32 ) && !defined( _WIN32_WCE ) ) || defined( __SYMBIAN32__ ) +# include +# include +#endif + +#if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) +# include +#elif defined( _WIN32_WCE ) +# include +#endif + +#define LINKLOCAL_SERVICE_PORT 5562 + +const std::string LINKLOCAL_SERVICE_TYPE = "_presence._tcp"; + +namespace gloox +{ + + namespace LinkLocal + { + + Manager::Manager( const std::string& user, ConnectionHandler* connHandler, const LogSink &logInstance ) + : m_publishRef( 0 ), m_browseRef( 0 ), m_user( user ), m_interface( 0 ), m_port( 0 ), + m_logInstance( logInstance ), m_browseFd( 0 ), m_server( connHandler, m_logInstance, EmptyString, LINKLOCAL_SERVICE_PORT ), + m_linkLocalHandler( 0 ), m_connectionHandler( connHandler ) + { + + setPort( LINKLOCAL_SERVICE_PORT ); // does more than just setting m_port + addTXTData( "node", GLOOX_CAPS_NODE ); + } + + Manager::~Manager() + { + deregisterService(); + stopBrowsing(); + } + + void Manager::addTXTData( const std::string& key, const std::string& value ) + { + if( value.empty() || key.empty() || key == "txtvers" ) + return; + + m_txtData[key] = value; + } + + void Manager::removeTXTData( const std::string& key ) + { + m_txtData.erase( key ); + } + + void Manager::registerService() + { + if( m_publishRef ) + deregisterService(); + + m_server.connect(); + + std::string txtRecord; + txtRecord += (char)9; // length of mandatory txtvers=1 + txtRecord += "txtvers=1"; // this is here because it SHOULD be the first entry + StringMap::const_iterator it = m_txtData.begin(); + for( ; it != m_txtData.end(); ++it ) + { + txtRecord += (char)( (*it).first.length() + (*it).second.length() + 1 ); + txtRecord += (*it).first; + txtRecord += '='; + txtRecord += (*it).second; + } + + std::string service = m_user + "@"; + if( m_host.empty() ) + { + char host[65]; + gethostname( host, 65 ); + service += host; + } + else + service += m_host; + + /*DNSServiceErrorType e =*/ DNSServiceRegister( &m_publishRef, + 0, // flags + m_interface, // interface, 0 = any, -1 = local only + service.c_str(), // service name, 0 = local computer name + LINKLOCAL_SERVICE_TYPE.c_str(), // service type + m_domain.c_str(), // domain, 0 = default domain(s) + m_host.c_str(), // host, 0 = default host name(s) + htons( m_port ), // port + (short unsigned int)txtRecord.length(), // TXT record length + (const void*)txtRecord.c_str(), // TXT record + 0, // callback + 0 ); // context + } + + void Manager::deregisterService() + { + if( m_publishRef ) + { + DNSServiceRefDeallocate( m_publishRef ); + m_publishRef = 0; + } + } + + bool Manager::startBrowsing() + { + if( !m_linkLocalHandler ) + return false; + + if( m_browseRef ) + stopBrowsing(); + + DNSServiceErrorType e = DNSServiceBrowse( &m_browseRef, + 0, // flags, currently ignored + m_interface, // interface, 0 = any, -1 = local only + LINKLOCAL_SERVICE_TYPE.c_str(), // service type + m_domain.c_str(), // domain, 0 = default domain(s) + &handleBrowseReply, // callback + this ); // context + if ( e != kDNSServiceErr_NoError ) + return false; + + return true; + } + + void Manager::stopBrowsing() + { + if( m_browseRef ) + { + DNSServiceRefDeallocate( m_browseRef ); + m_browseRef = 0; + } + } + + void Manager::recv( int timeout ) + { + if( !m_browseRef ) + return; + + struct timeval tv; + + fd_set fds; + FD_ZERO( &fds ); + // the following causes a C4127 warning in VC++ Express 2008 and possibly other versions. + // however, the reason for the warning can't be fixed in gloox. + FD_SET( DNSServiceRefSockFD( m_browseRef ), &fds ); + + tv.tv_sec = timeout / 1000000; + tv.tv_usec = timeout % 1000000; + + if( select( FD_SETSIZE, &fds, 0, 0, timeout == -1 ? 0 : &tv ) > 0 ) + { + if( FD_ISSET( DNSServiceRefSockFD( m_browseRef ), &fds ) != 0 ) + DNSServiceProcessResult( m_browseRef ); + } + + m_server.recv( timeout ); + } + + + void Manager::handleBrowseReply( DNSServiceRef /*sdRef*/, DNSServiceFlags flags, uint32_t interfaceIndex, + DNSServiceErrorType errorCode, const char* serviceName, const char* regtype, + const char* replyDomain, void* context ) + { + if( !context || errorCode != kDNSServiceErr_NoError ) + return; + + Flag f = ( flags & kDNSServiceFlagsAdd ) == kDNSServiceFlagsAdd + ? AddService + : RemoveService; + + Manager* m = static_cast( context ); + m->handleBrowse( f, serviceName, regtype, replyDomain, interfaceIndex, ( flags & kDNSServiceFlagsMoreComing ) == kDNSServiceFlagsMoreComing ); + + } + + void Manager::handleBrowse( Flag flag, const std::string& service, const std::string& regtype, const std::string& domain, int iface, bool moreComing ) + { + Service s( flag, service, regtype, domain, interface ); + m_tmpServices.push_back( s ); + +// switch( flag ) +// { +// case AddService: +// { +// m_services.push_back( s ); +// break; +// } +// case RemoveService: +// { +// ServiceList::iterator it = m_services.begin(); +// for( ; it != m_services.end(); ++it ) +// { +// if( (*it)->service == service && (*it)->regtype == regtype && (*it)->domain == domain ) +// { +// m_services.erase( it ); +// } +// } +// break; +// } +// } + + if( !moreComing ) + { + m_linkLocalHandler->handleBrowseReply( m_tmpServices ); + m_tmpServices.clear(); + } + } + + +// const StringMap Manager::decodeTXT( const std::string& txt ) +// { +// StringMap result; +// if( txt.empty() ) +// return result; +// +// std::string::const_iterator it = txt.begin(); +// while( it < txt.end() ) +// { +// int len = (int)(*it); +// std::string tmp( ++it, it + len + 1 ); +// it += len; +// size_t pos = tmp.find( '=' ); +// result.insert( std::make_pair( tmp.substr( 0, pos ), tmp.substr( pos + 1 ) ) ); +// } +// +// return result; +// } + + } + +} + +#endif // HAVE_MDNS diff --git a/libs/libgloox/linklocalmanager.h b/libs/libgloox/linklocalmanager.h new file mode 100644 index 0000000..44af24e --- /dev/null +++ b/libs/libgloox/linklocalmanager.h @@ -0,0 +1,356 @@ +/* + Copyright (c) 2012-2015 by Jakob Schröter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + +#ifndef LINKLOCALMANAGER_H___ +#define LINKLOCALMANAGER_H___ + +#include "config.h" + +#ifdef HAVE_MDNS + +#include "linklocal.h" +#include "macros.h" +#include "gloox.h" +#include "util.h" +#include "logsink.h" +#include "connectiontcpserver.h" +#include "mutex.h" +#include "jid.h" + +#include + +#include + +namespace gloox +{ + + class ConnectionHandler; + class ConnectionTCPClient; + + namespace LinkLocal + { + + class Handler; + + /** + * @brief This is a manager for server-less messaging (@xep{0174}). + * + * Enable compilation of this code with the @c \-\-enable-mdns switch to @c configure, or add + * @c \#define @c HAVE_MDNS to your platform's @c config.h. @c dns_sd.h, @c libdns_sd.so, as well + * as the @c mdnsd daemon from Apple's bonjour distribution are required. The @c mdnsd daemon has + * to be running on the local host. + * + * ### Browsing the local network for XMPP services + * + * You can use the Manager to browse the local network for XMPP services. + * First, create a new instance, register a LinkLocal::Handler, and call startBrowsing(). + * @code + * m_mdns = new LinkLocal::Manager( ... ); + * m_mdns->registerLinkLocalHandler( yourHandler ); + * m_mdns->startBrowsing(); + * @endcode + * + * Then you will need to call @c recv() periodcally. The handler will then receive lists of available + * or removed services. Check the @c flag member of the Service struct. + * + * @code + * void MyClass::handleBrowseReply( const Service& service ) + * { + * LinkLocal::ServiceList::const_iterator it = services.begin(); + * for( ; it != services.end(); ++it ) + * { + * if( (*it).flag == LinkLocal::AddService ) + * { + * // new service + * } + * else + * { + * // service removed + * } + * } + * } + * @endcode + * + * @note Note that your own service may show up in the list, too. + * + * ### Connecting to an XMPP service + * + * Using the info from the Service struct you can initiate a connection to the remote entity. + * First, create a new instance of LinkLocal::Client and register some basic handlers like you + * would with a normal gloox::Client: + * + * @code + * LinkLocal::Client c( JID( "romeo@montague.net" ) ); + * c.logInstance().registerLogHandler( LogLevelDebug, LogAreaAll, this ); // optional + * c.registerConnectionListener( yourConnectionListener ); + * @endcode + * + * Then call @link gloox::LinkLocal::Client::connect( const std::string&, const std::string&, const std::string&, int ) connect() @endlink + * and pass the paramters from the Service struct that you received in handleBrowseReply(). + * + * @code + * c.connect( "juliet@laptop", "_presence._tcp", ".local", 4 ); // don't use literal values + * @endcode + * + * Put your LinkLocal::Client instance in your event loop (or in a separate thread) and call + * @link gloox::LinkLocal::Client::recv() recv() @endlink periodically. + * + * ### Advertising an XMPP service on the local network + * + * To advertise your own XMPP service you can (re-)use the same Manager instance from 'browsing the local network' + * above. + * + * You can publish some basic info about your service in a DNS TXT record. The Manager offers the addTXTData() function + * for that. See http://xmpp.org/registrar/linklocal.html for a list of official parameters. + * + * @code + * m_mdns->addTXTData("nick","July"); + * m_mdns->addTXTData("1st","Juliet"); + * m_mdns->addTXTData("last","Capulet"); + * m_mdns->addTXTData("msg","Hanging out"); + * m_mdns->addTXTData("jid","julia@capulet.com"); + * m_mdns->addTXTData("status","avail"); + * @endcode + * + * Then, to start publishing the availability of your service as well as the TXT record with the additional info + * you just call @c registerService(). + * + * @code + * m_mdns->registerService(); + * @endcode + * + * Other entities on the network will now be informed about the availability of your service. + * + * ### Listening for incoming connections + * + * The second argument to Manager's constructor is a ConnectionHandler-derived class that + * will receive incoming connections. + * + * When registerService() gets called, the Manager will also start a local server that will + * accept incoming connections. By default, it will listen on port 5562. + * + * In @link gloox::ConnectionHandler::handleIncomingConnection() handleIncomingConnection() @endlink + * you should create a new LinkLocal::Client and register some basic handlers: + * + * @code + * LinkLocal::Client c( JID( "romeo@desktop" ) ); + * c.logInstance().registerLogHandler( LogLevelDebug, LogAreaAll, this ); + * c.registerMessageHandler( this ); + * c.registerConnectionListener( this ); + * @endcode + * + * Finally you have to attach the incoming connection to the Client instance, and call connect(). + * + * @code + * connection->registerConnectionDataHandler( &c ); + * c.setConnectionImpl( connection ); + * c.connect(); + * @endcode + * + * Add the Client to your event loop to call recv() periodically. + * + * @see @c linklocal_example.cpp in @c src/examples/ for a (very) simple implementation of a bot + * handling both incoming and outgoing connections. + * + * @author Jakob Schröter + * @since 1.0.x + */ + class GLOOX_API Manager + { + + public: + + /** + * Creates a new Link-local Manager instance. You can call @c registerService() and/or @c startBrowsing() + * immediately on a new Manager object, it will use sane defaults. + * @param user The username to advertise, preferably (as per @xep{0174}) the locally + * logged-in user. This is just the local part of the local JID. + * @param connHandler A pointer to a ConnectionHandler that will receive incoming connections. + * @param logInstance The log target. Obtain it from ClientBase::logInstance(). + */ + Manager( const std::string& user, ConnectionHandler* connHandler, const LogSink &logInstance ); + + /** + * Virtual destructor. + * @note @c deregisterService() and @c stopBrowsing() will be called automatically if necessary. + */ + virtual ~Manager(); + + /** + * Lets you add additional data to the published TXT record. + * @note The @c txtvers=1 parameter is included by default and cannot be changed. + * @param key The key of a key=value parameter pair. Must be non-empty. If the given key + * has been set before, its value will be overwritten by the new value. + * @param value The value of a @c key=value parameter pair. Must be non-empty. + * @note If either parameter is empty, this function is a NOOP. + * @note The additional data will not be automatically published if you have already called + * @c registerService(). You will have to call @c registerService() again to update the + * TXT record. + */ + void addTXTData( const std::string& key, const std::string& value ); + + /** + * Lets you remove TXT record data by key. + * @note The @c txtvers=1 parameter is included by default and cannot be removed. + * @param key The key of the @c key=value parameter pair that should be removed. Must be non-empty. + * @note A published TXT record will not be automatically updated if you have already called + * @c registerService(). You will have to call @c registerService() again to update the TXT record. + */ + void removeTXTData( const std::string& key ); + + /** + * Starts advertising link-local messaging capabilities by publishing a number of DNS records, + * as per @xep{0174}. + * You can call this function again to publish any values you updated after the first call. + */ + void registerService(); + + /** + * Removes the published DNS records and thereby stops advertising link-local messaging + * capabilities. + */ + void deregisterService(); + + /** + * Lets you specify a new username. + * @param user The new username. + * @note The new username will not be automatically advertised if you have already called + * @c registerService(). You will have to call @c registerService() again to update the username. + */ + void setUser( const std::string& user ) { m_user = user; } + + /** + * Lets you specify an alternate host name to advertise. By default the local machine's hostname + * as returned by @c gethostname() will be used. + * @param host The hostname to use. + * @note The new hostname will not be automatically advertised if you have already called + * @c registerService(). You will have to call @c registerService() again to update the hostname. + */ + void setHost( const std::string& host ) { m_host = host; } + + /** + * This function lets you set an alternate browse domain. The default domain should work in most cases. + * @param domain The browse domain to set. + * @note The new domain will not be automatically used if you have already called + * @c registerService(). You will have to call @c registerService() again to update the domain. + * The same applies to @c startBrowsing(). + */ + void setDomain( const std::string& domain ) { m_domain = domain; } + + /** + * Lets you specifiy a port to listen on for incoming server-less XMPP connections. Default + * for this implementation is port 5562, but any unused port can be used. + * @note In addition to the SRV record, the port will also be published in the TXT record + * automaticlly, you do not have to add it manually. That is, you should not call + * @c addTXTData() with a key of @c "port.p2pj". + * @param port The port to use. + * @note The new port will not be automatically advertised if you have already called + * @c registerService(). You will have to call @c registerService() again to update the port. + */ + void setPort( const int port ) { m_port = port; addTXTData( "port.p2pj", util::int2string( m_port ) ); } + + /** + * This function can be used to set a non-default interface. Use @c if_nametoindex() to + * find the index for a specific named device. + * By default DNS records will be broadcast on all available interfaces, and all available + * interfaces will be used or browsing services. + * @param iface The interface to use. If you set an interface here, and want to change it + * back to 'any', use @b 0. Use @b -1 to broadcast only on the local machine. + * @note The new interface will not be automatically used if you have already called + * @c registerService(). You will have to call @c registerService() again to use the new + * interface. The same applies to @c startBrowsing(). + */ + void setInterface( unsigned iface ) { m_interface = iface; } + + /** + * Starts looking for other entities of type @c _presence. You have to set a handler for + * results using @c registerLinkLocalHandler() before calling this function. + * You can call this function again to re-start browsing with updated parameters. + * @return Returns @b true if browsing could be started successfully, @b false otherwise. + */ + bool startBrowsing(); + + /** + * Stops the browsing. + */ + void stopBrowsing(); + + /** + * Exposes the socket used for browsing so you can have it checked in your own event loop, + * if desired. If your event loop signals new data on the socket, you should NOT + * try to read from it directly. Instead you should call @c recv(). + * As an alternative to using the raw socket you could also put the Manager in a + * separate thread and call @c recv() repeatedly, or achieve this in any other way. + */ + int socket() const { return m_browseFd; } + + /** + * Checks once for new data on the socket used for browsing. + * @param timeout The timeout for @c select() in microseconds. Use @b -1 if blocking behavior + * is acceptable. + */ + void recv( int timeout ); + + /** + * Registers a handler that will be notfied about entities found on the network that match + * the given browse criteria. + * @param handler The handler to register. + */ + void registerLinkLocalHandler( Handler* handler ) { m_linkLocalHandler = handler; } + +// /** +// * +// */ +// static const StringMap decodeTXT( const std::string& txt ); + + private: + static void handleBrowseReply( DNSServiceRef sdRef, DNSServiceFlags flags, unsigned interfaceIndex, + DNSServiceErrorType errorCode, const char* serviceName, const char* regtype, + const char* replyDomain, void* context ); + + void handleBrowse( Flag flag, const std::string& service, const std::string& regtype, const std::string& domain, int iface, bool moreComing ); + + typedef std::list ConnectionList; + typedef std::map ConnectionMap; + + DNSServiceRef m_publishRef; + DNSServiceRef m_browseRef; + + ServiceList m_tmpServices; +// ServiceList m_services; + + std::string m_user; + std::string m_host; + std::string m_domain; + unsigned m_interface; + int m_port; + + const LogSink& m_logInstance; + + int m_browseFd; + + StringMap m_txtData; + + ConnectionTCPServer m_server; + + Handler* m_linkLocalHandler; + ConnectionHandler* m_connectionHandler; + + }; + + } + +} + +#endif // HAVE_MDNS + +#endif // LINKLOCALMANAGER_H___ diff --git a/libs/libgloox/mucinvitationhandler.cpp b/libs/libgloox/mucinvitationhandler.cpp new file mode 100644 index 0000000..d9416f9 --- /dev/null +++ b/libs/libgloox/mucinvitationhandler.cpp @@ -0,0 +1,27 @@ +/* + Copyright (c) 2006-2015 by Jakob Schröter + This file is part of the gloox library. http://camaya.net/gloox + + This software is distributed under a license. The full license + agreement can be found in the file LICENSE in this distribution. + This software may not be copied, modified, sold or distributed + other than expressed in the named license agreement. + + This software is distributed without any warranty. +*/ + + + +#include "mucinvitationhandler.h" +#include "mucroom.h" + +namespace gloox +{ + + MUCInvitationHandler::MUCInvitationHandler( ClientBase* parent ) + { + if( parent ) + parent->registerStanzaExtension( new MUCRoom::MUCUser() ); + } + +} diff --git a/libs/libgloox/version.rc b/libs/libgloox/version.rc new file mode 100644 index 0000000..ce7aaa8 --- /dev/null +++ b/libs/libgloox/version.rc @@ -0,0 +1,31 @@ +#include + + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,13,0 + PRODUCTVERSION 1,0,13,0 + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK + FILEFLAGS 0 + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL + BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "FileDescription", "gloox Jabber/XMPP library\0" + VALUE "ProductVersion", "1.0.13\0" + VALUE "FileVersion", "1.0.13\0" + VALUE "InternalName", "gloox\0" + VALUE "OriginalFilename", "gloox.dll\0" + VALUE "CompanyName", "Jakob Schroeter\0" + VALUE "LegalCopyright", "Copyright (C) 2004-2015 Jakob Schroeter\0" + VALUE "Licence", "GPLv3\0" + VALUE "Info", "http://camaya.net/gloox\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END + END