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 
28 namespace 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 
127  if( m_state != StateDisconnected )
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 
165  if( m_state == StateDisconnected )
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 
188  if( m_state == StateDisconnected )
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  {
438  if( m_handler && m_state == StateConnecting )
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:
453  case ModePersistentHTTP:
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
529  + "' version='" + XMPP_STREAM_VERSION_MAJOR + "." + XMPP_STREAM_VERSION_MINOR
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:
577  case ModePersistentHTTP:
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;
631  case ModePersistentHTTP:
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 }
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 void disconnect()
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
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 & name() const
Definition: tag.h:394
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 ForEach(T &t, F f)
Definition: util.h:96
void clearList(std::list< T * > &L)
Definition: util.h:152
The namespace for the gloox library.
Definition: adhoc.cpp:28
std::list< Tag * > TagList
Definition: tag.h:26
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