gloox  1.0.28
inbandbytestream.cpp
1 /*
2  Copyright (c) 2006-2023 by Jakob Schröter <js@camaya.net>
3  This file is part of the gloox library. http://camaya.net/gloox
4 
5  This software is distributed under a license. The full license
6  agreement can be found in the file LICENSE in this distribution.
7  This software may not be copied, modified, sold or distributed
8  other than expressed in the named license agreement.
9 
10  This software is distributed without any warranty.
11 */
12 
13 
14 #include "inbandbytestream.h"
15 #include "base64.h"
16 #include "bytestreamdatahandler.h"
17 #include "disco.h"
18 #include "clientbase.h"
19 #include "error.h"
20 #include "message.h"
21 #include "util.h"
22 
23 #include <cstdlib>
24 
25 namespace gloox
26 {
27 
28  // ---- InBandBytestream::IBB ----
29  static const char* typeValues[] =
30  {
31  "open", "data", "close"
32  };
33 
34  InBandBytestream::IBB::IBB( const std::string& sid, int blocksize )
35  : StanzaExtension( ExtIBB ), m_sid ( sid ), m_seq( 0 ), m_blockSize( blocksize ),
36  m_type( IBBOpen )
37  {
38  }
39 
40  InBandBytestream::IBB::IBB( const std::string& sid, int seq, const std::string& data )
41  : StanzaExtension( ExtIBB ), m_sid ( sid ), m_seq( seq ), m_blockSize( 0 ),
42  m_data( data ), m_type( IBBData )
43  {
44  }
45 
46  InBandBytestream::IBB::IBB( const std::string& sid )
47  : StanzaExtension( ExtIBB ), m_sid ( sid ), m_seq( 0 ), m_blockSize( 0 ),
48  m_type( IBBClose )
49  {
50  }
51 
52  InBandBytestream::IBB::IBB( const Tag* tag )
53  : StanzaExtension( ExtIBB ), m_type( IBBInvalid )
54  {
55  if( !tag || tag->xmlns() != XMLNS_IBB )
56  return;
57 
58  m_type = static_cast<IBBType>( util::lookup( tag->name(), typeValues ) );
59  m_blockSize = atoi( tag->findAttribute( "block-size" ).c_str() );
60  m_seq = atoi( tag->findAttribute( "seq" ).c_str() );
61  m_sid = tag->findAttribute( "sid" );
62  m_data = Base64::decode64( tag->cdata() );
63  }
64 
65  InBandBytestream::IBB::~IBB()
66  {
67  }
68 
69  const std::string& InBandBytestream::IBB::filterString() const
70  {
71  static const std::string filter = "/iq/open[@xmlns='" + XMLNS_IBB + "']"
72  "|/iq/data[@xmlns='" + XMLNS_IBB + "']"
73  "|/message/data[@xmlns='" + XMLNS_IBB + "']"
74  "|/iq/close[@xmlns='" + XMLNS_IBB + "']";
75  return filter;
76  }
77 
78  Tag* InBandBytestream::IBB::tag() const
79  {
80  if( m_type == IBBInvalid )
81  return 0;
82 
83  Tag* t = new Tag( util::lookup( m_type, typeValues ) );
84  t->setXmlns( XMLNS_IBB );
85  t->addAttribute( "sid", m_sid );
86  if( m_type == IBBData )
87  {
88  t->setCData( Base64::encode64( m_data ) );
89  t->addAttribute( "seq", m_seq );
90  }
91  else if( m_type == IBBOpen )
92  t->addAttribute( "block-size", m_blockSize );
93 
94  return t;
95  }
96  // ---- ~InBandBytestream::IBB ----
97 
98  // ---- InBandBytestream ----
99  InBandBytestream::InBandBytestream( ClientBase* clientbase, LogSink& logInstance, const JID& initiator,
100  const JID& target, const std::string& sid )
101  : Bytestream( Bytestream::IBB, logInstance, initiator, target, sid ),
102  m_clientbase( clientbase ), m_blockSize( 4096 ), m_sequence( -1 ), m_lastChunkReceived( -1 )
103  {
104  if( m_clientbase )
105  {
106  m_clientbase->registerStanzaExtension( new IBB() );
107  m_clientbase->registerIqHandler( this, ExtIBB );
108  m_clientbase->registerMessageHandler( this );
109  }
110 
111  m_open = false;
112  }
113 
114  InBandBytestream::~InBandBytestream()
115  {
116  m_handler = 0; // to prevent handleBytestreamClose() from being called in close()
117 
118  if( m_open )
119  close();
120 
121  if( m_clientbase )
122  {
123  m_clientbase->removeMessageHandler( this );
124  m_clientbase->removeIqHandler( this, ExtIBB );
125  m_clientbase->removeIDHandler( this );
126  }
127  }
128 
129  bool InBandBytestream::connect()
130  {
131  if( !m_clientbase )
132  return false;
133 
134  if( m_target == m_clientbase->jid() )
135  return true;
136 
137  const std::string& id = m_clientbase->getID();
138  IQ iq( IQ::Set, m_target, id );
139  iq.addExtension( new IBB( m_sid, m_blockSize ) );
140  m_clientbase->send( iq, this, IBBOpen );
141  return true;
142  }
143 
144  void InBandBytestream::handleIqID( const IQ& iq, int context )
145  {
146  switch( iq.subtype() )
147  {
148  case IQ::Result:
149  if( context == IBBOpen && m_handler )
150  {
151  m_handler->handleBytestreamOpen( this );
152  m_open = true;
153  }
154  else if( context == IBBData && m_handler )
155  {
156  m_handler->handleBytestreamDataAck( this );
157  }
158  break;
159  case IQ::Error:
160  closed();
161  break;
162  default:
163  break;
164  }
165  }
166 
167  bool InBandBytestream::handleIq( const IQ& iq ) // data or open request, always 'set'
168  {
169  const IBB* i = iq.findExtension<IBB>( ExtIBB );
170  if( !i || !m_handler || iq.subtype() != IQ::Set || i->sid() != this->sid() )
171  return false;
172 
173  if( !m_open )
174  {
175  if( i->type() == IBBOpen )
176  {
177  returnResult( iq.from(), iq.id() );
178  m_open = true;
179  m_handler->handleBytestreamOpen( this );
180  return true;
181  }
182  return false;
183  }
184 
185  if( i->type() == IBBClose )
186  {
187  returnResult( iq.from(), iq.id() );
188  closed();
189  return true;
190  }
191 
192  if( ++m_lastChunkReceived != i->seq() )
193  {
194  m_open = false;
195  returnError( iq.from(), iq.id(), StanzaErrorTypeModify, StanzaErrorItemNotFound );
196  return true;
197  }
198 
199  if( m_lastChunkReceived == 65535 )
200  m_lastChunkReceived = -1;
201 
202  if( i->data().empty() )
203  {
204  m_open = false;
205  returnError( iq.from(), iq.id(), StanzaErrorTypeModify, StanzaErrorBadRequest );
206  return true;
207  }
208 
209  returnResult( iq.from(), iq.id() );
210  m_handler->handleBytestreamData( this, i->data() );
211 
212  return true;
213  }
214 
215  void InBandBytestream::handleMessage( const Message& msg, MessageSession* /*session*/ )
216  {
217  if( msg.from() != m_target || !m_handler )
218  return;
219 
220  const IBB* i = msg.findExtension<IBB>( ExtIBB );
221  if( !i )
222  return;
223 
224  if( !m_open )
225  return;
226 
227  if( m_lastChunkReceived != i->seq() )
228  {
229  m_open = false;
230  return;
231  }
232 
233  if( i->data().empty() )
234  {
235  m_open = false;
236  return;
237  }
238 
239  m_handler->handleBytestreamData( this, i->data() );
240  m_lastChunkReceived++;
241  }
242 
243  void InBandBytestream::returnResult( const JID& to, const std::string& id )
244  {
245  IQ iq( IQ::Result, to, id );
246  m_clientbase->send( iq );
247  }
248 
249  void InBandBytestream::returnError( const JID& to, const std::string& id, StanzaErrorType type, StanzaError error )
250  {
251  IQ iq( IQ::Error, to, id );
252  iq.addExtension( new Error( type, error ) );
253  m_clientbase->send( iq );
254  }
255 
256  bool InBandBytestream::send( const std::string& data )
257  {
258  if( !m_open || !m_clientbase )
259  return false;
260 
261  size_t pos = 0;
262  size_t len = data.length();
263  do
264  {
265  const std::string& id = m_clientbase->getID();
266  IQ iq( IQ::Set, m_clientbase->jid() == m_target ? m_initiator : m_target, id );
267  iq.addExtension( new IBB( m_sid, ++m_sequence, data.substr( pos, m_blockSize ) ) );
268  m_clientbase->send( iq, this, IBBData );
269 
270  pos += m_blockSize;
271  if( m_sequence == 65535 )
272  m_sequence = -1;
273  }
274  while( pos < len );
275 
276  return true;
277  }
278 
279  void InBandBytestream::closed()
280  {
281  if( !m_open )
282  return;
283 
284  m_open = false;
285 
286  if( m_handler )
287  m_handler->handleBytestreamClose( this );
288  }
289 
290  void InBandBytestream::close()
291  {
292  m_open = false;
293 
294  if( !m_clientbase )
295  return;
296 
297  const std::string& id = m_clientbase->getID();
298  IQ iq( IQ::Set, m_target, id );
299  iq.addExtension( new IBB( m_sid ) );
300  m_clientbase->send( iq, this, IBBClose );
301 
302  if( m_handler )
303  m_handler->handleBytestreamClose( this );
304  }
305 
306 }
An abstraction of an IQ stanza.
Definition: iq.h:34
IqType subtype() const
Definition: iq.h:74
An abstraction of a JID.
Definition: jid.h:31
An abstraction of a message session between any two entities.
An abstraction of a message stanza.
Definition: message.h:34
void addExtension(const StanzaExtension *se)
Definition: stanza.cpp:52
const std::string & id() const
Definition: stanza.h:63
const JID & from() const
Definition: stanza.h:51
const StanzaExtension * findExtension(int type) const
Definition: stanza.cpp:57
The namespace for the gloox library.
Definition: adhoc.cpp:28
const std::string XMLNS_IBB
Definition: gloox.cpp:32
@ StanzaErrorItemNotFound
Definition: gloox.h:893
@ StanzaErrorBadRequest
Definition: gloox.h:874
@ StanzaErrorTypeModify
Definition: gloox.h:861