inbandbytestream.cpp
/*
Copyright (c) 2006-2023 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 "inbandbytestream.h"
#include "base64.h"
#include "bytestreamdatahandler.h"
#include "disco.h"
#include "clientbase.h"
#include "error.h"
#include "message.h"
#include "util.h"
#include <cstdlib>
namespace gloox
{
// ---- InBandBytestream::IBB ----
static const char* typeValues[] =
{
"open", "data", "close"
};
InBandBytestream::IBB::IBB( const std::string& sid, int blocksize )
: StanzaExtension( ExtIBB ), m_sid ( sid ), m_seq( 0 ), m_blockSize( blocksize ),
m_type( IBBOpen )
{
}
InBandBytestream::IBB::IBB( const std::string& sid, int seq, const std::string& data )
: StanzaExtension( ExtIBB ), m_sid ( sid ), m_seq( seq ), m_blockSize( 0 ),
m_data( data ), m_type( IBBData )
{
}
InBandBytestream::IBB::IBB( const std::string& sid )
: StanzaExtension( ExtIBB ), m_sid ( sid ), m_seq( 0 ), m_blockSize( 0 ),
m_type( IBBClose )
{
}
InBandBytestream::IBB::IBB( const Tag* tag )
: StanzaExtension( ExtIBB ), m_type( IBBInvalid )
{
if( !tag || tag->xmlns() != XMLNS_IBB )
return;
m_type = static_cast<IBBType>( util::lookup( tag->name(), typeValues ) );
m_blockSize = atoi( tag->findAttribute( "block-size" ).c_str() );
m_seq = atoi( tag->findAttribute( "seq" ).c_str() );
m_sid = tag->findAttribute( "sid" );
m_data = Base64::decode64( tag->cdata() );
}
InBandBytestream::IBB::~IBB()
{
}
const std::string& InBandBytestream::IBB::filterString() const
{
static const std::string filter = "/iq/open[@xmlns='" + XMLNS_IBB + "']"
"|/iq/data[@xmlns='" + XMLNS_IBB + "']"
"|/message/data[@xmlns='" + XMLNS_IBB + "']"
"|/iq/close[@xmlns='" + XMLNS_IBB + "']";
return filter;
}
Tag* InBandBytestream::IBB::tag() const
{
if( m_type == IBBInvalid )
return 0;
Tag* t = new Tag( util::lookup( m_type, typeValues ) );
t->setXmlns( XMLNS_IBB );
t->addAttribute( "sid", m_sid );
if( m_type == IBBData )
{
t->setCData( Base64::encode64( m_data ) );
t->addAttribute( "seq", m_seq );
}
else if( m_type == IBBOpen )
t->addAttribute( "block-size", m_blockSize );
return t;
}
// ---- ~InBandBytestream::IBB ----
// ---- InBandBytestream ----
InBandBytestream::InBandBytestream( ClientBase* clientbase, LogSink& logInstance, const JID& initiator,
const JID& target, const std::string& sid )
: Bytestream( Bytestream::IBB, logInstance, initiator, target, sid ),
m_clientbase( clientbase ), m_blockSize( 4096 ), m_sequence( -1 ), m_lastChunkReceived( -1 )
{
if( m_clientbase )
{
m_clientbase->registerStanzaExtension( new IBB() );
m_clientbase->registerIqHandler( this, ExtIBB );
m_clientbase->registerMessageHandler( this );
}
m_open = false;
}
InBandBytestream::~InBandBytestream()
{
m_handler = 0; // to prevent handleBytestreamClose() from being called in close()
if( m_open )
close();
if( m_clientbase )
{
m_clientbase->removeMessageHandler( this );
m_clientbase->removeIqHandler( this, ExtIBB );
m_clientbase->removeIDHandler( this );
}
}
bool InBandBytestream::connect()
{
if( !m_clientbase )
return false;
if( m_target == m_clientbase->jid() )
return true;
const std::string& id = m_clientbase->getID();
IQ iq( IQ::Set, m_target, id );
iq.addExtension( new IBB( m_sid, m_blockSize ) );
m_clientbase->send( iq, this, IBBOpen );
return true;
}
void InBandBytestream::handleIqID( const IQ& iq, int context )
{
switch( iq.subtype() )
{
case IQ::Result:
if( context == IBBOpen && m_handler )
{
m_handler->handleBytestreamOpen( this );
m_open = true;
}
else if( context == IBBData && m_handler )
{
m_handler->handleBytestreamDataAck( this );
}
break;
case IQ::Error:
closed();
break;
default:
break;
}
}
bool InBandBytestream::handleIq( const IQ& iq ) // data or open request, always 'set'
{
const IBB* i = iq.findExtension<IBB>( ExtIBB );
if( !i || !m_handler || iq.subtype() != IQ::Set || i->sid() != this->sid() )
return false;
if( !m_open )
{
if( i->type() == IBBOpen )
{
returnResult( iq.from(), iq.id() );
m_open = true;
m_handler->handleBytestreamOpen( this );
return true;
}
return false;
}
if( i->type() == IBBClose )
{
returnResult( iq.from(), iq.id() );
closed();
return true;
}
if( ++m_lastChunkReceived != i->seq() )
{
m_open = false;
returnError( iq.from(), iq.id(), StanzaErrorTypeModify, StanzaErrorItemNotFound );
return true;
}
if( m_lastChunkReceived == 65535 )
m_lastChunkReceived = -1;
if( i->data().empty() )
{
m_open = false;
returnError( iq.from(), iq.id(), StanzaErrorTypeModify, StanzaErrorBadRequest );
return true;
}
returnResult( iq.from(), iq.id() );
m_handler->handleBytestreamData( this, i->data() );
return true;
}
void InBandBytestream::handleMessage( const Message& msg, MessageSession* /*session*/ )
{
if( msg.from() != m_target || !m_handler )
return;
const IBB* i = msg.findExtension<IBB>( ExtIBB );
if( !i )
return;
if( !m_open )
return;
if( m_lastChunkReceived != i->seq() )
{
m_open = false;
return;
}
if( i->data().empty() )
{
m_open = false;
return;
}
m_handler->handleBytestreamData( this, i->data() );
m_lastChunkReceived++;
}
void InBandBytestream::returnResult( const JID& to, const std::string& id )
{
IQ iq( IQ::Result, to, id );
m_clientbase->send( iq );
}
void InBandBytestream::returnError( const JID& to, const std::string& id, StanzaErrorType type, StanzaError error )
{
IQ iq( IQ::Error, to, id );
iq.addExtension( new Error( type, error ) );
m_clientbase->send( iq );
}
bool InBandBytestream::send( const std::string& data )
{
if( !m_open || !m_clientbase )
return false;
size_t pos = 0;
size_t len = data.length();
do
{
const std::string& id = m_clientbase->getID();
IQ iq( IQ::Set, m_clientbase->jid() == m_target ? m_initiator : m_target, id );
iq.addExtension( new IBB( m_sid, ++m_sequence, data.substr( pos, m_blockSize ) ) );
m_clientbase->send( iq, this, IBBData );
pos += m_blockSize;
if( m_sequence == 65535 )
m_sequence = -1;
}
while( pos < len );
return true;
}
void InBandBytestream::closed()
{
if( !m_open )
return;
m_open = false;
if( m_handler )
m_handler->handleBytestreamClose( this );
}
void InBandBytestream::close()
{
m_open = false;
if( !m_clientbase )
return;
const std::string& id = m_clientbase->getID();
IQ iq( IQ::Set, m_target, id );
iq.addExtension( new IBB( m_sid ) );
m_clientbase->send( iq, this, IBBClose );
if( m_handler )
m_handler->handleBytestreamClose( this );
}
}
gloox::Bytestream::m_open
bool m_open
Definition bytestream.h:171
gloox::Bytestream::m_target
const JID m_target
Definition bytestream.h:162
gloox::Bytestream::m_sid
std::string m_sid
Definition bytestream.h:168
gloox::Bytestream::m_initiator
const JID m_initiator
Definition bytestream.h:159
gloox::Bytestream::m_handler
BytestreamDataHandler * m_handler
Definition bytestream.h:153
gloox::IQ
An abstraction of an IQ stanza.
Definition iq.h:34
gloox::IQ::subtype
IqType subtype() const
Definition iq.h:74
gloox::IQ::Set
@ Set
Definition iq.h:46
gloox::IQ::Error
@ Error
Definition iq.h:49
gloox::IQ::Result
@ Result
Definition iq.h:48
gloox::InBandBytestream::~InBandBytestream
virtual ~InBandBytestream()
Definition inbandbytestream.cpp:114
gloox::InBandBytestream::handleIqID
virtual void handleIqID(const IQ &iq, int context)
Definition inbandbytestream.cpp:144
gloox::InBandBytestream::handleIq
virtual bool handleIq(const IQ &iq)
Definition inbandbytestream.cpp:167
gloox::InBandBytestream::close
virtual void close()
Definition inbandbytestream.cpp:290
gloox::InBandBytestream::connect
virtual bool connect()
Definition inbandbytestream.cpp:129
gloox::InBandBytestream::send
bool send(const std::string &data)
Definition inbandbytestream.cpp:256
gloox::InBandBytestream::handleMessage
virtual void handleMessage(const Message &msg, MessageSession *session=0)
Definition inbandbytestream.cpp:215
gloox::JID
An abstraction of a JID.
Definition jid.h:31
gloox::MessageSession
An abstraction of a message session between any two entities.
Definition messagesession.h:146
gloox::Message
An abstraction of a message stanza.
Definition message.h:34
gloox::StanzaExtension
This class abstracts a stanza extension, which is usually an XML child element in a specific namespac...
Definition stanzaextension.h:204
gloox::Stanza::addExtension
void addExtension(const StanzaExtension *se)
Definition stanza.cpp:52
gloox::Stanza::id
const std::string & id() const
Definition stanza.h:63
gloox::Stanza::from
const JID & from() const
Definition stanza.h:51
gloox::Stanza::findExtension
const StanzaExtension * findExtension(int type) const
Definition stanza.cpp:57
gloox
The namespace for the gloox library.
Definition adhoc.cpp:28
gloox::XMLNS_IBB
const std::string XMLNS_IBB
Definition gloox.cpp:32
gloox::StanzaErrorItemNotFound
@ StanzaErrorItemNotFound
Definition gloox.h:893
gloox::StanzaErrorBadRequest
@ StanzaErrorBadRequest
Definition gloox.h:874
gloox::StanzaErrorTypeModify
@ StanzaErrorTypeModify
Definition gloox.h:861
gloox::ExtIBB
@ ExtIBB
Definition stanzaextension.h:67