gloox 1.0.24
inbandbytestream.cpp
1/*
2 Copyright (c) 2006-2019 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
25namespace 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