gloox  1.0.1
clientbase.cpp
1 /*
2  Copyright (c) 2005-2012 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 
15 #include "config.h"
16 
17 #include "base64.h"
18 #include "clientbase.h"
19 #include "compressionbase.h"
20 #include "compressionzlib.h"
21 #include "connectionbase.h"
22 #include "connectionlistener.h"
23 #include "connectiontcpclient.h"
24 #include "disco.h"
25 #include "error.h"
26 #include "eventhandler.h"
27 #include "event.h"
28 #include "iq.h"
29 #include "iqhandler.h"
30 #include "jid.h"
31 #include "loghandler.h"
32 #include "md5.h"
33 #include "message.h"
34 #include "messagehandler.h"
35 #include "messagesessionhandler.h"
36 #include "mucinvitationhandler.h"
37 #include "mucroom.h"
38 #include "mutexguard.h"
39 #include "presence.h"
40 #include "presencehandler.h"
41 #include "rosterlistener.h"
42 #include "stanzaextensionfactory.h"
43 #include "subscription.h"
44 #include "subscriptionhandler.h"
45 #include "tag.h"
46 #include "taghandler.h"
47 #include "tlsbase.h"
48 #include "tlsdefault.h"
49 #include "util.h"
50 
51 #include <cstdlib>
52 #include <string>
53 #include <map>
54 #include <list>
55 #include <algorithm>
56 #include <cmath>
57 #include <ctime>
58 #include <cstdio>
59 
60 #include <string.h> // for memset()
61 
62 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
63 #include <tchar.h>
64 #endif
65 
66 namespace gloox
67 {
68 
69  // ---- ClientBase::Ping ----
70  ClientBase::Ping::Ping()
71  : StanzaExtension( ExtPing )
72  {
73  }
74 
75  ClientBase::Ping::~Ping()
76  {
77  }
78 
79  const std::string& ClientBase::Ping::filterString() const
80  {
81  static const std::string filter = "/iq/ping[@xmlns='" + XMLNS_XMPP_PING + "']";
82  return filter;
83  }
84  // ---- ~ClientBase::Ping ----
85 
86  // ---- ClientBase ----
87  ClientBase::ClientBase( const std::string& ns, const std::string& server, int port )
88  : m_connection( 0 ), m_encryption( 0 ), m_compression( 0 ), m_disco( 0 ), m_namespace( ns ),
89  m_xmllang( "en" ), m_server( server ), m_compressionActive( false ), m_encryptionActive( false ),
90  m_compress( true ), m_authed( false ), m_block( false ), m_sasl( true ), m_tls( TLSOptional ), m_port( port ),
91  m_availableSaslMechs( SaslMechAll ),
92  m_statisticsHandler( 0 ), m_mucInvitationHandler( 0 ),
93  m_messageSessionHandlerChat( 0 ), m_messageSessionHandlerGroupchat( 0 ),
94  m_messageSessionHandlerHeadline( 0 ), m_messageSessionHandlerNormal( 0 ),
95  m_parser( this ), m_seFactory( 0 ), m_authError( AuthErrorUndefined ),
96  m_streamError( StreamErrorUndefined ), m_streamErrorAppCondition( 0 ),
97  m_selectedSaslMech( SaslMechNone ), m_autoMessageSession( false ), m_customConnection( false ),
98  m_uniqueBaseId( (unsigned int)( ( (unsigned long long)time( 0 ) & 0xFFFF ) << 16 ) | ( ( (unsigned long long)&m_nextId ) & 0xFFFF ) )
99  {
100  init();
101  }
102 
103  ClientBase::ClientBase( const std::string& ns, const std::string& password,
104  const std::string& server, int port )
105  : m_connection( 0 ), m_encryption( 0 ), m_compression( 0 ), m_disco( 0 ), m_namespace( ns ),
106  m_password( password ),
107  m_xmllang( "en" ), m_server( server ), m_compressionActive( false ), m_encryptionActive( false ),
108  m_compress( true ), m_authed( false ), m_block( false ), m_sasl( true ), m_tls( TLSOptional ),
109  m_port( port ), m_availableSaslMechs( SaslMechAll ),
110  m_statisticsHandler( 0 ), m_mucInvitationHandler( 0 ),
111  m_messageSessionHandlerChat( 0 ), m_messageSessionHandlerGroupchat( 0 ),
112  m_messageSessionHandlerHeadline( 0 ), m_messageSessionHandlerNormal( 0 ),
113  m_parser( this ), m_seFactory( 0 ), m_authError( AuthErrorUndefined ),
114  m_streamError( StreamErrorUndefined ), m_streamErrorAppCondition( 0 ),
115  m_selectedSaslMech( SaslMechNone ), m_autoMessageSession( false ), m_customConnection( false ),
116  m_uniqueBaseId( (unsigned int)( ( (unsigned long long)time( 0 ) & 0xFFFF ) << 16 ) | ( ( (unsigned long long)&m_nextId ) & 0xFFFF ) )
117  {
118  init();
119  }
120 
121  void ClientBase::init()
122  {
123  if( !m_disco )
124  {
125  m_disco = new Disco( this );
126  m_disco->setVersion( "based on gloox", GLOOX_VERSION );
128  }
129 
130  registerStanzaExtension( new Error() );
131  registerStanzaExtension( new Ping() );
132  registerIqHandler( this, ExtPing );
133 
134  m_streamError = StreamErrorUndefined;
135  m_block = false;
136  memset( &m_stats, 0, sizeof( m_stats ) );
137  cleanup();
138  }
139 
141  {
142  m_iqHandlerMapMutex.lock();
143  m_iqIDHandlers.clear();
144  m_iqHandlerMapMutex.unlock();
145 
146  m_iqExtHandlerMapMutex.lock();
147  m_iqExtHandlers.clear();
148  m_iqExtHandlerMapMutex.unlock();
149 
150  delete m_connection;
151  delete m_encryption;
152  delete m_compression;
153  delete m_seFactory;
154  m_seFactory = 0; // to avoid usage when Disco gets deleted below
155  delete m_disco;
156  m_disco = 0;
157 
158  util::clearList( m_messageSessions );
159 
160  PresenceJidHandlerList::const_iterator it1 = m_presenceJidHandlers.begin();
161  for( ; it1 != m_presenceJidHandlers.end(); ++it1 )
162  delete (*it1).jid;
163  }
164 
166  {
168  return ConnNotConnected;
169 
170  return m_connection->recv( timeout );
171  }
172 
173  bool ClientBase::connect( bool block )
174  {
175  if( m_server.empty() )
176  return false;
177 
178  if( !m_connection )
179  m_connection = new ConnectionTCPClient( this, m_logInstance, m_server, m_port );
180 
182  return true;
183 
184  if( !m_encryption )
185  m_encryption = getDefaultEncryption();
186 
187  if( !m_compression )
188  m_compression = getDefaultCompression();
189 
190  m_logInstance.dbg( LogAreaClassClientbase, "This is gloox " + GLOOX_VERSION + ", connecting to "
191  + m_server + ( ( m_customConnection )?( " using a custom connection" ):( ":" + util::int2string( m_port ) ) ) + "..." );
192  m_block = block;
194  if( ret != ConnNoError )
195  return false;
196 
197  if( m_block )
199 
200  return true;
201  }
202 
204  {
205  if( !tag )
206  {
207  logInstance().dbg( LogAreaClassClientbase, "stream closed" );
209  return;
210  }
211 
212  logInstance().dbg( LogAreaXmlIncoming, tag->xml() );
213  ++m_stats.totalStanzasReceived;
214 
215  if( tag->name() == "stream" && tag->xmlns() == XMLNS_STREAM )
216  {
217  const std::string& version = tag->findAttribute( "version" );
218  if( !checkStreamVersion( version ) )
219  {
220  logInstance().dbg( LogAreaClassClientbase, "This server is not XMPP-compliant"
221  " (it does not send a 'version' attribute). Please fix it or try another one.\n" );
223  return;
224  }
225 
226  m_sid = tag->findAttribute( "id" );
227  handleStartNode( tag );
228  }
229  else if( tag->name() == "error" && tag->xmlns() == XMLNS_STREAM )
230  {
231  handleStreamError( tag );
233  }
234  else
235  {
236  if( !handleNormalNode( tag ) )
237  {
238  if( tag->xmlns().empty() || tag->xmlns() == XMLNS_CLIENT )
239  {
240  if( tag->name() == "iq" )
241  {
242  IQ iq( tag );
243  m_seFactory->addExtensions( iq, tag );
244  notifyIqHandlers( iq );
245  ++m_stats.iqStanzasReceived;
246  }
247  else if( tag->name() == "message" )
248  {
249  Message msg( tag );
250  m_seFactory->addExtensions( msg, tag );
251  notifyMessageHandlers( msg );
252  ++m_stats.messageStanzasReceived;
253  }
254  else if( tag->name() == "presence" )
255  {
256  const std::string& type = tag->findAttribute( TYPE );
257  if( type == "subscribe" || type == "unsubscribe"
258  || type == "subscribed" || type == "unsubscribed" )
259  {
260  Subscription sub( tag );
261  m_seFactory->addExtensions( sub, tag );
262  notifySubscriptionHandlers( sub );
263  ++m_stats.s10nStanzasReceived;
264  }
265  else
266  {
267  Presence pres( tag );
268  m_seFactory->addExtensions( pres, tag );
269  notifyPresenceHandlers( pres );
270  ++m_stats.presenceStanzasReceived;
271  }
272  }
273  else
274  m_logInstance.err( LogAreaClassClientbase, "Received invalid stanza." );
275  }
276  else
277  {
278  notifyTagHandlers( tag );
279  }
280  }
281  }
282 
283  if( m_statisticsHandler )
284  m_statisticsHandler->handleStatistics( getStatistics() );
285  }
286 
287  void ClientBase::handleCompressedData( const std::string& data )
288  {
290  m_encryption->encrypt( data );
291  else if( m_connection )
292  m_connection->send( data );
293  else
294  m_logInstance.err( LogAreaClassClientbase, "Compression finished, but chain broken" );
295  }
296 
297  void ClientBase::handleDecompressedData( const std::string& data )
298  {
299  parse( data );
300  }
301 
302  void ClientBase::handleEncryptedData( const TLSBase* /*base*/, const std::string& data )
303  {
304  if( m_connection )
305  m_connection->send( data );
306  else
307  m_logInstance.err( LogAreaClassClientbase, "Encryption finished, but chain broken" );
308  }
309 
310  void ClientBase::handleDecryptedData( const TLSBase* /*base*/, const std::string& data )
311  {
313  m_compression->decompress( data );
314  else
315  parse( data );
316  }
317 
318  void ClientBase::handleHandshakeResult( const TLSBase* /*base*/, bool success, CertInfo &certinfo )
319  {
320  if( success )
321  {
322  if( !notifyOnTLSConnect( certinfo ) )
323  {
324  logInstance().err( LogAreaClassClientbase, "Server's certificate rejected!" );
326  }
327  else
328  {
329  logInstance().dbg( LogAreaClassClientbase, "connection encryption active" );
330  header();
331  }
332  }
333  else
334  {
335  logInstance().err( LogAreaClassClientbase, "TLS handshake failed!" );
337  }
338  }
339 
340  void ClientBase::handleReceivedData( const ConnectionBase* /*connection*/, const std::string& data )
341  {
343  m_encryption->decrypt( data );
344  else if( m_compression && m_compressionActive )
345  m_compression->decompress( data );
346  else
347  parse( data );
348  }
349 
350  void ClientBase::handleConnect( const ConnectionBase* /*connection*/ )
351  {
352  header();
353  }
354 
355  void ClientBase::handleDisconnect( const ConnectionBase* /*connection*/, ConnectionError reason )
356  {
357  if( m_connection )
359 
360  if( m_encryption )
362 
363  if( m_compression )
365 
366  m_encryptionActive = false;
367  m_compressionActive = false;
368 
369  notifyOnDisconnect( reason );
370  }
371 
373  {
375  return;
376 
377  if( reason != ConnTlsFailed )
378  send( "</stream:stream>" );
379 
382 
383  if( m_encryption )
385 
386  if( m_compression )
388 
389  m_encryptionActive = false;
390  m_compressionActive = false;
391 
392  notifyOnDisconnect( reason );
393  }
394 
395  void ClientBase::parse( const std::string& data )
396  {
397  std::string copy = data;
398  int i = 0;
399  if( ( i = m_parser.feed( copy ) ) >= 0 )
400  {
401  std::string error = "parse error (at pos ";
402  error += util::int2string( i );
403  error += "): ";
404  m_logInstance.err( LogAreaClassClientbase, error + copy );
405  Tag* e = new Tag( "stream:error" );
406  new Tag( e, "restricted-xml", "xmlns", XMLNS_XMPP_STREAM );
407  send( e );
409  }
410  }
411 
413  {
414  std::string head = "<?xml version='1.0' ?>";
415  head += "<stream:stream to='" + m_jid.server() + "' xmlns='" + m_namespace + "' ";
416  head += "xmlns:stream='http://etherx.jabber.org/streams' xml:lang='" + m_xmllang + "' ";
417  head += "version='" + XMPP_STREAM_VERSION_MAJOR + "." + XMPP_STREAM_VERSION_MINOR + "'>";
418  send( head );
419  }
420 
422  {
423 #if defined( HAVE_GNUTLS ) || defined( HAVE_OPENSSL ) || defined( HAVE_WINTLS )
424  return true;
425 #else
426  return false;
427 #endif
428  }
429 
431  {
432  send( new Tag( "starttls", XMLNS, XMLNS_STREAM_TLS ) );
433  }
434 
435  void ClientBase::setServer( const std::string &server )
436  {
437  m_server = server;
438  if( m_connection )
439  m_connection->setServer( server );
440  }
441 
442  void ClientBase::setClientCert( const std::string& clientKey, const std::string& clientCerts )
443  {
444  m_clientKey = clientKey;
445  m_clientCerts = clientCerts;
446  }
447 
449  {
450  m_selectedSaslMech = type;
451 
452  Tag* a = new Tag( "auth", XMLNS, XMLNS_STREAM_SASL );
453 
454  switch( type )
455  {
456  case SaslMechDigestMd5:
457  a->addAttribute( "mechanism", "DIGEST-MD5" );
458  break;
459  case SaslMechPlain:
460  {
461  a->addAttribute( "mechanism", "PLAIN" );
462 
463  std::string tmp;
464  if( m_authzid )
465  tmp += m_authzid.bare();
466 
467  tmp += '\0';
468  if( !m_authcid.empty() )
469  tmp += m_authcid;
470  else
471  tmp += m_jid.username();
472  tmp += '\0';
473  tmp += m_password;
474  a->setCData( Base64::encode64( tmp ) );
475  break;
476  }
477  case SaslMechAnonymous:
478  a->addAttribute( "mechanism", "ANONYMOUS" );
479  break;
480  case SaslMechExternal:
481  a->addAttribute( "mechanism", "EXTERNAL" );
483  break;
484  case SaslMechGssapi:
485  {
486 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
487  a->addAttribute( "mechanism", "GSSAPI" );
488 // The client calls GSS_Init_sec_context, passing in 0 for
489 // input_context_handle (initially) and a targ_name equal to output_name
490 // from GSS_Import_Name called with input_name_type of
491 // GSS_C_NT_HOSTBASED_SERVICE and input_name_string of
492 // "service@hostname" where "service" is the service name specified in
493 // the protocol's profile, and "hostname" is the fully qualified host
494 // name of the server. The client then responds with the resulting
495 // output_token.
496  std::string token;
497  a->setCData( Base64::encode64( token ) );
498 // etc... see gssapi-sasl-draft.txt
499 #else
501  "SASL GSSAPI is not supported on this platform. You should never see this." );
502 #endif
503  break;
504  }
505  case SaslMechNTLM:
506  {
507 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
508  a->addAttribute( "mechanism", "NTLM" );
509  SEC_WINNT_AUTH_IDENTITY_W identity, *ident = 0;
510  memset( &identity, 0, sizeof( identity ) );
511 
512  WCHAR *usernameW = 0, *domainW = NULL, *passwordW = 0;
513  int cchUsernameW = 0, cchDomainW = 0, cchPasswordW = 0;
514 
515  if( m_jid.username().length() > 0 )
516  {
517  // NOTE: The return values of MultiByteToWideChar will include room
518  // for the NUL character since we use -1 for the input length.
519 
520  cchUsernameW = ::MultiByteToWideChar( CP_UTF8, 0, m_jid.username().c_str(), -1, 0, 0 );
521  if( cchUsernameW > 0 )
522  {
523  usernameW = new WCHAR[cchUsernameW];
524  ::MultiByteToWideChar( CP_UTF8, 0, m_jid.username().c_str(), -1, usernameW, cchUsernameW );
525  // Guarantee its NUL terminated.
526  usernameW[cchUsernameW-1] = L'\0';
527  }
528  cchDomainW = ::MultiByteToWideChar( CP_UTF8, 0, m_ntlmDomain.c_str(), -1, 0, 0 );
529  if( cchDomainW > 0 )
530  {
531  domainW = new WCHAR[cchDomainW];
532  ::MultiByteToWideChar( CP_UTF8, 0, m_ntlmDomain.c_str(), -1, domainW, cchDomainW );
533  // Guarantee its NUL terminated.
534  domainW[cchDomainW-1] = L'\0';
535  }
536  cchPasswordW = ::MultiByteToWideChar( CP_UTF8, 0, m_password.c_str(), -1, 0, 0 );
537  if( cchPasswordW > 0 )
538  {
539  passwordW = new WCHAR[cchPasswordW];
540  ::MultiByteToWideChar( CP_UTF8, 0, m_password.c_str(), -1, passwordW, cchPasswordW );
541  // Guarantee its NUL terminated.
542  passwordW[cchPasswordW-1] = L'\0';
543  }
544  identity.User = (unsigned short*)usernameW;
545  identity.UserLength = (unsigned long)cchUsernameW-1;
546  identity.Domain = (unsigned short*)domainW;
547  identity.DomainLength = (unsigned long)cchDomainW-1;
548  identity.Password = (unsigned short*)passwordW;
549  identity.PasswordLength = (unsigned long)cchPasswordW-1;
550  identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
551  ident = &identity;
552  }
553 
554  AcquireCredentialsHandleW( 0, L"NTLM", SECPKG_CRED_OUTBOUND, 0, ident, 0, 0, &m_credHandle, 0 );
555 
556  if( usernameW != 0 )
557  {
558  delete[] usernameW;
559  usernameW = 0;
560  }
561  if( domainW != 0 )
562  {
563  delete[] domainW;
564  domainW = 0;
565  }
566  if( passwordW != 0 )
567  {
568  ::SecureZeroMemory( passwordW, cchPasswordW* sizeof( WCHAR ) );
569  delete[] passwordW;
570  passwordW = 0;
571  }
572 
573 #else
575  "SASL NTLM is not supported on this platform. You should never see this." );
576 #endif
577  break;
578  }
579  default:
580  break;
581  }
582 
583  send( a );
584  }
585 
586  void ClientBase::processSASLChallenge( const std::string& challenge )
587  {
588  Tag* t = new Tag( "response", XMLNS, XMLNS_STREAM_SASL );
589 
590  const std::string& decoded = Base64::decode64( challenge );
591 
592  switch( m_selectedSaslMech )
593  {
594  case SaslMechDigestMd5:
595  {
596  if( !decoded.compare( 0, 7, "rspauth" ) )
597  break;
598 
599  std::string realm;
600  std::string::size_type end = 0;
601  std::string::size_type pos = decoded.find( "realm=" );
602  if( pos != std::string::npos )
603  {
604  end = decoded.find( '"', pos + 7 );
605  realm = decoded.substr( pos + 7, end - ( pos + 7 ) );
606  }
607  else
608  realm = m_jid.server();
609 
610  pos = decoded.find( "nonce=" );
611  if( pos == std::string::npos )
612  return;
613 
614  end = decoded.find( '"', pos + 7 );
615  while( decoded[end-1] == '\\' )
616  end = decoded.find( '"', end + 1 );
617  std::string nonce = decoded.substr( pos + 7, end - ( pos + 7 ) );
618 
619  std::string cnonce;
620  char cn[4*8+1];
621  for( int i = 0; i < 4; ++i )
622  sprintf( cn + i*8, "%08x", rand() );
623  cnonce.assign( cn, 4*8 );
624 
625  MD5 md5;
626  md5.feed( m_jid.username() );
627  md5.feed( ":" );
628  md5.feed( realm );
629  md5.feed( ":" );
630  md5.feed( m_password );
631  md5.finalize();
632  const std::string& a1_h = md5.binary();
633  md5.reset();
634  md5.feed( a1_h );
635  md5.feed( ":" );
636  md5.feed( nonce );
637  md5.feed( ":" );
638  md5.feed( cnonce );
639  md5.finalize();
640  const std::string& a1 = md5.hex();
641  md5.reset();
642  md5.feed( "AUTHENTICATE:xmpp/" );
643  md5.feed( m_jid.server() );
644  md5.finalize();
645  const std::string& a2 = md5.hex();
646  md5.reset();
647  md5.feed( a1 );
648  md5.feed( ":" );
649  md5.feed( nonce );
650  md5.feed( ":00000001:" );
651  md5.feed( cnonce );
652  md5.feed( ":auth:" );
653  md5.feed( a2 );
654  md5.finalize();
655 
656  std::string response = "username=\"";
657  response += m_jid.username();
658  response += "\",realm=\"";
659  response += realm;
660  response += "\",nonce=\"";
661  response += nonce;
662  response += "\",cnonce=\"";
663  response += cnonce;
664  response += "\",nc=00000001,qop=auth,digest-uri=\"xmpp/";
665  response += m_jid.server();
666  response += "\",response=";
667  response += md5.hex();
668  response += ",charset=utf-8";
669 
670  if( m_authzid )
671  response += ",authzid=" + m_authzid.bare();
672 
673  t->setCData( Base64::encode64( response ) );
674 
675  break;
676  }
677  case SaslMechGssapi:
678 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
679  // see gssapi-sasl-draft.txt
680 #else
681  m_logInstance.err( LogAreaClassClientbase,
682  "Huh, received GSSAPI challenge?! This should have never happened!" );
683 #endif
684  break;
685  case SaslMechNTLM:
686  {
687 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
688  bool type1 = ( decoded.length() < 7 ) ? true : false;
689 
690  SecBuffer bufferIn = { type1 ? 0 : (unsigned long)decoded.length(),
691  SECBUFFER_TOKEN,
692  (void*)decoded.c_str() };
693  SecBufferDesc secIn = { 0, 1, &bufferIn };
694 
695  char buffer[4096];
696 
697  SecBuffer bufferOut = { sizeof( buffer ), SECBUFFER_TOKEN, buffer };
698  SecBufferDesc secOut = { 0, 1, &bufferOut };
699 
700  TimeStamp timestamp;
701  unsigned long contextAttr;
702 
703  SECURITY_STATUS status = InitializeSecurityContext( &m_credHandle, type1 ? 0 : &m_ctxtHandle,
704  0, ISC_REQ_MUTUAL_AUTH, 0, 0, &secIn, 0,
705  &m_ctxtHandle, &secOut, &contextAttr,
706  &timestamp );
707  std::string response;
708  if( SUCCEEDED( status ) )
709  {
710  response = std::string( (const char *)bufferOut.pvBuffer, bufferOut.cbBuffer );
711  }
712  else
713  {
715  "InitializeSecurityContext() failed, return value "
716  + util::int2string( status ) );
717  }
718 
719  t->setCData( Base64::encode64( response ) );
720 #else
721  m_logInstance.err( LogAreaClassClientbase,
722  "Huh, received NTLM challenge?! This should have never happened!" );
723 #endif
724  break;
725  }
726 
727  default:
728  // should never happen.
729  break;
730  }
731 
732  send( t );
733  }
734 
736  {
737  if( tag->hasChild( "aborted" ) )
738  m_authError = SaslAborted;
739  else if( tag->hasChild( "incorrect-encoding" ) )
740  m_authError = SaslIncorrectEncoding;
741  else if( tag->hasChild( "invalid-authzid" ) )
742  m_authError = SaslInvalidAuthzid;
743  else if( tag->hasChild( "invalid-mechanism" ) )
744  m_authError = SaslInvalidMechanism;
745  else if( tag->hasChild( "malformed-request" ) )
746  m_authError = SaslMalformedRequest;
747  else if( tag->hasChild( "mechanism-too-weak" ) )
748  m_authError = SaslMechanismTooWeak;
749  else if( tag->hasChild( "not-authorized" ) )
750  m_authError = SaslNotAuthorized;
751  else if( tag->hasChild( "temporary-auth-failure" ) )
752  m_authError = SaslTemporaryAuthFailure;
753 
754 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
755  if( m_selectedSaslMech == SaslMechNTLM )
756  {
757  FreeCredentialsHandle( &m_credHandle );
758  DeleteSecurityContext( &m_ctxtHandle );
759  }
760 #endif
761  }
762 
764  {
765 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
766  if( m_selectedSaslMech == SaslMechNTLM )
767  {
768  FreeCredentialsHandle( &m_credHandle );
769  DeleteSecurityContext( &m_ctxtHandle );
770  }
771 #endif
772  }
773 
774  void ClientBase::send( IQ& iq, IqHandler* ih, int context, bool del )
775  {
776  if( ih && ( iq.subtype() == IQ::Set || iq.subtype() == IQ::Get ) )
777  {
778  if( iq.id().empty() )
779  iq.setID( getID() );
780 
781  TrackStruct track;
782  track.ih = ih;
783  track.context = context;
784  track.del = del;
785  m_iqHandlerMapMutex.lock();
786  m_iqIDHandlers[iq.id()] = track;
787  m_iqHandlerMapMutex.unlock();
788  }
789 
790  send( iq );
791  }
792 
793  void ClientBase::send( const IQ& iq )
794  {
795  ++m_stats.iqStanzasSent;
796  Tag* tag = iq.tag();
797  addFrom( tag );
798  addNamespace( tag );
799  send( tag );
800  }
801 
802  void ClientBase::send( const Message& msg )
803  {
804  ++m_stats.messageStanzasSent;
805  Tag* tag = msg.tag();
806  addFrom( tag );
807  addNamespace( tag );
808  send( tag );
809  }
810 
811  void ClientBase::send( const Subscription& sub )
812  {
813  ++m_stats.s10nStanzasSent;
814  Tag* tag = sub.tag();
815  addFrom( tag );
816  addNamespace( tag );
817  send( tag );
818  }
819 
820  void ClientBase::send( const Presence& pres )
821  {
822  ++m_stats.presenceStanzasSent;
823  Tag* tag = pres.tag();
824  StanzaExtensionList::const_iterator it = m_presenceExtensions.begin();
825  for( ; it != m_presenceExtensions.end(); ++it )
826  tag->addChild( (*it)->tag() );
827  addFrom( tag );
828  addNamespace( tag );
829  send( tag );
830  }
831 
832  void ClientBase::send( Tag* tag )
833  {
834  if( !tag )
835  return;
836 
837  send( tag->xml() );
838 
839  ++m_stats.totalStanzasSent;
840 
841  if( m_statisticsHandler )
842  m_statisticsHandler->handleStatistics( getStatistics() );
843 
844  delete tag;
845  }
846 
847  void ClientBase::send( const std::string& xml )
848  {
850  {
852  m_compression->compress( xml );
853  else if( m_encryption && m_encryptionActive )
854  m_encryption->encrypt( xml );
855  else
856  m_connection->send( xml );
857 
859  }
860  }
861 
862  void ClientBase::addFrom( Tag* tag )
863  {
864  if( !m_authed /*for IQ Auth */ || !tag || tag->hasAttribute( "from" ) )
865  return;
866 
867  tag->addAttribute( "from", m_jid.full() );
868  }
869 
870  void ClientBase::addNamespace( Tag* tag )
871  {
872  if( !tag || !tag->xmlns().empty() )
873  return;
874 
875  tag->setXmlns( m_namespace );
876  }
877 
879  {
880  if( !m_seFactory )
881  m_seFactory = new StanzaExtensionFactory();
882 
883  m_seFactory->registerExtension( ext );
884  }
885 
887  {
888  if( !m_seFactory )
889  return false;
890 
891  return m_seFactory->removeExtension( ext );
892  }
893 
895  {
896  if( m_connection )
898 
899  return m_stats;
900  }
901 
903  {
905  }
906 
908  {
909  send( " " );
910  }
911 
912  void ClientBase::xmppPing( const JID& to, EventHandler* eh )
913  {
914  const std::string& id = getID();
915  IQ iq( IQ::Get, to, id );
916  iq.addExtension( new Ping() );
917  m_dispatcher.registerEventHandler( eh, id );
918  send( iq, this, XMPPPing );
919  }
920 
921  bool ClientBase::handleIq( const IQ& iq )
922  {
923  const Ping* p = iq.findExtension<Ping>( ExtPing );
924  if( !p || iq.subtype() != IQ::Get )
925  return false;
926 
927  m_dispatcher.dispatch( Event( Event::PingPing, iq ) );
928  IQ re( IQ::Result, iq.from(), iq.id() );
929  send( re );
930 
931  return true;
932  }
933 
934  void ClientBase::handleIqID( const IQ& iq, int context )
935  {
936  if( context == XMPPPing )
937  m_dispatcher.dispatch( Event( ( iq.subtype() == IQ::Result ) ? Event::PingPong
938  : Event::PingError, iq ),
939  iq.id(), true );
940  else
941  handleIqIDForward( iq, context );
942  }
943 
944  const std::string ClientBase::getID()
945  {
946  char r[21+1];
947  sprintf( r, "uid:%08x:%08x", m_uniqueBaseId, m_nextId.increment() );
948  std::string ret( r, 21 );
949  return ret;
950  }
951 
952  bool ClientBase::checkStreamVersion( const std::string& version )
953  {
954  if( version.empty() )
955  return false;
956 
957  int major = 0;
958  int minor = 0;
959  int myMajor = atoi( XMPP_STREAM_VERSION_MAJOR.c_str() );
960 
961  size_t dot = version.find( '.' );
962  if( !version.empty() && dot && dot != std::string::npos )
963  {
964  major = atoi( version.substr( 0, dot ).c_str() );
965  minor = atoi( version.substr( dot ).c_str() );
966  }
967 
968  return myMajor >= major;
969  }
970 
972  {
973  if( m_connection )
974  {
975  delete m_connection;
976  }
977  m_connection = cb;
978  m_customConnection = true;
979  }
980 
982  {
983  if( m_encryption )
984  {
985  delete m_encryption;
986  }
987  m_encryption = tb;
988  }
989 
991  {
992  if( m_compression )
993  {
994  delete m_compression;
995  }
996  m_compression = cb;
997  }
998 
999  void ClientBase::handleStreamError( Tag* tag )
1000  {
1002  const TagList& c = tag->children();
1003  TagList::const_iterator it = c.begin();
1004  for( ; it != c.end(); ++it )
1005  {
1006  const std::string& name = (*it)->name();
1007  if( name == "bad-format" )
1008  err = StreamErrorBadFormat;
1009  else if( name == "bad-namespace-prefix" )
1011  else if( name == "conflict" )
1012  err = StreamErrorConflict;
1013  else if( name == "connection-timeout" )
1015  else if( name == "host-gone" )
1016  err = StreamErrorHostGone;
1017  else if( name == "host-unknown" )
1018  err = StreamErrorHostUnknown;
1019  else if( name == "improper-addressing" )
1021  else if( name == "internal-server-error" )
1023  else if( name == "invalid-from" )
1024  err = StreamErrorInvalidFrom;
1025  else if( name == "invalid-id" )
1026  err = StreamErrorInvalidId;
1027  else if( name == "invalid-namespace" )
1029  else if( name == "invalid-xml" )
1030  err = StreamErrorInvalidXml;
1031  else if( name == "not-authorized" )
1033  else if( name == "policy-violation" )
1035  else if( name == "remote-connection-failed" )
1037  else if( name == "resource-constraint" )
1039  else if( name == "restricted-xml" )
1041  else if( name == "see-other-host" )
1042  {
1044  m_streamErrorCData = tag->findChild( "see-other-host" )->cdata();
1045  }
1046  else if( name == "system-shutdown" )
1048  else if( name == "undefined-condition" )
1050  else if( name == "unsupported-encoding" )
1052  else if( name == "unsupported-stanza-type" )
1054  else if( name == "unsupported-version" )
1056  else if( name == "xml-not-well-formed" )
1058  else if( name == "text" )
1059  {
1060  const std::string& lang = (*it)->findAttribute( "xml:lang" );
1061  if( !lang.empty() )
1062  m_streamErrorText[lang] = (*it)->cdata();
1063  else
1064  m_streamErrorText["default"] = (*it)->cdata();
1065  }
1066  else
1067  m_streamErrorAppCondition = (*it);
1068 
1069  if( err != StreamErrorUndefined && (*it)->hasAttribute( XMLNS, XMLNS_XMPP_STREAM ) )
1070  m_streamError = err;
1071  }
1072  }
1073 
1074  const std::string& ClientBase::streamErrorText( const std::string& lang ) const
1075  {
1076  StringMap::const_iterator it = m_streamErrorText.find( lang );
1077  return ( it != m_streamErrorText.end() ) ? (*it).second : EmptyString;
1078  }
1079 
1081  {
1082  if( types & Message::Chat || types == 0 )
1083  m_messageSessionHandlerChat = msh;
1084 
1085  if( types & Message::Normal || types == 0 )
1086  m_messageSessionHandlerNormal = msh;
1087 
1088  if( types & Message::Groupchat || types == 0 )
1089  m_messageSessionHandlerGroupchat = msh;
1090 
1091  if( types & Message::Headline || types == 0 )
1092  m_messageSessionHandlerHeadline = msh;
1093  }
1094 
1096  {
1097  if( ph )
1098  m_presenceHandlers.push_back( ph );
1099  }
1100 
1102  {
1103  if( ph )
1104  m_presenceHandlers.remove( ph );
1105  }
1106 
1108  {
1109  if( ph && jid )
1110  {
1111  JidPresHandlerStruct jph;
1112  jph.jid = new JID( jid.bare() );
1113  jph.ph = ph;
1114  m_presenceJidHandlers.push_back( jph );
1115  }
1116  }
1117 
1119  {
1120  PresenceJidHandlerList::iterator t;
1121  PresenceJidHandlerList::iterator it = m_presenceJidHandlers.begin();
1122  while( it != m_presenceJidHandlers.end() )
1123  {
1124  t = it;
1125  ++it;
1126  if( ( !ph || (*t).ph == ph ) && (*t).jid->bare() == jid.bare() )
1127  {
1128  delete (*t).jid;
1129  m_presenceJidHandlers.erase( t );
1130  }
1131  }
1132  }
1133 
1135  {
1136  IqTrackMap::iterator t;
1137  m_iqHandlerMapMutex.lock();
1138  IqTrackMap::iterator it = m_iqIDHandlers.begin();
1139  while( it != m_iqIDHandlers.end() )
1140  {
1141  t = it;
1142  ++it;
1143  if( ih == (*t).second.ih )
1144  m_iqIDHandlers.erase( t );
1145  }
1146  m_iqHandlerMapMutex.unlock();
1147  }
1148 
1149  void ClientBase::registerIqHandler( IqHandler* ih, int exttype )
1150  {
1151  if( !ih )
1152  return;
1153 
1154  util::MutexGuard m( m_iqExtHandlerMapMutex );
1155  typedef IqHandlerMap::const_iterator IQci;
1156  std::pair<IQci, IQci> g = m_iqExtHandlers.equal_range( exttype );
1157  for( IQci it = g.first; it != g.second; ++it )
1158  {
1159  if( (*it).second == ih )
1160  return;
1161  }
1162 
1163  m_iqExtHandlers.insert( std::make_pair( exttype, ih ) );
1164  }
1165 
1166  void ClientBase::removeIqHandler( IqHandler* ih, int exttype )
1167  {
1168  if( !ih )
1169  return;
1170 
1171  util::MutexGuard m( m_iqExtHandlerMapMutex );
1172  typedef IqHandlerMap::iterator IQi;
1173  std::pair<IQi, IQi> g = m_iqExtHandlers.equal_range( exttype );
1174  IQi it2;
1175  IQi it = g.first;
1176  while( it != g.second )
1177  {
1178  it2 = it++;
1179  if( (*it2).second == ih )
1180  m_iqExtHandlers.erase( it2 );
1181  }
1182  }
1183 
1185  {
1186  if( session )
1187  m_messageSessions.push_back( session );
1188  }
1189 
1191  {
1192  if( !session )
1193  return;
1194 
1195  MessageSessionList::iterator it = std::find( m_messageSessions.begin(),
1196  m_messageSessions.end(),
1197  session );
1198  if( it != m_messageSessions.end() )
1199  {
1200  delete (*it);
1201  m_messageSessions.erase( it );
1202  }
1203  }
1204 
1206  {
1207  if( mh )
1208  m_messageHandlers.push_back( mh );
1209  }
1210 
1212  {
1213  if( mh )
1214  m_messageHandlers.remove( mh );
1215  }
1216 
1218  {
1219  if( sh )
1220  m_subscriptionHandlers.push_back( sh );
1221  }
1222 
1224  {
1225  if( sh )
1226  m_subscriptionHandlers.remove( sh );
1227  }
1228 
1229  void ClientBase::registerTagHandler( TagHandler* th, const std::string& tag, const std::string& xmlns )
1230  {
1231  if( th && !tag.empty() )
1232  {
1233  TagHandlerStruct ths;
1234  ths.tag = tag;
1235  ths.xmlns = xmlns;
1236  ths.th = th;
1237  m_tagHandlers.push_back( ths );
1238  }
1239  }
1240 
1241  void ClientBase::removeTagHandler( TagHandler* th, const std::string& tag, const std::string& xmlns )
1242  {
1243  if( th )
1244  {
1245  TagHandlerList::iterator it = m_tagHandlers.begin();
1246  for( ; it != m_tagHandlers.end(); ++it )
1247  {
1248  if( (*it).th == th && (*it).tag == tag && (*it).xmlns == xmlns )
1249  m_tagHandlers.erase( it );
1250  }
1251  }
1252  }
1253 
1255  {
1256  if( sh )
1257  m_statisticsHandler = sh;
1258  }
1259 
1261  {
1262  m_statisticsHandler = 0;
1263  }
1264 
1266  {
1267  if( mih )
1268  {
1269  m_mucInvitationHandler = mih;
1271  }
1272  }
1273 
1275  {
1276  m_mucInvitationHandler = 0;
1278  }
1279 
1281  {
1282  if( cl )
1283  m_connectionListeners.push_back( cl );
1284  }
1285 
1287  {
1288  if( cl )
1289  m_connectionListeners.remove( cl );
1290  }
1291 
1293  {
1294  util::ForEach( m_connectionListeners, &ConnectionListener::onConnect );
1295  }
1296 
1297  void ClientBase::notifyOnDisconnect( ConnectionError e )
1298  {
1299  util::ForEach( m_connectionListeners, &ConnectionListener::onDisconnect, e );
1300  init();
1301  }
1302 
1304  {
1305  ConnectionListenerList::const_iterator it = m_connectionListeners.begin();
1306  for( ; it != m_connectionListeners.end() && (*it)->onTLSConnect( info ); ++it )
1307  ;
1308  return m_stats.encryption = ( it == m_connectionListeners.end() );
1309  }
1310 
1312  {
1313  util::ForEach( m_connectionListeners, &ConnectionListener::onResourceBindError, error );
1314  }
1315 
1316  void ClientBase::notifyOnResourceBind( const std::string& resource )
1317  {
1318  util::ForEach( m_connectionListeners, &ConnectionListener::onResourceBind, resource );
1319  }
1320 
1322  {
1323  util::ForEach( m_connectionListeners, &ConnectionListener::onSessionCreateError, error );
1324  }
1325 
1327  {
1328  util::ForEach( m_connectionListeners, &ConnectionListener::onStreamEvent, event );
1329  }
1330 
1331  void ClientBase::notifyPresenceHandlers( Presence& pres )
1332  {
1333  bool match = false;
1334  PresenceJidHandlerList::const_iterator t;
1335  PresenceJidHandlerList::const_iterator itj = m_presenceJidHandlers.begin();
1336  while( itj != m_presenceJidHandlers.end() )
1337  {
1338  t = itj++;
1339  if( (*t).jid->bare() == pres.from().bare() && (*t).ph )
1340  {
1341  (*t).ph->handlePresence( pres );
1342  match = true;
1343  }
1344  }
1345  if( match )
1346  return;
1347 
1348  // FIXME remove this for() for 1.1:
1349  PresenceHandlerList::const_iterator it = m_presenceHandlers.begin();
1350  for( ; it != m_presenceHandlers.end(); ++it )
1351  {
1352  (*it)->handlePresence( pres );
1353  }
1354  // FIXME and reinstantiate this:
1355 // util::ForEach( m_presenceHandlers, &PresenceHandler::handlePresence, pres );
1356  }
1357 
1358  void ClientBase::notifySubscriptionHandlers( Subscription& s10n )
1359  {
1360  // FIXME remove this for() for 1.1:
1361  SubscriptionHandlerList::const_iterator it = m_subscriptionHandlers.begin();
1362  for( ; it != m_subscriptionHandlers.end(); ++it )
1363  {
1364  (*it)->handleSubscription( s10n );
1365  }
1366  // FIXME and reinstantiate this:
1367 // util::ForEach( m_subscriptionHandlers, &SubscriptionHandler::handleSubscription, s10n );
1368  }
1369 
1370  void ClientBase::notifyIqHandlers( IQ& iq )
1371  {
1372  m_iqHandlerMapMutex.lock();
1373  IqTrackMap::iterator it_id = m_iqIDHandlers.find( iq.id() );
1374  bool haveIdHandler = ( it_id != m_iqIDHandlers.end() );
1375  m_iqHandlerMapMutex.unlock();
1376  if( haveIdHandler && ( iq.subtype() == IQ::Result || iq.subtype() == IQ::Error ) )
1377  {
1378  (*it_id).second.ih->handleIqID( iq, (*it_id).second.context );
1379  if( (*it_id).second.del )
1380  delete (*it_id).second.ih;
1381  m_iqHandlerMapMutex.lock();
1382  m_iqIDHandlers.erase( it_id );
1383  m_iqHandlerMapMutex.unlock();
1384  return;
1385  }
1386 
1387  if( iq.extensions().empty() )
1388  {
1389  if ( iq.subtype() == IQ::Get || iq.subtype() == IQ::Set )
1390  {
1391  IQ re( IQ::Error, iq.from(), iq.id() );
1392  re.addExtension( new Error( StanzaErrorTypeCancel, StanzaErrorFeatureNotImplemented ) );
1393  send( re );
1394  }
1395  return;
1396  }
1397 
1398  bool handled = false;
1399 
1400  // FIXME remove for 1.1
1401 // typedef IqHandlerMapXmlns::const_iterator IQciXmlns
1402 // Tag *tag = iq.tag()->xmlns();
1403 // std::pair<IQciXmlns, IQciXmlns> g = m_iqNSHandlers.equal_range( tag->xmlns() );
1404 // for( IQciXmlns it = g.first; it != g.second; ++it )
1405 // {
1406 // if( (*it).second->handleIq( iq ) )
1407 // res = true;
1408 // }
1409 // delete tag;
1410 
1411  m_iqExtHandlerMapMutex.lock();
1412  typedef IqHandlerMap::const_iterator IQci;
1413  const StanzaExtensionList& sel = iq.extensions();
1414  StanzaExtensionList::const_iterator itse = sel.begin();
1415  for( ; !handled && itse != sel.end(); ++itse )
1416  {
1417  std::pair<IQci, IQci> g = m_iqExtHandlers.equal_range( (*itse)->extensionType() );
1418  for( IQci it = g.first; !handled && it != g.second; ++it )
1419  {
1420  if( (*it).second->handleIq( iq ) )
1421  handled = true;
1422  }
1423  }
1424  m_iqExtHandlerMapMutex.unlock();
1425 
1426  if( !handled && ( iq.subtype() == IQ::Get || iq.subtype() == IQ::Set ) )
1427  {
1428  IQ re( IQ::Error, iq.from(), iq.id() );
1429  re.addExtension( new Error( StanzaErrorTypeCancel, StanzaErrorServiceUnavailable ) );
1430  send( re );
1431  }
1432  }
1433 
1434  void ClientBase::notifyMessageHandlers( Message& msg )
1435  {
1436  if( m_mucInvitationHandler )
1437  {
1438  const MUCRoom::MUCUser* mu = msg.findExtension<MUCRoom::MUCUser>( ExtMUCUser );
1439  if( mu && mu->operation() != MUCRoom::OpInviteTo )
1440  {
1441 
1442  m_mucInvitationHandler->handleMUCInvitation( msg.from(),
1443  mu->jid() ? JID( *(mu->jid()) ) : JID(),
1444  mu->reason() ? *(mu->reason()) : EmptyString,
1445  msg.body(),
1446  mu->password() ? *(mu->password()) : EmptyString,
1447  mu->continued(),
1448  mu->thread() ? *(mu->thread()) : EmptyString );
1449  return;
1450  }
1451  }
1452 
1453  MessageSessionList::const_iterator it1 = m_messageSessions.begin();
1454  for( ; it1 != m_messageSessions.end(); ++it1 )
1455  {
1456  if( (*it1)->target().full() == msg.from().full() &&
1457  ( msg.thread().empty()
1458  || (*it1)->threadID() == msg.thread()
1459  || (*it1)->honorThreadID() ) &&
1460 // FIXME don't use '== 0' here
1461  ( (*it1)->types() & msg.subtype() || (*it1)->types() == 0 ) )
1462  {
1463  (*it1)->handleMessage( msg );
1464  return;
1465  }
1466  }
1467 
1468  it1 = m_messageSessions.begin();
1469  for( ; it1 != m_messageSessions.end(); ++it1 )
1470  {
1471  if( (*it1)->target().bare() == msg.from().bare() &&
1472  ( msg.thread().empty()
1473  || (*it1)->threadID() == msg.thread()
1474  || (*it1)->honorThreadID() ) &&
1475 // FIXME don't use '== 0' here
1476  ( (*it1)->types() & msg.subtype() || (*it1)->types() == 0 ) )
1477  {
1478  (*it1)->handleMessage( msg );
1479  return;
1480  }
1481  }
1482 
1483  MessageSessionHandler* msHandler = 0;
1484 
1485  switch( msg.subtype() )
1486  {
1487  case Message::Chat:
1488  msHandler = m_messageSessionHandlerChat;
1489  break;
1490  case Message::Normal:
1491  msHandler = m_messageSessionHandlerNormal;
1492  break;
1493  case Message::Groupchat:
1494  msHandler = m_messageSessionHandlerGroupchat;
1495  break;
1496  case Message::Headline:
1497  msHandler = m_messageSessionHandlerHeadline;
1498  break;
1499  default:
1500  break;
1501  }
1502 
1503  if( msHandler )
1504  {
1505  if( msg.subtype() == Message::Chat && msg.body().empty() )
1506  return; // don't want a new MS for empty messages
1507  MessageSession* session = new MessageSession( this, msg.from(), true, msg.subtype() );
1508  msHandler->handleMessageSession( session );
1509  session->handleMessage( msg );
1510  }
1511  else
1512  {
1513  // FIXME remove this for() for 1.1:
1514  MessageHandlerList::const_iterator it = m_messageHandlers.begin();
1515  for( ; it != m_messageHandlers.end(); ++it )
1516  {
1517  (*it)->handleMessage( msg );
1518  }
1519  // FIXME and reinstantiate this:
1520 // util::ForEach( m_messageHandlers, &MessageHandler::handleMessage, msg ); // FIXME remove for 1.1
1521  }
1522  }
1523 
1524  void ClientBase::notifyTagHandlers( Tag* tag )
1525  {
1526  TagHandlerList::const_iterator it = m_tagHandlers.begin();
1527  for( ; it != m_tagHandlers.end(); ++it )
1528  {
1529  if( (*it).tag == tag->name() && tag->hasAttribute( XMLNS, (*it).xmlns ) )
1530  (*it).th->handleTag( tag );
1531  }
1532  }
1533 
1535  {
1536  if( !se )
1537  return;
1538 
1540  m_presenceExtensions.push_back( se );
1541  }
1542 
1544  {
1545  StanzaExtensionList::iterator it = m_presenceExtensions.begin();
1546  for( ; it != m_presenceExtensions.end(); ++it )
1547  {
1548  if( (*it)->extensionType() == type )
1549  {
1550  delete (*it);
1551  m_presenceExtensions.erase( it );
1552  return true;
1553  }
1554  }
1555 
1556  return false;
1557  }
1558 
1559  CompressionBase* ClientBase::getDefaultCompression()
1560  {
1561  if( !m_compress )
1562  return 0;
1563 
1564 #ifdef HAVE_ZLIB
1565  CompressionBase* cmp = new CompressionZlib( this );
1566  if( cmp->init() )
1567  return cmp;
1568 
1569  delete cmp;
1570 #endif
1571  return 0;
1572  }
1573 
1574  TLSBase* ClientBase::getDefaultEncryption()
1575  {
1576  if( m_tls == TLSDisabled || !hasTls() )
1577  return 0;
1578 
1579  TLSDefault* tls = new TLSDefault( this, m_server );
1580  if( tls->init( m_clientKey, m_clientCerts, m_cacerts ) )
1581  return tls;
1582  else
1583  {
1584  delete tls;
1585  return 0;
1586  }
1587  }
1588 
1589 }