Add remaining part of libgloox.

This commit is contained in:
Barrett17 2015-06-24 16:08:12 +00:00
parent 05633562d6
commit f487e366fb
31 changed files with 4799 additions and 0 deletions

View File

@ -0,0 +1,61 @@
/*
Copyright (c) 2015 by Jakob Schröter <js@camaya.net>
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 <js@camaya.net>
* @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__

View File

@ -0,0 +1,93 @@
/*
Copyright (c) 2007-2015 by Jakob Schröter <js@camaya.net>
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 <windows.h>
#elif defined( __APPLE__ )
# include <libkern/OSAtomic.h>
#elif defined( HAVE_GCC_ATOMIC_BUILTINS )
// Use intrinsic functions - no #include required.
#else
# include "mutexguard.h"
#endif
#ifdef _WIN32_WCE
# include <winbase.h>
#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
}
}
}

View File

@ -0,0 +1,75 @@
/*
Copyright (c) 2007-2015 by Jakob Schröter <js@camaya.net>
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 <js@camaya.net>
* @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__

103
libs/libgloox/carbons.cpp Normal file
View File

@ -0,0 +1,103 @@
/*
* Copyright (c) 2013-2015 by Jakob Schröter <js@camaya.net>
* 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
}
}

185
libs/libgloox/carbons.h Normal file
View File

@ -0,0 +1,185 @@
/*
Copyright (c) 2013-2015 by Jakob Schröter <js@camaya.net>
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<const Carbons>( ExtCarbons );
* if( carbon && carbon->embeddedStanza() )
* {
* Message* embeddedMessage = static_cast<Message *>( 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 <js@camaya.net>
* @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__

85
libs/libgloox/forward.cpp Normal file
View File

@ -0,0 +1,85 @@
/*
Copyright (c) 2013-2015 by Jakob Schröter <js@camaya.net>
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<DelayedDelivery*>( m_delay->clone() ) );
}
}

104
libs/libgloox/forward.h Normal file
View File

@ -0,0 +1,104 @@
/*
Copyright (c) 2013-2015 by Jakob Schröter <js@camaya.net>
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 <string>
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 (&lt;message&gt;, &lt;iq&gt;,
* or &lt;presence&gt;).
*
* XEP-Version: 0.5
*
* @author Jakob Schröter <js@camaya.net>
* @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__

226
libs/libgloox/iodata.cpp Normal file
View File

@ -0,0 +1,226 @@
/*
Copyright (c) 2015 by Jakob Schröter <js@camaya.net>
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 );
}
}

186
libs/libgloox/iodata.h Normal file
View File

@ -0,0 +1,186 @@
/*
Copyright (c) 2015 by Jakob Schröter <js@camaya.net>
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 <string>
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 <js@camaya.net>
* @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 &lt;iodata&gt; 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 &lt;in&gt;.
* @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 &lt;out&gt;.
* @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 &lt;error&gt;.
* @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__

View File

@ -0,0 +1,104 @@
/*
Copyright (c) 2008-2015 by Jakob Schröter <js@camaya.net>
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;
}
}
}

View File

@ -0,0 +1,138 @@
/*
Copyright (c) 2008-2015 by Jakob Schröter <js@camaya.net>
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 <string>
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 <js@camaya.net>
* @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__

View File

@ -0,0 +1,173 @@
/*
Copyright (c) 2013-2015 by Jakob Schröter <js@camaya.net>
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 <cstdlib>
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;
}
}
}

View File

@ -0,0 +1,134 @@
/*
Copyright (c) 2013-2015 by Jakob Schröter <js@camaya.net>
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 <string>
#include <list>
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 <js@camaya.net>
* @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<File> 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__

View File

@ -0,0 +1,113 @@
/*
Copyright (c) 2013-2015 by Jakob Schröter <js@camaya.net>
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;
}
}
}

View File

@ -0,0 +1,141 @@
/*
Copyright (c) 2013-2015 by Jakob Schröter <js@camaya.net>
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 <string>
#include <list>
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 <js@camaya.net>
* @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<Candidate> 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__

View File

@ -0,0 +1,176 @@
/*
Copyright (c) 2008-2015 by Jakob Schröter <js@camaya.net>
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 <string>
#include <list>
namespace gloox
{
class Tag;
namespace Jingle
{
class Plugin;
class PluginFactory;
enum JinglePluginType
{
PluginNone, /**< Invalid plugin type. */
PluginContent, /**< A plugin abstracting a &lt;content&gt; 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<const Plugin*> 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 <js@camaya.net>
* @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<MyPlugin>( 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<const T*>( 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__

View File

@ -0,0 +1,91 @@
/*
Copyright (c) 2013-2015 by Jakob Schröter <js@camaya.net>
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 );
}
}
}
}
}
}

View File

@ -0,0 +1,82 @@
/*
Copyright (c) 2013-2015 by Jakob Schröter <js@camaya.net>
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 <js@camaya.net>
* @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__

View File

@ -0,0 +1,501 @@
/*
Copyright (c) 2007-2015 by Jakob Schröter <js@camaya.net>
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<Jingle>( 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<Error>( 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;
}
}
}
}
}

View File

@ -0,0 +1,575 @@
/*
Copyright (c) 2007-2015 by Jakob Schröter <js@camaya.net>
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 <string>
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 <js@camaya.net>
* @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 <js@camaya.net>
* @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 <js@camaya.net>
* @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
* &lt;text&gt; 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}) &lt;jingle&gt; element as a StanzaExtension.
*
* XEP Version: 1.1
* @author Jakob Schröter <js@camaya.net>
* @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 &lt;jingle&gt;
* 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 &lt;jingle&gt;
* 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__

View File

@ -0,0 +1,74 @@
/*
Copyright (c) 2008-2015 by Jakob Schröter <js@camaya.net>
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 <js@camaya.net>
* @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__

View File

@ -0,0 +1,104 @@
/*
Copyright (c) 2013-2015 by Jakob Schröter <js@camaya.net>
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<Session::Jingle>( ExtJingle );
if( !j )
return false;
m_factory.addPlugins( const_cast<Session::Jingle&>( *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;
}
}
}

View File

@ -0,0 +1,117 @@
/*
Copyright (c) 2013-2015 by Jakob Schröter <js@camaya.net>
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 <list>
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 <js@camaya.net>
* @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<Jingle::Session*> SessionList;
SessionList m_sessions;
ClientBase* m_parent;
SessionHandler* m_handler;
PluginFactory m_factory;
};
}
}
#endif // JINGLESESSIONMANAGER_H__

79
libs/libgloox/linklocal.h Normal file
View File

@ -0,0 +1,79 @@
/*
Copyright (c) 2012-2015 by Jakob Schröter <js@camaya.net>
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 <string>
#include <list>
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 <js@camaya.net>
* @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<Service> ServiceList;
}
}
#endif // HAVE_MDNS
#endif // LINKLOCAL_H__

View File

@ -0,0 +1,219 @@
/*
Copyright (c) 2012-2015 by Jakob Schröter <js@camaya.net>
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 <cstdio>
#if ( !defined( _WIN32 ) && !defined( _WIN32_WCE ) ) || defined( __SYMBIAN32__ )
# include <arpa/inet.h>
#endif
#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
# include <winsock.h>
#elif defined( _WIN32_WCE )
# include <winsock2.h>
#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<Client*>( 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<const unsigned char*>( 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<Client*>( 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<ConnectionBase*>( 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 = "<?xml version='1.0' encoding='UTF-8'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' to='";
s += to;
s += "' from='";
s += m_jid.full().c_str();
s += "' version='1.0'>";
send( s );
}
}
}
#endif // HAVE_MDNS

View File

@ -0,0 +1,125 @@
/*
Copyright (c) 2012-2015 by Jakob Schröter <js@camaya.net>
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 <string>
#include <dns_sd.h>
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 <js@camaya.net>
* @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__

View File

@ -0,0 +1,66 @@
/*
Copyright (c) 2012-2015 by Jakob Schröter <js@camaya.net>
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 <string>
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 <js@camaya.net>
* @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__

View File

@ -0,0 +1,255 @@
/*
Copyright (c) 2012-2015 by Jakob Schröter <js@camaya.net>
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 <cstdio>
#if ( !defined( _WIN32 ) && !defined( _WIN32_WCE ) ) || defined( __SYMBIAN32__ )
# include <unistd.h>
# include <arpa/inet.h>
#endif
#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
# include <winsock.h>
#elif defined( _WIN32_WCE )
# include <winsock2.h>
#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<Manager*>( 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

View File

@ -0,0 +1,356 @@
/*
Copyright (c) 2012-2015 by Jakob Schröter <js@camaya.net>
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 <string>
#include <dns_sd.h>
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 <js@camaya.net>
* @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<ConnectionTCPClient*> ConnectionList;
typedef std::map<ConnectionTCPClient*, const JID> 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___

View File

@ -0,0 +1,27 @@
/*
Copyright (c) 2006-2015 by Jakob Schröter <js@camaya.net>
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() );
}
}

31
libs/libgloox/version.rc Normal file
View File

@ -0,0 +1,31 @@
#include <winver.h>
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