gloox  1.0.1
inbandbytestream.cpp
1 /*
2  Copyright (c) 2006-2012 by Jakob Schroeter <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 = (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 
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 
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  {
152  m_open = true;
153  }
154  break;
155  case IQ::Error:
156  closed();
157  break;
158  default:
159  break;
160  }
161  }
162 
163  bool InBandBytestream::handleIq( const IQ& iq ) // data or open request, always 'set'
164  {
165  const IBB* i = iq.findExtension<IBB>( ExtIBB );
166  if( !i || !m_handler || iq.subtype() != IQ::Set || i->sid() != this->sid() )
167  return false;
168 
169  if( !m_open )
170  {
171  if( i->type() == IBBOpen )
172  {
173  returnResult( iq.from(), iq.id() );
174  m_open = true;
176  return true;
177  }
178  return false;
179  }
180 
181  if( i->type() == IBBClose )
182  {
183  returnResult( iq.from(), iq.id() );
184  closed();
185  return true;
186  }
187 
188  if( ( m_lastChunkReceived + 1 ) != i->seq() )
189  {
190  m_open = false;
191  returnError( iq.from(), iq.id(), StanzaErrorTypeModify, StanzaErrorItemNotFound );
192  return false;
193  }
194 
195  if( i->data().empty() )
196  {
197  m_open = false;
198  returnError( iq.from(), iq.id(), StanzaErrorTypeModify, StanzaErrorBadRequest );
199  return false;
200  }
201 
202  returnResult( iq.from(), iq.id() );
203  m_handler->handleBytestreamData( this, i->data() );
204  m_lastChunkReceived++;
205  return true;
206  }
207 
208  void InBandBytestream::handleMessage( const Message& msg, MessageSession* /*session*/ )
209  {
210  if( msg.from() != m_target || !m_handler )
211  return;
212 
213  const IBB* i = msg.findExtension<IBB>( ExtIBB );
214  if( !i )
215  return;
216 
217  if( !m_open )
218  return;
219 
220  if( m_lastChunkReceived != i->seq() )
221  {
222  m_open = false;
223  return;
224  }
225 
226  if( i->data().empty() )
227  {
228  m_open = false;
229  return;
230  }
231 
232  m_handler->handleBytestreamData( this, i->data() );
233  m_lastChunkReceived++;
234  }
235 
236  void InBandBytestream::returnResult( const JID& to, const std::string& id )
237  {
238  IQ iq( IQ::Result, to, id );
239  m_clientbase->send( iq );
240  }
241 
242  void InBandBytestream::returnError( const JID& to, const std::string& id, StanzaErrorType type, StanzaError error )
243  {
244  IQ iq( IQ::Error, to, id );
245  iq.addExtension( new Error( type, error ) );
246  m_clientbase->send( iq );
247  }
248 
249  bool InBandBytestream::send( const std::string& data )
250  {
251  if( !m_open || !m_clientbase )
252  return false;
253 
254  size_t pos = 0;
255  size_t len = data.length();
256  do
257  {
258  const std::string& id = m_clientbase->getID();
259  IQ iq( IQ::Set, m_clientbase->jid() == m_target ? m_initiator : m_target, id );
260  iq.addExtension( new IBB( m_sid, ++m_sequence, data.substr( pos, m_blockSize ) ) );
261  m_clientbase->send( iq, this, IBBData );
262 
263  pos += m_blockSize;
264  if( m_sequence == 65535 )
265  m_sequence = -1;
266  }
267  while( pos < len );
268 
269  return true;
270  }
271 
272  void InBandBytestream::closed()
273  {
274  if( !m_open )
275  return;
276 
277  m_open = false;
278 
279  if( m_handler )
281  }
282 
284  {
285  m_open = false;
286 
287  if( !m_clientbase )
288  return;
289 
290  const std::string& id = m_clientbase->getID();
291  IQ iq( IQ::Set, m_target, id );
292  iq.addExtension( new IBB( m_sid ) );
293  m_clientbase->send( iq, this, IBBClose );
294 
295  if( m_handler )
297  }
298 
299 }