gloox 1.0.28
connectionbosh.cpp
1/*
2 * Copyright (c) 2007-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#include "config.h"
14
15#include "gloox.h"
16
17#include "connectionbosh.h"
18#include "logsink.h"
19#include "prep.h"
20#include "tag.h"
21#include "util.h"
22
23#include <string>
24#include <cstdlib>
25#include <cctype>
26#include <algorithm>
27
28namespace gloox
29{
30
31 ConnectionBOSH::ConnectionBOSH( ConnectionBase* connection, const LogSink& logInstance,
32 const std::string& boshHost, const std::string& xmppServer,
33 int xmppPort )
34 : ConnectionBase( 0 ),
35 m_logInstance( logInstance ), m_parser( this ), m_boshHost( boshHost ), m_path( "/http-bind/" ),
36 m_rid( 0 ), m_initialStreamSent( false ), m_openRequests( 0 ),
37 m_maxOpenRequests( 2 ), m_wait( 30 ), m_hold( 1 ), m_streamRestart( false ),
38 m_lastRequestTime( std::time( 0 ) ), m_minTimePerRequest( 0 ), m_bufferContentLength( 0 ),
39 m_connMode( ModePipelining )
40 {
41 initInstance( connection, xmppServer, xmppPort );
42 }
43
45 const LogSink& logInstance, const std::string& boshHost,
46 const std::string& xmppServer, int xmppPort )
47 : ConnectionBase( cdh ),
48 m_logInstance( logInstance ), m_parser( this ), m_boshHost( boshHost ), m_path( "/http-bind/" ),
49 m_rid( 0 ), m_initialStreamSent( false ), m_openRequests( 0 ),
50 m_maxOpenRequests( 2 ), m_wait( 30 ), m_hold( 1 ), m_streamRestart( false ),
51 m_lastRequestTime( std::time( 0 ) ), m_minTimePerRequest( 0 ), m_bufferContentLength( 0 ),
52 m_connMode( ModePipelining )
53 {
54 initInstance( connection, xmppServer, xmppPort );
55 }
56
57 void ConnectionBOSH::initInstance( ConnectionBase* connection, const std::string& xmppServer,
58 const int xmppPort )
59 {
60// FIXME: check return value
61 prep::idna( xmppServer, m_server );
62 m_port = xmppPort;
63 if( m_port != -1 )
64 {
65 m_boshedHost = m_boshHost + ":" + util::int2string( m_port );
66 }
67
68 // drop this connection into our pool of available connections
69 if( connection )
70 {
71 connection->registerConnectionDataHandler( this );
72 m_connectionPool.push_back( connection );
73 }
74 }
75
77 {
78 util::clearList( m_activeConnections );
79 util::clearList( m_connectionPool );
80 }
81
83 {
84 ConnectionBase* pBaseConn = 0;
85
86 if( !m_connectionPool.empty() )
87 {
88 pBaseConn = m_connectionPool.front()->newInstance();
89 }
90 else if( !m_activeConnections.empty() )
91 {
92 pBaseConn = m_activeConnections.front()->newInstance();
93 }
94 else
95 {
96 return 0;
97 }
98
99 return new ConnectionBOSH( m_handler, pBaseConn, m_logInstance,
100 m_boshHost, m_server, m_port );
101 }
102
104 {
105 if( m_state >= StateConnecting )
106 return ConnNoError;
107
108 if( !m_handler )
109 return ConnNotConnected;
110
112 m_logInstance.dbg( LogAreaClassConnectionBOSH,
113 "Initiating BOSH connection to server: " +
114 ( ( m_connMode == ModePipelining ) ? std::string( "Pipelining" )
115 : ( ( m_connMode == ModeLegacyHTTP ) ? std::string( "LegacyHTTP" )
116 : std::string( "PersistentHTTP" ) ) ) );
117 getConnection();
118 return ConnNoError; // FIXME?
119 }
120
122 {
123 if( ( m_connMode == ModePipelining && m_activeConnections.empty() )
124 || ( m_connectionPool.empty() && m_activeConnections.empty() ) )
125 return;
126
128 {
129 ++m_rid;
130
131 std::string requestBody = "<body rid='" + util::long2string( m_rid ) + "' ";
132 requestBody += "sid='" + m_sid + "' ";
133 requestBody += "type='terminal' ";
134 requestBody += "xml:lang='en' ";
135 requestBody += "xmlns='" + XMLNS_HTTPBIND + "'";
136 if( m_sendBuffer.empty() ) // Make sure that any data in the send buffer gets sent
137 requestBody += "/>";
138 else
139 {
140 requestBody += ">" + m_sendBuffer + "</body>";
141 m_sendBuffer = EmptyString;
142 }
143 sendRequest( requestBody );
144
145 m_logInstance.dbg( LogAreaClassConnectionBOSH, "BOSH disconnection request sent" );
146 }
147 else
148 {
149 m_logInstance.err( LogAreaClassConnectionBOSH,
150 "Disconnecting from server in a non-graceful fashion" );
151 }
152
153 util::ForEach( m_activeConnections, &ConnectionBase::disconnect );
154 util::ForEach( m_connectionPool, &ConnectionBase::disconnect );
155
157 if( m_handler )
159 }
160
162 {
164
166 return ConnNotConnected;
167
168 if( !m_connectionPool.empty() )
169 ret = m_connectionPool.front()->recv( 0 );
170 if( !m_activeConnections.empty() )
171 ret = m_activeConnections.front()->recv( timeout );
172
173 // If there are no open requests then the spec allows us to send an empty request...
174 // (Some CMs do not obey this, it seems)
175 if( ( m_openRequests == 0 || m_sendBuffer.size() > 0 ) && m_state == StateConnected )
176 {
177 m_logInstance.dbg( LogAreaClassConnectionBOSH,
178 "Sending empty request (or there is data in the send buffer)" );
179 sendXML();
180 }
181
182 return ret;
183 }
184
185 bool ConnectionBOSH::send( const std::string& data )
186 {
187
189 return false;
190
191 if( data.substr( 0, 2 ) == "<?" )
192 {
193// if( m_initialStreamSent )
194 {
195 m_streamRestart = true;
196 sendXML();
197 return true;
198 }
199// else
200// {
201// m_initialStreamSent = true;
202// m_logInstance.dbg( LogAreaClassConnectionBOSH, "Initial <stream:stream> dropped" );
203// return true;
204// }
205 }
206 else if( data == "</stream:stream>" )
207 return true;
208
209 m_sendBuffer += data;
210 sendXML();
211
212 return true;
213 }
214
215 /* Sends XML. Wraps data in a <body/> tag, and then passes to sendRequest(). */
216 bool ConnectionBOSH::sendXML()
217 {
218 if( m_state != StateConnected )
219 {
220 m_logInstance.warn( LogAreaClassConnectionBOSH,
221 "Data sent before connection established (will be buffered)" );
222 return false;
223 }
224
225 if( m_sendBuffer.empty() )
226 {
227 time_t now = time( 0 );
228 unsigned long delta = now - m_lastRequestTime;
229 if( delta < m_minTimePerRequest && m_openRequests > 0 )
230 {
231 m_logInstance.dbg( LogAreaClassConnectionBOSH, "Too little time between requests: " + util::long2string( delta ) + " seconds" );
232 return false;
233 }
234 m_logInstance.dbg( LogAreaClassConnectionBOSH, "Send buffer is empty, sending empty request" );
235 }
236
237 ++m_rid;
238
239 std::string requestBody = "<body rid='" + util::long2string( m_rid ) + "' ";
240 requestBody += "sid='" + m_sid + "' ";
241 requestBody += "xmlns='" + XMLNS_HTTPBIND + "'";
242
243 if( m_streamRestart )
244 {
245 requestBody += " xmpp:restart='true' to='" + m_server + "' xml:lang='en' xmlns:xmpp='"
246 + XMLNS_XMPP_BOSH + "' />";
247 m_logInstance.dbg( LogAreaClassConnectionBOSH, "Restarting stream" );
248 }
249 else
250 {
251 requestBody += ">" + m_sendBuffer + "</body>";
252 }
253 // Send a request. Force if we are not sending an empty request, or if there are no connections open
254 if( sendRequest( requestBody ) )
255 {
256 m_logInstance.dbg( LogAreaClassConnectionBOSH, "Successfully sent m_sendBuffer" );
257 m_sendBuffer = EmptyString;
258 m_streamRestart = false;
259 }
260 else
261 {
262 --m_rid; // I think... (may need to rethink when acks are implemented)
263 m_logInstance.warn( LogAreaClassConnectionBOSH,
264 "Unable to send. Connection not complete, or too many open requests,"
265 " so added to buffer." );
266 }
267
268 return true;
269 }
270
271 /* Chooses the appropriate connection, or opens a new one if necessary. Wraps xml in HTTP and sends. */
272 bool ConnectionBOSH::sendRequest( const std::string& xml )
273 {
274 ConnectionBase* conn = getConnection();
275 if( !conn )
276 return false;
277
278 std::string request = "POST " + m_path;
279 if( m_connMode == ModeLegacyHTTP )
280 {
281 request += " HTTP/1.0\r\n";
282 request += "Connection: close\r\n";
283 }
284 else
285 request += " HTTP/1.1\r\n";
286
287 request += "Host: " + m_boshedHost + "\r\n";
288 request += "Content-Type: text/xml; charset=utf-8\r\n";
289 request += "Content-Length: " + util::long2string( xml.length() ) + "\r\n";
290 request += "User-Agent: gloox/" + GLOOX_VERSION + "\r\n\r\n";
291 request += xml;
292
293
294 if( conn->send( request ) )
295 {
296 m_lastRequestTime = time( 0 );
297 ++m_openRequests;
298 return true;
299 }
300// else // FIXME What to do in this case?
301// printf( "Error while trying to send on socket (state: %d)\n", conn->state() );
302
303 return false;
304 }
305
306 bool ci_equal( char ch1, char ch2 )
307 {
308 return std::toupper( ch1 ) == std::toupper( ch2 );
309 }
310
311 std::string::size_type ci_find( const std::string& str1, const std::string& str2 )
312 {
313 std::string::const_iterator pos = std::search( str1.begin(), str1.end(),
314 str2.begin(), str2.end(), ci_equal );
315 if( pos == str1.end() )
316 return std::string::npos;
317 else
318 return std::distance( str1.begin(), pos );
319 }
320
321 const std::string ConnectionBOSH::getHTTPField( const std::string& field )
322 {
323 std::string::size_type fp = ci_find( m_bufferHeader, "\r\n" + field + ": " );
324
325 if( fp == std::string::npos )
326 return EmptyString;
327
328 fp += field.length() + 4;
329
330 const std::string::size_type fp2 = m_bufferHeader.find( "\r\n", fp );
331 if( fp2 == std::string::npos )
332 return EmptyString;
333
334 return m_bufferHeader.substr( fp, fp2 - fp );
335 }
336
338 {
340 while( m_state != StateDisconnected && ( err = recv( 10 ) ) == ConnNoError )
341 ;
342 return err == ConnNoError ? ConnNotConnected : err;
343 }
344
346 {
348
349 util::ForEach( m_activeConnections, &ConnectionBase::cleanup );
350 util::ForEach( m_connectionPool, &ConnectionBase::cleanup );
351 }
352
353 void ConnectionBOSH::getStatistics( long int& totalIn, long int& totalOut )
354 {
355 util::ForEach( m_activeConnections, &ConnectionBase::getStatistics, totalIn, totalOut );
356 util::ForEach( m_connectionPool, &ConnectionBase::getStatistics, totalIn, totalOut );
357 }
358
360 const std::string& data )
361 {
362 m_buffer += data;
363 std::string::size_type headerLength = 0;
364 while( ( headerLength = m_buffer.find( "\r\n\r\n" ) ) != std::string::npos )
365 {
366 m_bufferHeader = m_buffer.substr( 0, headerLength+2 );
367
368 const std::string& statusCode = m_bufferHeader.substr( 9, 3 );
369 if( statusCode != "200" )
370 {
371 m_logInstance.warn( LogAreaClassConnectionBOSH,
372 "Received error via legacy HTTP status code: " + statusCode
373 + ". Disconnecting." );
374 m_state = StateDisconnected; // As per XEP, consider connection broken
375 disconnect();
376 }
377
378 m_bufferContentLength = atol( getHTTPField( "Content-Length" ).c_str() );
379 if( !m_bufferContentLength )
380 return;
381
382 if( m_connMode != ModeLegacyHTTP && ( getHTTPField( "Connection" ) == "close"
383 || m_bufferHeader.substr( 0, 8 ) == "HTTP/1.0" ) )
384 {
385 m_logInstance.dbg( LogAreaClassConnectionBOSH,
386 "Server indicated lack of support for HTTP/1.1 - falling back to HTTP/1.0" );
387 m_connMode = ModeLegacyHTTP;
388 }
389
390 if( m_buffer.length() >= ( headerLength + 4 + m_bufferContentLength ) )
391 {
392 putConnection();
393 --m_openRequests;
394 std::string xml = m_buffer.substr( headerLength + 4, m_bufferContentLength );
395 m_parser.feed( xml );
396 m_buffer.erase( 0, headerLength + 4 + m_bufferContentLength );
397 m_bufferContentLength = 0;
398 m_bufferHeader = EmptyString;
399 }
400 else
401 {
402 m_logInstance.warn( LogAreaClassConnectionBOSH, "Buffer length mismatch" );
403 break;
404 }
405 }
406 }
407
408 void ConnectionBOSH::handleConnect( const ConnectionBase* /*connection*/ )
409 {
410 if( m_state == StateConnecting )
411 {
412 m_rid = rand() % 100000 + 1728679472;
413
414 Tag requestBody( "body" );
415 requestBody.setXmlns( XMLNS_HTTPBIND );
416 requestBody.setXmlns( XMLNS_XMPP_BOSH, "xmpp" );
417
418 requestBody.addAttribute( "content", "text/xml; charset=utf-8" );
419 requestBody.addAttribute( "hold", m_hold );
420 requestBody.addAttribute( "rid", static_cast<long>( m_rid ) );
421 requestBody.addAttribute( "ver", "1.6" );
422 requestBody.addAttribute( "wait", m_wait );
423 requestBody.addAttribute( "ack", 0 );
424 requestBody.addAttribute( "secure", "false" );
425 requestBody.addAttribute( "route", "xmpp:" + m_server + ":5222" );
426 requestBody.addAttribute( "xml:lang", "en" );
427 requestBody.addAttribute( "xmpp:version", "1.0" );
428 requestBody.addAttribute( "to", m_server );
429
430 m_logInstance.dbg( LogAreaClassConnectionBOSH, "Sending BOSH connection request" );
431 sendRequest( requestBody.xml() );
432 }
433 }
434
436 ConnectionError reason )
437 {
439 {
441 m_handler->handleDisconnect( this, reason );
442 return;
443 }
444
445 switch( m_connMode ) // FIXME avoid that if we're disconnecting on purpose
446 {
447 case ModePipelining:
448 m_connMode = ModeLegacyHTTP; // Server seems not to support pipelining
449 m_logInstance.dbg( LogAreaClassConnectionBOSH,
450 "Connection closed - falling back to HTTP/1.0 connection method" );
451 break;
452 case ModeLegacyHTTP:
454 // FIXME do we need to do anything here?
455// printf( "A TCP connection %p was disconnected (reason: %d).\n", connection, reason );
456 break;
457 }
458 }
459
461 {
462 if( !m_handler || tag->name() != "body" )
463 return;
464
465 if( m_streamRestart )
466 {
467 m_streamRestart = false;
468 m_logInstance.dbg( LogAreaClassConnectionBOSH, "Sending spoofed <stream:stream>" );
469 m_handler->handleReceivedData( this, "<?xml version='1.0' ?>"
470 "<stream:stream xmlns:stream='http://etherx.jabber.org/streams'"
471 " xmlns='" + XMLNS_CLIENT + "' version='" + XMPP_STREAM_VERSION_MAJOR
472 + "." + XMPP_STREAM_VERSION_MINOR + "' from='" + m_server + "' id ='"
473 + m_sid + "' xml:lang='en'>" );
474 }
475
476 if( tag->hasAttribute( "sid" ) )
477 {
479 m_sid = tag->findAttribute( "sid" );
480
481 if( tag->hasAttribute( "requests" ) )
482 {
483 const int serverRequests = atoi( tag->findAttribute( "requests" ).c_str() );
484 if( serverRequests < m_maxOpenRequests )
485 {
486 m_maxOpenRequests = serverRequests;
487 m_logInstance.dbg( LogAreaClassConnectionBOSH,
488 "BOSH parameter 'requests' now set to " + tag->findAttribute( "requests" ) );
489 }
490 }
491 if( tag->hasAttribute( "hold" ) )
492 {
493 const int maxHold = atoi( tag->findAttribute( "hold" ).c_str() );
494 if( maxHold < m_hold )
495 {
496 m_hold = maxHold;
497 m_logInstance.dbg( LogAreaClassConnectionBOSH,
498 "BOSH parameter 'hold' now set to " + tag->findAttribute( "hold" ) );
499 }
500 }
501 if( tag->hasAttribute( "wait" ) )
502 {
503 const int maxWait = atoi( tag->findAttribute( "wait" ).c_str() );
504 if( maxWait < m_wait )
505 {
506 m_wait = maxWait;
507 m_logInstance.dbg( LogAreaClassConnectionBOSH,
508 "BOSH parameter 'wait' now set to " + tag->findAttribute( "wait" )
509 + " seconds" );
510 }
511 }
512 if( tag->hasAttribute( "polling" ) )
513 {
514 const int minTime = atoi( tag->findAttribute( "polling" ).c_str() );
515 m_minTimePerRequest = minTime;
516 m_logInstance.dbg( LogAreaClassConnectionBOSH,
517 "BOSH parameter 'polling' now set to " + tag->findAttribute( "polling" )
518 + " seconds" );
519 }
520
521 if( m_state < StateConnected )
522 m_handler->handleConnect( this );
523
524 m_handler->handleReceivedData( this, "<?xml version='1.0' ?>" // FIXME move to send() so that
525 // it is more clearly a response
526 // to the initial stream opener?
527 "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' "
528 "xmlns='" + XMLNS_CLIENT
530 + "' from='" + m_server + "' id ='" + m_sid + "' xml:lang='en'>" );
531 }
532
533 if( tag->findAttribute( "type" ) == "terminate" )
534 {
535 m_logInstance.dbg( LogAreaClassConnectionBOSH,
536 "BOSH connection closed by server: " + tag->findAttribute( "condition" ) );
539 return;
540 }
541
542 const TagList& stanzas = tag->children();
543 TagList::const_iterator it = stanzas.begin();
544 for( ; it != stanzas.end(); ++it )
545 m_handler->handleReceivedData( this, (*it)->xml() );
546 }
547
548 ConnectionBase* ConnectionBOSH::getConnection()
549 {
550 if( m_openRequests > 0 && m_openRequests >= m_maxOpenRequests )
551 {
552 m_logInstance.warn( LogAreaClassConnectionBOSH,
553 "Too many requests already open. Cannot send." );
554 return 0;
555 }
556
557 ConnectionBase* conn = 0;
558 switch( m_connMode )
559 {
560 case ModePipelining:
561 if( !m_activeConnections.empty() )
562 {
563 m_logInstance.dbg( LogAreaClassConnectionBOSH, "Using default connection for Pipelining." );
564 return m_activeConnections.front();
565 }
566 else if( !m_connectionPool.empty() )
567 {
568 m_logInstance.warn( LogAreaClassConnectionBOSH,
569 "Pipelining selected, but no connection open. Opening one." );
570 return activateConnection();
571 }
572 else
573 m_logInstance.warn( LogAreaClassConnectionBOSH,
574 "No available connections to pipeline on." );
575 break;
576 case ModeLegacyHTTP:
578 {
579 if( !m_connectionPool.empty() )
580 {
581 m_logInstance.dbg( LogAreaClassConnectionBOSH, "LegacyHTTP/PersistentHTTP selected, "
582 "using connection from pool." );
583 return activateConnection();
584 }
585 else if( !m_activeConnections.empty() )
586 {
587 m_logInstance.dbg( LogAreaClassConnectionBOSH, "No connections in pool, creating a new one." );
588 conn = m_activeConnections.front()->newInstance();
589 conn->registerConnectionDataHandler( this );
590 m_connectionPool.push_back( conn );
591 conn->connect();
592 }
593 else
594 m_logInstance.warn( LogAreaClassConnectionBOSH,
595 "No available connections to send on." );
596 break;
597 }
598 }
599 return 0;
600 }
601
602 ConnectionBase* ConnectionBOSH::activateConnection()
603 {
604 ConnectionBase* conn = m_connectionPool.front();
605 m_connectionPool.pop_front();
606 if( conn->state() == StateConnected )
607 {
608 m_activeConnections.push_back( conn );
609 return conn;
610 }
611
612 m_logInstance.dbg( LogAreaClassConnectionBOSH, "Connecting pooled connection." );
613 m_connectionPool.push_back( conn );
614 conn->connect();
615 return 0;
616 }
617
618 void ConnectionBOSH::putConnection()
619 {
620 ConnectionBase* conn = m_activeConnections.front();
621
622 switch( m_connMode )
623 {
624 case ModeLegacyHTTP:
625 m_logInstance.dbg( LogAreaClassConnectionBOSH, "Disconnecting LegacyHTTP connection" );
626 conn->disconnect();
627 conn->cleanup(); // This is necessary
628 m_activeConnections.pop_front();
629 m_connectionPool.push_back( conn );
630 break;
632 m_logInstance.dbg( LogAreaClassConnectionBOSH, "Deactivating PersistentHTTP connection" );
633 m_activeConnections.pop_front();
634 m_connectionPool.push_back( conn );
635 break;
636 case ModePipelining:
637 m_logInstance.dbg( LogAreaClassConnectionBOSH, "Keeping Pipelining connection" );
638 default:
639 break;
640 }
641 }
642
643}
This is an implementation of a BOSH (HTTP binding) connection.
virtual void handleDisconnect(const ConnectionBase *connection, ConnectionError reason)
virtual ConnectionError recv(int timeout=-1)
virtual void handleTag(Tag *tag)
virtual void handleConnect(const ConnectionBase *connection)
virtual void handleReceivedData(const ConnectionBase *connection, const std::string &data)
ConnectionBOSH(ConnectionBase *connection, const LogSink &logInstance, const std::string &boshHost, const std::string &xmppServer, int xmppPort=5222)
virtual ConnectionBase * newInstance() const
virtual ConnectionError connect()
virtual bool send(const std::string &data)
virtual ConnectionError receive()
virtual void getStatistics(long int &totalIn, long int &totalOut)
An abstract base class for a connection.
virtual void cleanup()
ConnectionState m_state
ConnectionDataHandler * m_handler
ConnectionBase(ConnectionDataHandler *cdh)
virtual void getStatistics(long int &totalIn, long int &totalOut)=0
virtual void disconnect()=0
void registerConnectionDataHandler(ConnectionDataHandler *cdh)
This is an abstract base class to receive events from a ConnectionBase-derived object.
virtual void handleReceivedData(const ConnectionBase *connection, const std::string &data)=0
virtual void handleDisconnect(const ConnectionBase *connection, ConnectionError reason)=0
virtual void handleConnect(const ConnectionBase *connection)=0
An implementation of log sink and source.
Definition logsink.h:39
void warn(LogArea area, const std::string &message) const
Definition logsink.h:75
void dbg(LogArea area, const std::string &message) const
Definition logsink.h:66
void err(LogArea area, const std::string &message) const
Definition logsink.h:84
int feed(std::string &data)
Definition parser.cpp:161
This is an abstraction of an XML element.
Definition tag.h:47
const std::string & name() const
Definition tag.h:394
bool addAttribute(Attribute *attr)
Definition tag.cpp:354
bool hasAttribute(const std::string &name, const std::string &value=EmptyString) const
Definition tag.cpp:602
const std::string & findAttribute(const std::string &name) const
Definition tag.cpp:589
const std::string xml() const
Definition tag.cpp:302
const TagList & children() const
Definition tag.cpp:510
bool setXmlns(const std::string &xmlns, const std::string &prefix=EmptyString)
Definition tag.cpp:522
bool idna(const std::string &domain, std::string &out)
Definition prep.cpp:107
void clearList(std::list< T * > &L)
Definition util.h:152
void ForEach(T &t, F f)
Definition util.h:96
The namespace for the gloox library.
Definition adhoc.cpp:28
std::list< Tag * > TagList
Definition tag.h:31
const std::string GLOOX_VERSION
Definition gloox.cpp:119
ConnectionError
Definition gloox.h:684
@ ConnUserDisconnected
Definition gloox.h:714
@ ConnNotConnected
Definition gloox.h:715
@ ConnNoError
Definition gloox.h:685
@ ConnStreamClosed
Definition gloox.h:689
@ LogAreaClassConnectionBOSH
Definition gloox.h:1066
const std::string XMPP_STREAM_VERSION_MAJOR
Definition gloox.cpp:117
const std::string XMLNS_CLIENT
Definition gloox.cpp:19
const std::string XMPP_STREAM_VERSION_MINOR
Definition gloox.cpp:118
const std::string EmptyString
Definition gloox.cpp:124
@ StateDisconnected
Definition gloox.h:642
@ StateConnected
Definition gloox.h:644
@ StateConnecting
Definition gloox.h:643
const std::string XMLNS_XMPP_BOSH
Definition gloox.cpp:97
const std::string XMLNS_HTTPBIND
Definition gloox.cpp:96