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