1 /*
2  * Copyright (c) 2007-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 #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 
ConnectionBOSH(ConnectionBase * connection,const LogSink & logInstance,const std::string & boshHost,const std::string & xmppServer,int xmppPort)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 
ConnectionBOSH(ConnectionDataHandler * cdh,ConnectionBase * connection,const LogSink & logInstance,const std::string & boshHost,const std::string & xmppServer,int xmppPort)44   ConnectionBOSH::ConnectionBOSH( ConnectionDataHandler* cdh, ConnectionBase* connection,
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 
initInstance(ConnectionBase * connection,const std::string & xmppServer,const int xmppPort)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 
~ConnectionBOSH()76   ConnectionBOSH::~ConnectionBOSH()
77   {
78     util::clearList( m_activeConnections );
79     util::clearList( m_connectionPool );
80   }
81 
newInstance() const82   ConnectionBase* ConnectionBOSH::newInstance() const
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 
connect()103   ConnectionError ConnectionBOSH::connect()
104   {
105     if( m_state >= StateConnecting )
106       return ConnNoError;
107 
108     if( !m_handler )
109       return ConnNotConnected;
110 
111     m_state = StateConnecting;
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 
disconnect()121   void ConnectionBOSH::disconnect()
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 
156     m_state = StateDisconnected;
157     if( m_handler )
158       m_handler->handleDisconnect( this, ConnUserDisconnected );
159   }
160 
recv(int timeout)161   ConnectionError ConnectionBOSH::recv( int timeout )
162   {
163     ConnectionError ret = ConnNoError;
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 
send(const std::string & data)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(). */
sendXML()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. */
sendRequest(const std::string & xml)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 
ci_equal(char ch1,char ch2)306   bool ci_equal( char ch1, char ch2 )
307   {
308     return std::toupper( ch1 ) == std::toupper( ch2 );
309   }
310 
ci_find(const std::string & str1,const std::string & str2)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 
getHTTPField(const std::string & field)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 
receive()337   ConnectionError ConnectionBOSH::receive()
338   {
339     ConnectionError err = ConnNoError;
340     while( m_state != StateDisconnected && ( err = recv( 10 ) ) == ConnNoError )
341       ;
342     return err == ConnNoError ? ConnNotConnected : err;
343   }
344 
cleanup()345   void ConnectionBOSH::cleanup()
346   {
347     m_state = StateDisconnected;
348 
349     util::ForEach( m_activeConnections, &ConnectionBase::cleanup );
350     util::ForEach( m_connectionPool, &ConnectionBase::cleanup );
351   }
352 
getStatistics(long int & totalIn,long int & totalOut)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 
handleReceivedData(const ConnectionBase *,const std::string & data)359   void ConnectionBOSH::handleReceivedData( const ConnectionBase* /*connection*/,
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 
handleConnect(const ConnectionBase *)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 
handleDisconnect(const ConnectionBase *,ConnectionError reason)435   void ConnectionBOSH::handleDisconnect( const ConnectionBase* /*connection*/,
436                                          ConnectionError reason )
437   {
438     if( m_handler && m_state == StateConnecting )
439     {
440       m_state = StateDisconnected;
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 
handleTag(Tag * tag)460   void ConnectionBOSH::handleTag( Tag* tag )
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     {
478       m_state = StateConnected;
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" ) );
537       m_state = StateDisconnected;
538       m_handler->handleDisconnect( this, ConnStreamClosed );
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 
getConnection()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 
activateConnection()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 
putConnection()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 }
644