inbandbytestream.cpp

00001 /*
00002   Copyright (c) 2006-2009 by Jakob Schroeter <js@camaya.net>
00003   This file is part of the gloox library. http://camaya.net/gloox
00004 
00005   This software is distributed under a license. The full license
00006   agreement can be found in the file LICENSE in this distribution.
00007   This software may not be copied, modified, sold or distributed
00008   other than expressed in the named license agreement.
00009 
00010   This software is distributed without any warranty.
00011 */
00012 
00013 
00014 #include "inbandbytestream.h"
00015 #include "base64.h"
00016 #include "bytestreamdatahandler.h"
00017 #include "disco.h"
00018 #include "clientbase.h"
00019 #include "error.h"
00020 #include "message.h"
00021 #include "util.h"
00022 
00023 #include <cstdlib>
00024 
00025 namespace gloox
00026 {
00027 
00028   // ---- InBandBytestream::IBB ----
00029   static const char* typeValues[] =
00030   {
00031     "open", "data", "close"
00032   };
00033 
00034   InBandBytestream::IBB::IBB( const std::string& sid, int blocksize )
00035     : StanzaExtension( ExtIBB ), m_sid ( sid ), m_seq( 0 ), m_blockSize( blocksize ),
00036       m_type( IBBOpen )
00037   {
00038   }
00039 
00040   InBandBytestream::IBB::IBB( const std::string& sid, int seq, const std::string& data )
00041     : StanzaExtension( ExtIBB ), m_sid ( sid ), m_seq( seq ), m_blockSize( 0 ),
00042       m_data( data ), m_type( IBBData )
00043   {
00044   }
00045 
00046   InBandBytestream::IBB::IBB( const std::string& sid )
00047     : StanzaExtension( ExtIBB ), m_sid ( sid ), m_seq( 0 ), m_blockSize( 0 ),
00048       m_type( IBBClose )
00049   {
00050   }
00051 
00052   InBandBytestream::IBB::IBB( const Tag* tag )
00053     : StanzaExtension( ExtIBB ), m_type( IBBInvalid )
00054   {
00055     if( !tag || tag->xmlns() != XMLNS_IBB )
00056       return;
00057 
00058     m_type = (IBBType)util::lookup( tag->name(), typeValues );
00059     m_blockSize = atoi( tag->findAttribute( "block-size" ).c_str() );
00060     m_seq = atoi( tag->findAttribute( "seq" ).c_str() );
00061     m_sid = tag->findAttribute( "sid" );
00062     m_data = Base64::decode64( tag->cdata() );
00063   }
00064 
00065   InBandBytestream::IBB::~IBB()
00066   {
00067   }
00068 
00069   const std::string& InBandBytestream::IBB::filterString() const
00070   {
00071     static const std::string filter = "/iq/open[@xmlns='" + XMLNS_IBB + "']"
00072                                       "|/iq/data[@xmlns='" + XMLNS_IBB + "']"
00073                                       "|/message/data[@xmlns='" + XMLNS_IBB + "']"
00074                                       "|/iq/close[@xmlns='" + XMLNS_IBB + "']";
00075     return filter;
00076   }
00077 
00078   Tag* InBandBytestream::IBB::tag() const
00079   {
00080     if( m_type == IBBInvalid )
00081       return 0;
00082 
00083     Tag* t = new Tag( util::lookup( m_type, typeValues ) );
00084     t->setXmlns( XMLNS_IBB );
00085     t->addAttribute( "sid", m_sid );
00086     if( m_type == IBBData )
00087     {
00088       t->setCData( Base64::encode64( m_data ) );
00089       t->addAttribute( "seq", m_seq );
00090     }
00091     else if( m_type == IBBOpen )
00092       t->addAttribute( "block-size", m_blockSize );
00093 
00094     return t;
00095   }
00096   // ---- ~InBandBytestream::IBB ----
00097 
00098   // ---- InBandBytestream ----
00099   InBandBytestream::InBandBytestream( ClientBase* clientbase, LogSink& logInstance, const JID& initiator,
00100                                       const JID& target, const std::string& sid )
00101     : Bytestream( Bytestream::IBB, logInstance, initiator, target, sid ),
00102       m_clientbase( clientbase ), m_blockSize( 4096 ), m_sequence( -1 ), m_lastChunkReceived( -1 )
00103   {
00104     if( m_clientbase )
00105     {
00106       m_clientbase->registerStanzaExtension( new IBB() );
00107       m_clientbase->registerIqHandler( this, ExtIBB );
00108       m_clientbase->registerMessageHandler( this );
00109     }
00110 
00111     m_open = false;
00112   }
00113 
00114   InBandBytestream::~InBandBytestream()
00115   {
00116     if( m_open )
00117       close();
00118 
00119     if( m_clientbase )
00120     {
00121       m_clientbase->removeMessageHandler( this );
00122       m_clientbase->removeIqHandler( this, ExtIBB );
00123       m_clientbase->removeIDHandler( this );
00124     }
00125   }
00126 
00127   bool InBandBytestream::connect()
00128   {
00129     if( !m_clientbase )
00130       return false;
00131 
00132     if( m_target == m_clientbase->jid() )
00133       return true;
00134 
00135     const std::string& id = m_clientbase->getID();
00136     IQ iq( IQ::Set, m_target, id );
00137     iq.addExtension( new IBB( m_sid, m_blockSize ) );
00138     m_clientbase->send( iq, this, IBBOpen );
00139     return true;
00140   }
00141 
00142   void InBandBytestream::handleIqID( const IQ& iq, int context )
00143   {
00144     switch( iq.subtype() )
00145     {
00146       case IQ::Result:
00147         if( context == IBBOpen && m_handler )
00148         {
00149           m_handler->handleBytestreamOpen( this );
00150           m_open = true;
00151         }
00152         break;
00153       case IQ::Error:
00154         closed();
00155         break;
00156       default:
00157         break;
00158     }
00159   }
00160 
00161   bool InBandBytestream::handleIq( const IQ& iq ) // data or open request, always 'set'
00162   {
00163     const IBB* i = iq.findExtension<IBB>( ExtIBB );
00164     if( !i || !m_handler || iq.subtype() != IQ::Set )
00165       return false;
00166 
00167     if( !m_open )
00168     {
00169       if( i->type() == IBBOpen )
00170       {
00171         returnResult( iq.from(), iq.id() );
00172         m_open = true;
00173         m_handler->handleBytestreamOpen( this );
00174         return true;
00175       }
00176       return false;
00177     }
00178 
00179     if( i->type() == IBBClose )
00180     {
00181       returnResult( iq.from(), iq.id() );
00182       closed();
00183       return true;
00184     }
00185 
00186     if( ( m_lastChunkReceived + 1 ) != i->seq() )
00187     {
00188       m_open = false;
00189       returnError( iq.from(), iq.id(), StanzaErrorTypeModify, StanzaErrorItemNotFound );
00190       return false;
00191     }
00192 
00193     if( i->data().empty() )
00194     {
00195       m_open = false;
00196       returnError( iq.from(), iq.id(), StanzaErrorTypeModify, StanzaErrorBadRequest );
00197       return false;
00198     }
00199 
00200     returnResult( iq.from(), iq.id() );
00201     m_handler->handleBytestreamData( this, i->data() );
00202     m_lastChunkReceived++;
00203     return true;
00204   }
00205 
00206   void InBandBytestream::handleMessage( const Message& msg, MessageSession* /*session*/ )
00207   {
00208     if( msg.from() != m_target || !m_handler )
00209       return;
00210 
00211     const IBB* i = msg.findExtension<IBB>( ExtIBB );
00212     if( !i )
00213       return;
00214 
00215     if( !m_open )
00216       return;
00217 
00218     if( m_lastChunkReceived != i->seq() )
00219     {
00220       m_open = false;
00221       return;
00222     }
00223 
00224     if( i->data().empty() )
00225     {
00226       m_open = false;
00227       return;
00228     }
00229 
00230     m_handler->handleBytestreamData( this, i->data() );
00231     m_lastChunkReceived++;
00232   }
00233 
00234   void InBandBytestream::returnResult( const JID& to, const std::string& id )
00235   {
00236     IQ iq( IQ::Result, to, id );
00237     m_clientbase->send( iq );
00238   }
00239 
00240   void InBandBytestream::returnError( const JID& to, const std::string& id, StanzaErrorType type, StanzaError error )
00241   {
00242     IQ iq( IQ::Error, to, id );
00243     iq.addExtension( new Error( type, error ) );
00244     m_clientbase->send( iq );
00245   }
00246 
00247   bool InBandBytestream::send( const std::string& data )
00248   {
00249     if( !m_open || !m_clientbase )
00250       return false;
00251 
00252     size_t pos = 0;
00253     size_t len = data.length();
00254     do
00255     {
00256       const std::string& id = m_clientbase->getID();
00257       IQ iq( IQ::Set, m_target, id );
00258       iq.addExtension( new IBB( m_sid, ++m_sequence, data.substr( pos, m_blockSize ) ) );
00259       m_clientbase->send( iq, this, IBBData );
00260 
00261       pos += m_blockSize;
00262       if( m_sequence == 65535 )
00263         m_sequence = -1;
00264     }
00265     while( pos < len );
00266 
00267     return true;
00268   }
00269 
00270   void InBandBytestream::closed()
00271   {
00272     if( !m_open )
00273       return;
00274 
00275     m_open = false;
00276 
00277     if( m_handler )
00278       m_handler->handleBytestreamClose( this );
00279   }
00280 
00281   void InBandBytestream::close()
00282   {
00283     m_open = false;
00284 
00285     if( !m_clientbase )
00286       return;
00287 
00288     const std::string& id = m_clientbase->getID();
00289     IQ iq( IQ::Set, m_target, id );
00290     iq.addExtension( new IBB( m_sid ) );
00291     m_clientbase->send( iq, this, IBBClose );
00292 
00293     if( m_handler )
00294       m_handler->handleBytestreamClose( this );
00295   }
00296 
00297 }
Generated on Tue Jun 8 23:37:54 2010 for gloox by  doxygen 1.6.3