gloox  1.0.10
clientbase.cpp
1 /*
2  Copyright (c) 2005-2013 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 "sha.h"
44 #include "subscription.h"
45 #include "subscriptionhandler.h"
46 #include "tag.h"
47 #include "taghandler.h"
48 #include "tlsbase.h"
49 #include "tlsdefault.h"
50 #include "prep.h"
51 #include "util.h"
52 
53 #include <cstdlib>
54 #include <string>
55 #include <map>
56 #include <list>
57 #include <algorithm>
58 #include <cmath>
59 #include <ctime>
60 #include <cstdio>
61 
62 #include <string.h> // for memset()
63 
64 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
65 #include <tchar.h>
66 # ifdef __MINGW32__
67 # ifndef SecureZeroMemory
68 # define SecureZeroMemory(p,s) RtlFillMemory((p),(s),0)
69 # endif
70 # endif
71 #endif
72 
73 namespace gloox
74 {
75 
76  // ---- ClientBase::Ping ----
77  ClientBase::Ping::Ping()
78  : StanzaExtension( ExtPing )
79  {
80  }
81 
82  ClientBase::Ping::~Ping()
83  {
84  }
85 
86  const std::string& ClientBase::Ping::filterString() const
87  {
88  static const std::string filter = "/iq/ping[@xmlns='" + XMLNS_XMPP_PING + "']";
89  return filter;
90  }
91  // ---- ~ClientBase::Ping ----
92 
93  // ---- ClientBase ----
94  ClientBase::ClientBase( const std::string& ns, const std::string& server, int port )
95  : m_connection( 0 ), m_encryption( 0 ), m_compression( 0 ), m_disco( 0 ), m_namespace( ns ),
96  m_xmllang( "en" ), m_server( server ), m_compressionActive( false ), m_encryptionActive( false ),
97  m_compress( true ), m_authed( false ), m_block( false ), m_sasl( true ), m_tls( TLSOptional ), m_port( port ),
98  m_availableSaslMechs( SaslMechAll ), m_smContext( CtxSMInvalid ), m_smHandled( 0 ),
99  m_statisticsHandler( 0 ), m_mucInvitationHandler( 0 ),
100  m_messageSessionHandlerChat( 0 ), m_messageSessionHandlerGroupchat( 0 ),
101  m_messageSessionHandlerHeadline( 0 ), m_messageSessionHandlerNormal( 0 ),
102  m_parser( this ), m_seFactory( 0 ), m_authError( AuthErrorUndefined ),
103  m_streamError( StreamErrorUndefined ), m_streamErrorAppCondition( 0 ),
104  m_selectedSaslMech( SaslMechNone ), m_customConnection( false ),
105  m_uniqueBaseId( (unsigned int)( ( (unsigned long long)time( 0 ) & 0xFFFF ) << 16 ) | ( ( (unsigned long long) & m_nextId ) & 0xFFFF ) ),
106  m_smSent( 0 )
107  {
108  init();
109  }
110 
111  ClientBase::ClientBase( const std::string& ns, const std::string& password,
112  const std::string& server, int port )
113  : m_connection( 0 ), m_encryption( 0 ), m_compression( 0 ), m_disco( 0 ), m_namespace( ns ),
114  m_password( password ),
115  m_xmllang( "en" ), m_server( server ), m_compressionActive( false ), m_encryptionActive( false ),
116  m_compress( true ), m_authed( false ), m_block( false ), m_sasl( true ), m_tls( TLSOptional ),
117  m_port( port ), m_availableSaslMechs( SaslMechAll ), m_smContext( CtxSMInvalid ), m_smHandled( 0 ),
118  m_statisticsHandler( 0 ), m_mucInvitationHandler( 0 ),
119  m_messageSessionHandlerChat( 0 ), m_messageSessionHandlerGroupchat( 0 ),
120  m_messageSessionHandlerHeadline( 0 ), m_messageSessionHandlerNormal( 0 ),
121  m_parser( this ), m_seFactory( 0 ), m_authError( AuthErrorUndefined ),
122  m_streamError( StreamErrorUndefined ), m_streamErrorAppCondition( 0 ),
123  m_selectedSaslMech( SaslMechNone ), m_customConnection( false ),
124  m_uniqueBaseId( (unsigned int)( ( (unsigned long long)time( 0 ) & 0xFFFF ) << 16 ) | ( ( (unsigned long long) & m_nextId ) & 0xFFFF ) ),
125  m_smSent( 0 )
126  {
127  init();
128  }
129 
130  void ClientBase::init()
131  {
132  srand( time( 0 ) );
133 
134  if( !m_disco )
135  {
136  m_disco = new Disco( this );
137  m_disco->setVersion( "based on gloox", GLOOX_VERSION );
139  }
140 
141  registerStanzaExtension( new Error() );
142  registerStanzaExtension( new Ping() );
143  registerIqHandler( this, ExtPing );
144 
145  m_streamError = StreamErrorUndefined;
146  m_block = false;
147  memset( &m_stats, 0, sizeof( m_stats ) );
148  cleanup();
149  }
150 
152  {
153  m_iqHandlerMapMutex.lock();
154  m_iqIDHandlers.clear();
155  m_iqHandlerMapMutex.unlock();
156 
157  m_iqExtHandlerMapMutex.lock();
158  m_iqExtHandlers.clear();
159  m_iqExtHandlerMapMutex.unlock();
160 
162  util::clearMap( m_smQueue );
163 
164  setConnectionImpl( 0 );
165  setEncryptionImpl( 0 );
166  setCompressionImpl( 0 );
167  delete m_seFactory;
168  m_seFactory = 0; // to avoid usage when Disco gets deleted below
169  delete m_disco;
170  m_disco = 0;
171 
172  util::clearList( m_messageSessions );
173 
174  PresenceJidHandlerList::const_iterator it1 = m_presenceJidHandlers.begin();
175  for( ; it1 != m_presenceJidHandlers.end(); ++it1 )
176  delete (*it1).jid;
177  }
178 
180  {
182  return ConnNotConnected;
183 
184  return m_connection->recv( timeout );
185  }
186 
187  bool ClientBase::connect( bool block )
188  {
189  if( m_server.empty() )
190  return false;
191 
192  if( !m_connection )
193  m_connection = new ConnectionTCPClient( this, m_logInstance, m_server, m_port );
194 
196  return true;
197 
198  if( !m_encryption )
199  m_encryption = getDefaultEncryption();
200 
201  if( !m_compression )
202  m_compression = getDefaultCompression();
203 
204  m_logInstance.dbg( LogAreaClassClientbase, "This is gloox " + GLOOX_VERSION + ", connecting to "
205  + m_server + ( ( m_customConnection )?( " using a custom connection" ):( m_port > 0 ? ( ":" + util::int2string( m_port ) ) : EmptyString ) ) + "..." );
206  m_block = block;
208  if( ret != ConnNoError )
209  return false;
210 
211  if( m_block )
213 
214  return true;
215  }
216 
218  {
219  if( !tag )
220  {
221  logInstance().dbg( LogAreaClassClientbase, "stream closed" );
223  return;
224  }
225 
226  logInstance().dbg( LogAreaXmlIncoming, tag->xml() );
227  ++m_stats.totalStanzasReceived;
228 
229  if( tag->name() == "stream" && tag->xmlns() == XMLNS_STREAM )
230  {
231  const std::string& version = tag->findAttribute( "version" );
232  if( !checkStreamVersion( version ) )
233  {
234  logInstance().dbg( LogAreaClassClientbase, "This server is not XMPP-compliant"
235  " (it does not send a 'version' attribute). Please fix it or try another one.\n" );
237  return;
238  }
239 
240  m_sid = tag->findAttribute( "id" );
241  handleStartNode( tag );
242  }
243  else if( tag->name() == "error" && tag->xmlns() == XMLNS_STREAM )
244  {
245  handleStreamError( tag );
247  }
248  else
249  {
250  if( !handleNormalNode( tag ) )
251  {
252  if( tag->xmlns().empty() || tag->xmlns() == XMLNS_CLIENT )
253  {
254  if( tag->name() == "iq" )
255  {
256  IQ iq( tag );
257  m_seFactory->addExtensions( iq, tag );
258  if( iq.hasEmbeddedStanza() )
259  m_seFactory->addExtensions( *iq.embeddedStanza(), iq.embeddedTag() );
260  notifyIqHandlers( iq );
261  ++m_stats.iqStanzasReceived;
262  if( m_smContext >= CtxSMEnabled )
263  ++m_smHandled;
264  }
265  else if( tag->name() == "message" )
266  {
267  Message msg( tag );
268  m_seFactory->addExtensions( msg, tag );
269  if( msg.hasEmbeddedStanza() )
270  m_seFactory->addExtensions( *msg.embeddedStanza(), msg.embeddedTag() );
271  notifyMessageHandlers( msg );
272  ++m_stats.messageStanzasReceived;
273  if( m_smContext >= CtxSMEnabled )
274  ++m_smHandled;
275  }
276  else if( tag->name() == "presence" )
277  {
278  const std::string& type = tag->findAttribute( TYPE );
279  if( type == "subscribe" || type == "unsubscribe"
280  || type == "subscribed" || type == "unsubscribed" )
281  {
282  Subscription sub( tag );
283  m_seFactory->addExtensions( sub, tag );
284  if( sub.hasEmbeddedStanza() )
285  m_seFactory->addExtensions( *sub.embeddedStanza(), sub.embeddedTag() );
286  notifySubscriptionHandlers( sub );
287  ++m_stats.s10nStanzasReceived;
288  }
289  else
290  {
291  Presence pres( tag );
292  m_seFactory->addExtensions( pres, tag );
293  if( pres.hasEmbeddedStanza() )
294  m_seFactory->addExtensions( *pres.embeddedStanza(), pres.embeddedTag() );
295  notifyPresenceHandlers( pres );
296  ++m_stats.presenceStanzasReceived;
297  }
298  if( m_smContext >= CtxSMEnabled )
299  ++m_smHandled;
300  }
301  else
302  m_logInstance.err( LogAreaClassClientbase, "Invalid stanza received: " + tag->name() );
303  }
304  else
305  {
306  notifyTagHandlers( tag );
307  }
308  }
309  }
310 
311  if( m_statisticsHandler )
312  m_statisticsHandler->handleStatistics( getStatistics() );
313  }
314 
315  void ClientBase::handleCompressedData( const std::string& data )
316  {
318  m_encryption->encrypt( data );
319  else if( m_connection )
320  m_connection->send( data );
321  else
322  m_logInstance.err( LogAreaClassClientbase, "Compression finished, but chain broken" );
323  }
324 
325  void ClientBase::handleDecompressedData( const std::string& data )
326  {
327  parse( data );
328  }
329 
330  void ClientBase::handleEncryptedData( const TLSBase* /*base*/, const std::string& data )
331  {
332  if( m_connection )
333  m_connection->send( data );
334  else
335  m_logInstance.err( LogAreaClassClientbase, "Encryption finished, but chain broken" );
336  }
337 
338  void ClientBase::handleDecryptedData( const TLSBase* /*base*/, const std::string& data )
339  {
341  m_compression->decompress( data );
342  else
343  parse( data );
344  }
345 
346  void ClientBase::handleHandshakeResult( const TLSBase* /*base*/, bool success, CertInfo &certinfo )
347  {
348  if( success )
349  {
350  if( !notifyOnTLSConnect( certinfo ) )
351  {
352  logInstance().err( LogAreaClassClientbase, "Server's certificate rejected!" );
354  }
355  else
356  {
357  logInstance().dbg( LogAreaClassClientbase, "connection encryption active" );
358  header();
359  }
360  }
361  else
362  {
363  logInstance().err( LogAreaClassClientbase, "TLS handshake failed!" );
365  }
366  }
367 
368  void ClientBase::handleReceivedData( const ConnectionBase* /*connection*/, const std::string& data )
369  {
371  m_encryption->decrypt( data );
372  else if( m_compression && m_compressionActive )
373  m_compression->decompress( data );
374  else
375  parse( data );
376  }
377 
378  void ClientBase::handleConnect( const ConnectionBase* /*connection*/ )
379  {
380  header();
381  }
382 
383  void ClientBase::handleDisconnect( const ConnectionBase* /*connection*/, ConnectionError reason )
384  {
385  if( m_connection )
387 
388  if( m_encryption )
390 
391  if( m_compression )
393 
394  m_encryptionActive = false;
395  m_compressionActive = false;
396 
397  notifyOnDisconnect( reason );
398  }
399 
401  {
403  return;
404 
405  if( reason != ConnTlsFailed )
406  send( "</stream:stream>" );
407 
410 
411  if( m_encryption )
413 
414  if( m_compression )
416 
417  m_encryptionActive = false;
418  m_compressionActive = false;
419  m_smSent = 0;
420 
421  notifyOnDisconnect( reason );
422 
423 #ifdef CLIENTBASE_TEST
424  m_nextId.reset();
425 #endif
426  }
427 
428  void ClientBase::parse( const std::string& data )
429  {
430  std::string copy = data;
431  int i = 0;
432  if( ( i = m_parser.feed( copy ) ) >= 0 )
433  {
434  std::string error = "parse error (at pos ";
435  error += util::int2string( i );
436  error += "): ";
437  m_logInstance.err( LogAreaClassClientbase, error + copy );
438  Tag* e = new Tag( "stream:error" );
439  new Tag( e, "restricted-xml", "xmlns", XMLNS_XMPP_STREAM );
440  send( e );
442  }
443  }
444 
446  {
447  std::string head = "<?xml version='1.0' ?>";
448  head += "<stream:stream to='" + m_jid.server() + "' xmlns='" + m_namespace + "' ";
449  head += "xmlns:stream='http://etherx.jabber.org/streams' xml:lang='" + m_xmllang + "' ";
450  head += "version='" + XMPP_STREAM_VERSION_MAJOR + "." + XMPP_STREAM_VERSION_MINOR + "'>";
451  send( head );
452  }
453 
455  {
456 #if defined( HAVE_GNUTLS ) || defined( HAVE_OPENSSL ) || defined( HAVE_WINTLS )
457  return true;
458 #else
459  return false;
460 #endif
461  }
462 
464  {
465  send( new Tag( "starttls", XMLNS, XMLNS_STREAM_TLS ) );
466  }
467 
468  void ClientBase::setServer( const std::string &server )
469  {
470  m_server = server;
471  if( m_connection )
472  m_connection->setServer( server );
473  }
474 
475  void ClientBase::setClientCert( const std::string& clientKey, const std::string& clientCerts )
476  {
477  m_clientKey = clientKey;
478  m_clientCerts = clientCerts;
479  }
480 
482  {
483  m_selectedSaslMech = type;
484 
485  Tag* a = new Tag( "auth", XMLNS, XMLNS_STREAM_SASL );
486 
487  switch( type )
488  {
490  case SaslMechScramSha1:
491  {
492  if( type == SaslMechScramSha1 )
493  {
494  if( ( m_availableSaslMechs & SaslMechScramSha1Plus ) != SaslMechScramSha1Plus )
495  m_gs2Header = "y,";
496  else
497  m_gs2Header = "n,";
498  a->addAttribute( "mechanism", "SCRAM-SHA-1" );
499  }
500  else // SaslMechScramSha1Plus
501  {
502  m_gs2Header = "p=tls-unique,";
503  a->addAttribute( "mechanism", "SCRAM-SHA-1-PLUS" );
504  }
505 
506  std::string t;
507  if( m_authzid && prep::saslprep( m_authzid.bare(), t ) )
508  m_gs2Header += "a=" + t;
509 
510  m_gs2Header += ",";
511 
512  m_clientFirstMessageBare = "n=";
513  if( !m_authcid.empty() && prep::saslprep( m_authcid, t ) )
514  m_clientFirstMessageBare += t;
515  else if( prep::saslprep( m_jid.username(), t ) )
516  m_clientFirstMessageBare += t;
517 
518  m_clientFirstMessageBare += ",r=" + getRandom();
519 
520  a->setCData( Base64::encode64( m_gs2Header + m_clientFirstMessageBare ) );
521  break;
522  }
523  case SaslMechDigestMd5:
524  a->addAttribute( "mechanism", "DIGEST-MD5" );
525  break;
526  case SaslMechPlain:
527  {
528  a->addAttribute( "mechanism", "PLAIN" );
529 
530  std::string tmp;
531  if( m_authzid )
532  tmp += m_authzid.bare();
533 
534  tmp += '\0';
535  if( !m_authcid.empty() )
536  tmp += m_authcid;
537  else
538  tmp += m_jid.username();
539  tmp += '\0';
540  tmp += m_password;
541  a->setCData( Base64::encode64( tmp ) );
542  break;
543  }
544  case SaslMechAnonymous:
545  a->addAttribute( "mechanism", "ANONYMOUS" );
546  break;
547  case SaslMechExternal:
548  a->addAttribute( "mechanism", "EXTERNAL" );
550  break;
551  case SaslMechGssapi:
552  {
553 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
554  a->addAttribute( "mechanism", "GSSAPI" );
555 // The client calls GSS_Init_sec_context, passing in 0 for
556 // input_context_handle (initially) and a targ_name equal to output_name
557 // from GSS_Import_Name called with input_name_type of
558 // GSS_C_NT_HOSTBASED_SERVICE and input_name_string of
559 // "service@hostname" where "service" is the service name specified in
560 // the protocol's profile, and "hostname" is the fully qualified host
561 // name of the server. The client then responds with the resulting
562 // output_token.
563  std::string token;
564  a->setCData( Base64::encode64( token ) );
565 // etc... see gssapi-sasl-draft.txt
566 #else
568  "SASL GSSAPI is not supported on this platform. You should never see this." );
569 #endif
570  break;
571  }
572  case SaslMechNTLM:
573  {
574 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
575  a->addAttribute( "mechanism", "NTLM" );
576  SEC_WINNT_AUTH_IDENTITY_W identity, *ident = 0;
577  memset( &identity, 0, sizeof( identity ) );
578 
579  WCHAR *usernameW = 0, *domainW = 0, *passwordW = 0;
580  int cchUsernameW = 0, cchDomainW = 0, cchPasswordW = 0;
581 
582  if( m_jid.username().length() > 0 )
583  {
584  // NOTE: The return values of MultiByteToWideChar will include room
585  // for the NUL character since we use -1 for the input length.
586 
587  cchUsernameW = ::MultiByteToWideChar( CP_UTF8, 0, m_jid.username().c_str(), -1, 0, 0 );
588  if( cchUsernameW > 0 )
589  {
590  usernameW = new WCHAR[cchUsernameW];
591  ::MultiByteToWideChar( CP_UTF8, 0, m_jid.username().c_str(), -1, usernameW, cchUsernameW );
592  // Guarantee its NUL terminated.
593  usernameW[cchUsernameW-1] = L'\0';
594  }
595  cchDomainW = ::MultiByteToWideChar( CP_UTF8, 0, m_ntlmDomain.c_str(), -1, 0, 0 );
596  if( cchDomainW > 0 )
597  {
598  domainW = new WCHAR[cchDomainW];
599  ::MultiByteToWideChar( CP_UTF8, 0, m_ntlmDomain.c_str(), -1, domainW, cchDomainW );
600  // Guarantee its NUL terminated.
601  domainW[cchDomainW-1] = L'\0';
602  }
603  cchPasswordW = ::MultiByteToWideChar( CP_UTF8, 0, m_password.c_str(), -1, 0, 0 );
604  if( cchPasswordW > 0 )
605  {
606  passwordW = new WCHAR[cchPasswordW];
607  ::MultiByteToWideChar( CP_UTF8, 0, m_password.c_str(), -1, passwordW, cchPasswordW );
608  // Guarantee its NUL terminated.
609  passwordW[cchPasswordW-1] = L'\0';
610  }
611  identity.User = (unsigned short*)usernameW;
612  identity.UserLength = (unsigned long)cchUsernameW-1;
613  identity.Domain = (unsigned short*)domainW;
614  identity.DomainLength = (unsigned long)cchDomainW-1;
615  identity.Password = (unsigned short*)passwordW;
616  identity.PasswordLength = (unsigned long)cchPasswordW-1;
617  identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
618  ident = &identity;
619  }
620 
621  AcquireCredentialsHandleW( 0, L"NTLM", SECPKG_CRED_OUTBOUND, 0, ident, 0, 0, &m_credHandle, 0 );
622 
623  if( usernameW != 0 )
624  {
625  delete[] usernameW;
626  usernameW = 0;
627  }
628  if( domainW != 0 )
629  {
630  delete[] domainW;
631  domainW = 0;
632  }
633  if( passwordW != 0 )
634  {
635  ::SecureZeroMemory( passwordW, cchPasswordW* sizeof( WCHAR ) );
636  delete[] passwordW;
637  passwordW = 0;
638  }
639 
640 #else
642  "SASL NTLM is not supported on this platform. You should never see this." );
643 #endif
644  break;
645  }
646  default:
647  break;
648  }
649 
650  send( a );
651  }
652 
653  std::string ClientBase::hmac( const std::string& key, const std::string& str )
654  {
655  SHA sha;
656  std::string key_ = key;
657  if( key_.length() > 64 )
658  {
659  sha.feed( key_ );
660  key_ = sha.binary();
661  sha.reset();
662  }
663  unsigned char ipad[65];
664  unsigned char opad[65];
665  memset( ipad, '\0', sizeof( ipad ) );
666  memset( opad, '\0', sizeof( opad ) );
667  memcpy( ipad, key_.c_str(), key_.length() );
668  memcpy( opad, key_.c_str(), key_.length() );
669  for( int i = 0; i < 64; i++ )
670  {
671  ipad[i] ^= 0x36;
672  opad[i] ^= 0x5c;
673  }
674  sha.feed( ipad, 64 );
675  sha.feed( str );
676  key_ = sha.binary();
677  sha.reset();
678  sha.feed( opad, 64 );
679  sha.feed( key_ );
680 
681  return sha.binary(); // hex() for testing
682  }
683 
684  std::string ClientBase::hi( const std::string& str, const std::string& salt, int iter )
685  {
686  unsigned char xored[20];
687  memset( xored, '\0', sizeof( xored ) );
688  std::string tmp = salt;
689  tmp.append( "\0\0\0\1", 4 );
690  for( int i = 0; i < iter; ++i )
691  {
692  tmp = hmac( str, tmp );
693  for( int j = 0; j < 20; ++j )
694  xored[j] ^= tmp.c_str()[j];
695  }
696  return std::string( (char*)xored, 20 );
697  }
698 
699  void ClientBase::processSASLChallenge( const std::string& challenge )
700  {
701  Tag* t = new Tag( "response", XMLNS, XMLNS_STREAM_SASL );
702 
703  const std::string& decoded = Base64::decode64( challenge );
704 
705  switch( m_selectedSaslMech )
706  {
708  case SaslMechScramSha1:
709  {
710  std::string snonce, salt, tmp;
711  int iter = 0;
712  std::string::size_type posn = decoded.find( "r=" );
713  std::string::size_type poss = decoded.find( "s=" );
714  std::string::size_type posi = decoded.find( "i=" );
715  if( posn == std::string::npos || poss == std::string::npos || posi == std::string::npos )
716  break;
717 
718  snonce = decoded.substr( posn + 2, poss - posn - 3 );
719  salt = Base64::decode64( decoded.substr( poss + 2, posi - poss - 3 ) );
720  tmp = decoded.substr( posi + 2, decoded.length() - posi - 2 );
721  iter = atoi( tmp.c_str() );
722 
723  if( !prep::saslprep( m_password, tmp ) )
724  break;
725 
726  std::string saltedPwd = hi( tmp, salt, iter );
727  std::string ck = hmac( saltedPwd, "Client Key" );
728  SHA sha;
729  sha.feed( ck );
730  std::string storedKey = sha.binary();
731 
732  if( m_selectedSaslMech == SaslMechScramSha1Plus )
733  tmp = "c=" + Base64::encode64( m_gs2Header + m_encryption->channelBinding() );
734  else
735  tmp = "c=biws";
736  tmp += ",r=" + snonce;
737 
738  std::string authMessage = m_clientFirstMessageBare + "," + decoded + "," + tmp; // client-final-message-without-proof
739  std::string clientSignature = hmac( storedKey, authMessage );
740  unsigned char clientProof[20]; // ck XOR clientSignature
741  memcpy( clientProof, ck.c_str(), 20 );
742  for( int i = 0; i < 20; ++i )
743  clientProof[i] ^= clientSignature.c_str()[i];
744  std::string serverKey = hmac( saltedPwd, "Server Key" );
745  m_serverSignature = hmac( serverKey, authMessage );
746 
747  tmp += ",p=";
748  tmp.append( Base64::encode64( std::string( (char*)clientProof, 20 ) ) );
749 
750  t->setCData( Base64::encode64( tmp ) );
751 
752  break;
753  }
754  case SaslMechDigestMd5:
755  {
756  if( !decoded.compare( 0, 7, "rspauth" ) )
757  break;
758 
759  std::string realm;
760  std::string::size_type end = 0;
761  std::string::size_type pos = decoded.find( "realm=" );
762  if( pos != std::string::npos )
763  {
764  end = decoded.find( '"', pos + 7 );
765  realm = decoded.substr( pos + 7, end - ( pos + 7 ) );
766  }
767  else
768  realm = m_jid.server();
769 
770  pos = decoded.find( "nonce=" );
771  if( pos == std::string::npos )
772  return;
773 
774  end = decoded.find( '"', pos + 7 );
775  while( decoded[end-1] == '\\' )
776  end = decoded.find( '"', end + 1 );
777  std::string nonce = decoded.substr( pos + 7, end - ( pos + 7 ) );
778 
779  std::string cnonce = getRandom();
780 
781  MD5 md5;
782  md5.feed( m_jid.username() );
783  md5.feed( ":" );
784  md5.feed( realm );
785  md5.feed( ":" );
786  md5.feed( m_password );
787  md5.finalize();
788  const std::string& a1_h = md5.binary();
789  md5.reset();
790  md5.feed( a1_h );
791  md5.feed( ":" );
792  md5.feed( nonce );
793  md5.feed( ":" );
794  md5.feed( cnonce );
795  md5.finalize();
796  const std::string& a1 = md5.hex();
797  md5.reset();
798  md5.feed( "AUTHENTICATE:xmpp/" );
799  md5.feed( m_jid.server() );
800  md5.finalize();
801  const std::string& a2 = md5.hex();
802  md5.reset();
803  md5.feed( a1 );
804  md5.feed( ":" );
805  md5.feed( nonce );
806  md5.feed( ":00000001:" );
807  md5.feed( cnonce );
808  md5.feed( ":auth:" );
809  md5.feed( a2 );
810  md5.finalize();
811 
812  std::string response = "username=\"";
813  response += m_jid.username();
814  response += "\",realm=\"";
815  response += realm;
816  response += "\",nonce=\"";
817  response += nonce;
818  response += "\",cnonce=\"";
819  response += cnonce;
820  response += "\",nc=00000001,qop=auth,digest-uri=\"xmpp/";
821  response += m_jid.server();
822  response += "\",response=";
823  response += md5.hex();
824  response += ",charset=utf-8";
825 
826  if( m_authzid )
827  response += ",authzid=" + m_authzid.bare();
828 
829  t->setCData( Base64::encode64( response ) );
830 
831  break;
832  }
833  case SaslMechGssapi:
834 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
835  // see gssapi-sasl-draft.txt
836 #else
837  m_logInstance.err( LogAreaClassClientbase,
838  "Huh, received GSSAPI challenge?! This should have never happened!" );
839 #endif
840  break;
841  case SaslMechNTLM:
842  {
843 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
844  bool type1 = ( decoded.length() < 7 ) ? true : false;
845 
846  SecBuffer bufferIn = { type1 ? 0 : (unsigned long)decoded.length(),
847  SECBUFFER_TOKEN,
848  (void*)decoded.c_str() };
849  SecBufferDesc secIn = { 0, 1, &bufferIn };
850 
851  char buffer[4096];
852 
853  SecBuffer bufferOut = { sizeof( buffer ), SECBUFFER_TOKEN, buffer };
854  SecBufferDesc secOut = { 0, 1, &bufferOut };
855 
856  TimeStamp timestamp;
857  unsigned long contextAttr;
858 
859  SECURITY_STATUS status = InitializeSecurityContext( &m_credHandle, type1 ? 0 : &m_ctxtHandle,
860  0, ISC_REQ_MUTUAL_AUTH, 0, 0, &secIn, 0,
861  &m_ctxtHandle, &secOut, &contextAttr,
862  &timestamp );
863  std::string response;
864  if( SUCCEEDED( status ) )
865  {
866  response = std::string( (const char *)bufferOut.pvBuffer, bufferOut.cbBuffer );
867  }
868  else
869  {
871  "InitializeSecurityContext() failed, return value "
872  + util::int2string( status ) );
873  }
874 
875  t->setCData( Base64::encode64( response ) );
876 #else
877  m_logInstance.err( LogAreaClassClientbase,
878  "Huh, received NTLM challenge?! This should have never happened!" );
879 #endif
880  break;
881  }
882 
883  default:
884  // should never happen.
885  break;
886  }
887 
888  send( t );
889  }
890 
892  {
893  if( tag->hasChild( "aborted" ) )
894  m_authError = SaslAborted;
895  else if( tag->hasChild( "incorrect-encoding" ) )
896  m_authError = SaslIncorrectEncoding;
897  else if( tag->hasChild( "invalid-authzid" ) )
898  m_authError = SaslInvalidAuthzid;
899  else if( tag->hasChild( "invalid-mechanism" ) )
900  m_authError = SaslInvalidMechanism;
901  else if( tag->hasChild( "malformed-request" ) )
902  m_authError = SaslMalformedRequest;
903  else if( tag->hasChild( "mechanism-too-weak" ) )
904  m_authError = SaslMechanismTooWeak;
905  else if( tag->hasChild( "not-authorized" ) )
906  m_authError = SaslNotAuthorized;
907  else if( tag->hasChild( "temporary-auth-failure" ) )
908  m_authError = SaslTemporaryAuthFailure;
909 
910 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
911  if( m_selectedSaslMech == SaslMechNTLM )
912  {
913  FreeCredentialsHandle( &m_credHandle );
914  DeleteSecurityContext( &m_ctxtHandle );
915  }
916 #endif
917  }
918 
919  bool ClientBase::processSASLSuccess( const std::string& payload )
920  {
921 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
922  if( m_selectedSaslMech == SaslMechNTLM )
923  {
924  FreeCredentialsHandle( &m_credHandle );
925  DeleteSecurityContext( &m_ctxtHandle );
926  }
927 #endif
928  if( m_selectedSaslMech == SaslMechScramSha1 || m_selectedSaslMech == SaslMechScramSha1Plus )
929  {
930  const std::string decoded = Base64::decode64( payload );
931  if( decoded.length() < 3 || Base64::decode64( decoded.substr( 2 ) ) != m_serverSignature )
932  return false;
933  }
934 
935  return true;
936  }
937 
938  void ClientBase::send( IQ& iq, IqHandler* ih, int context, bool del )
939  {
940  if( ih && ( iq.subtype() == IQ::Set || iq.subtype() == IQ::Get ) )
941  {
942  if( iq.id().empty() )
943  iq.setID( getID() );
944 
945  TrackStruct track;
946  track.ih = ih;
947  track.context = context;
948  track.del = del;
949  m_iqHandlerMapMutex.lock();
950  m_iqIDHandlers[iq.id()] = track;
951  m_iqHandlerMapMutex.unlock();
952  }
953 
954  send( iq );
955  }
956 
957  void ClientBase::send( const IQ& iq )
958  {
959  ++m_stats.iqStanzasSent;
960  Tag* tag = iq.tag();
961  addFrom( tag );
962  addNamespace( tag );
963  send( tag, true, false );
964  }
965 
966  void ClientBase::send( const Message& msg )
967  {
968  ++m_stats.messageStanzasSent;
969  Tag* tag = msg.tag();
970  addFrom( tag );
971  addNamespace( tag );
972  send( tag, true, false );
973  }
974 
975  void ClientBase::send( const Subscription& sub )
976  {
977  ++m_stats.s10nStanzasSent;
978  Tag* tag = sub.tag();
979  addFrom( tag );
980  addNamespace( tag );
981  send( tag, true, false );
982  }
983 
984  void ClientBase::send( const Presence& pres )
985  {
986  ++m_stats.presenceStanzasSent;
987  Tag* tag = pres.tag();
988  StanzaExtensionList::const_iterator it = m_presenceExtensions.begin();
989  for( ; it != m_presenceExtensions.end(); ++it )
990  tag->addChild( (*it)->tag() );
991  addFrom( tag );
992  addNamespace( tag );
993  send( tag, true, false );
994  }
995 
996  void ClientBase::send( Tag* tag )
997  {
998  if( !tag )
999  return;
1000 
1001  send( tag, false, true );
1002  }
1003 
1004  void ClientBase::send( Tag* tag, bool queue, bool del )
1005  {
1006  if( !tag )
1007  return;
1008 
1009  send( tag->xml() );
1010 
1011  ++m_stats.totalStanzasSent;
1012 
1013  if( m_statisticsHandler )
1014  m_statisticsHandler->handleStatistics( getStatistics() );
1015 
1016  if( queue && m_smContext >= CtxSMEnabled )
1017  {
1018  m_queueMutex.lock();
1019  m_smQueue.insert( std::make_pair( ++m_smSent, tag ) );
1020  m_queueMutex.unlock();
1021  }
1022  else if( del )
1023  delete tag;
1024  }
1025 
1026  void ClientBase::send( const std::string& xml )
1027  {
1029  {
1031  m_compression->compress( xml );
1032  else if( m_encryption && m_encryptionActive )
1033  m_encryption->encrypt( xml );
1034  else
1035  m_connection->send( xml );
1036 
1038  }
1039  }
1040 
1041  void ClientBase::checkQueue( int handled, bool resend )
1042  {
1043  if( m_smContext < CtxSMEnabled || handled < 0 )
1044  return;
1045 
1046  util::MutexGuard mg( m_queueMutex );
1047  SMQueueMap::iterator it = m_smQueue.begin();
1048  while( it != m_smQueue.end() )
1049  {
1050  if( (*it).first <= handled )
1051  {
1052  delete (*it).second;
1053  m_smQueue.erase( it++ );
1054  }
1055  else if( resend && (*it).first > handled )
1056  {
1057  send( (*it).second, false, false );
1058  ++it;
1059  }
1060  else
1061  {
1062  ++it;
1063  }
1064  }
1065  }
1066 
1068  {
1069  TagList l;
1070  util::MutexGuard mg( m_queueMutex );
1071  SMQueueMap::iterator it = m_smQueue.begin();
1072  for( ; it != m_smQueue.end(); ++it )
1073  l.push_back( (*it).second->clone() );
1074 
1075  return l;
1076  }
1077 
1078  void ClientBase::addFrom( Tag* tag )
1079  {
1080  if( !m_authed /*for IQ Auth */ || !tag || tag->hasAttribute( "from" ) )
1081  return;
1082 
1083  tag->addAttribute( "from", m_jid.full() );
1084  }
1085 
1086  void ClientBase::addNamespace( Tag* tag )
1087  {
1088  if( !tag || !tag->xmlns().empty() )
1089  return;
1090 
1091  tag->setXmlns( m_namespace );
1092  }
1093 
1095  {
1096  if( !m_seFactory )
1097  m_seFactory = new StanzaExtensionFactory();
1098 
1099  m_seFactory->registerExtension( ext );
1100  }
1101 
1103  {
1104  if( !m_seFactory )
1105  return false;
1106 
1107  return m_seFactory->removeExtension( ext );
1108  }
1109 
1111  {
1112  if( m_connection )
1114 
1115  return m_stats;
1116  }
1117 
1119  {
1121  }
1122 
1124  {
1125  send( " " );
1126  }
1127 
1128  void ClientBase::xmppPing( const JID& to, EventHandler* eh )
1129  {
1130  const std::string& id = getID();
1131  IQ iq( IQ::Get, to, id );
1132  iq.addExtension( new Ping() );
1133  m_dispatcher.registerEventHandler( eh, id );
1134  send( iq, this, XMPPPing );
1135  }
1136 
1137  bool ClientBase::handleIq( const IQ& iq )
1138  {
1139  const Ping* p = iq.findExtension<Ping>( ExtPing );
1140  if( !p || iq.subtype() != IQ::Get )
1141  return false;
1142 
1143  m_dispatcher.dispatch( Event( Event::PingPing, iq ) );
1144  IQ re( IQ::Result, iq.from(), iq.id() );
1145  send( re );
1146 
1147  return true;
1148  }
1149 
1150  void ClientBase::handleIqID( const IQ& iq, int context )
1151  {
1152  if( context == XMPPPing )
1153  m_dispatcher.dispatch( Event( ( iq.subtype() == IQ::Result ) ? Event::PingPong
1154  : Event::PingError, iq ),
1155  iq.id(), true );
1156  else
1157  handleIqIDForward( iq, context );
1158  }
1159 
1160  const std::string ClientBase::getID()
1161  {
1162 #ifdef CLIENTBASE_TEST // to create predictable UIDs in test mode
1163  return "uid" + util::int2string( m_nextId.increment() );
1164 #else
1165  char r[21+1];
1166  sprintf( r, "uid-%08x-%08x", m_uniqueBaseId, m_nextId.increment() );
1167  std::string ret( r, 21 );
1168  return ret;
1169 #endif
1170  }
1171 
1172  bool ClientBase::checkStreamVersion( const std::string& version )
1173  {
1174  if( version.empty() )
1175  return false;
1176 
1177  int major = 0;
1178 // int minor = 0;
1179  int myMajor = atoi( XMPP_STREAM_VERSION_MAJOR.c_str() );
1180 
1181  size_t dot = version.find( '.' );
1182  if( !version.empty() && dot && dot != std::string::npos )
1183  {
1184  major = atoi( version.substr( 0, dot ).c_str() );
1185 // minor = atoi( version.substr( dot ).c_str() );
1186  }
1187 
1188  return myMajor >= major;
1189  }
1190 
1192  {
1194  m_connection = connection;
1195  m_customConnection = true;
1196  if( old )
1197  delete old;
1198  }
1199 
1201  {
1202  TLSBase* old = m_encryption;
1203  m_encryption = encryption;
1204  if( old )
1205  delete old;
1206  }
1207 
1209  {
1212  if( old )
1213  delete old;
1214  }
1215 
1216  void ClientBase::handleStreamError( Tag* tag )
1217  {
1219  const TagList& c = tag->children();
1220  TagList::const_iterator it = c.begin();
1221  for( ; it != c.end(); ++it )
1222  {
1223  const std::string& name = (*it)->name();
1224  if( name == "bad-format" )
1225  err = StreamErrorBadFormat;
1226  else if( name == "bad-namespace-prefix" )
1228  else if( name == "conflict" )
1229  err = StreamErrorConflict;
1230  else if( name == "connection-timeout" )
1232  else if( name == "host-gone" )
1233  err = StreamErrorHostGone;
1234  else if( name == "host-unknown" )
1235  err = StreamErrorHostUnknown;
1236  else if( name == "improper-addressing" )
1238  else if( name == "internal-server-error" )
1240  else if( name == "invalid-from" )
1241  err = StreamErrorInvalidFrom;
1242  else if( name == "invalid-id" )
1243  err = StreamErrorInvalidId;
1244  else if( name == "invalid-namespace" )
1246  else if( name == "invalid-xml" )
1247  err = StreamErrorInvalidXml;
1248  else if( name == "not-authorized" )
1250  else if( name == "policy-violation" )
1252  else if( name == "remote-connection-failed" )
1254  else if( name == "resource-constraint" )
1256  else if( name == "restricted-xml" )
1258  else if( name == "see-other-host" )
1259  {
1261  m_streamErrorCData = tag->findChild( "see-other-host" )->cdata();
1262  }
1263  else if( name == "system-shutdown" )
1265  else if( name == "undefined-condition" )
1267  else if( name == "unsupported-encoding" )
1269  else if( name == "unsupported-stanza-type" )
1271  else if( name == "unsupported-version" )
1273  else if( name == "xml-not-well-formed" )
1275  else if( name == "text" )
1276  {
1277  const std::string& lang = (*it)->findAttribute( "xml:lang" );
1278  if( !lang.empty() )
1279  m_streamErrorText[lang] = (*it)->cdata();
1280  else
1281  m_streamErrorText["default"] = (*it)->cdata();
1282  }
1283  else
1284  m_streamErrorAppCondition = (*it);
1285 
1286  if( err != StreamErrorUndefined && (*it)->hasAttribute( XMLNS, XMLNS_XMPP_STREAM ) )
1287  m_streamError = err;
1288  }
1289  }
1290 
1291  const std::string& ClientBase::streamErrorText( const std::string& lang ) const
1292  {
1293  StringMap::const_iterator it = m_streamErrorText.find( lang );
1294  return ( it != m_streamErrorText.end() ) ? (*it).second : EmptyString;
1295  }
1296 
1298  {
1299  if( types & Message::Chat || types == 0 )
1300  m_messageSessionHandlerChat = msh;
1301 
1302  if( types & Message::Normal || types == 0 )
1303  m_messageSessionHandlerNormal = msh;
1304 
1305  if( types & Message::Groupchat || types == 0 )
1306  m_messageSessionHandlerGroupchat = msh;
1307 
1308  if( types & Message::Headline || types == 0 )
1309  m_messageSessionHandlerHeadline = msh;
1310  }
1311 
1313  {
1314  if( ph )
1315  m_presenceHandlers.push_back( ph );
1316  }
1317 
1319  {
1320  if( ph )
1321  m_presenceHandlers.remove( ph );
1322  }
1323 
1325  {
1326  if( ph && jid )
1327  {
1328  JidPresHandlerStruct jph;
1329  jph.jid = new JID( jid.bare() );
1330  jph.ph = ph;
1331  m_presenceJidHandlers.push_back( jph );
1332  }
1333  }
1334 
1336  {
1337  PresenceJidHandlerList::iterator t;
1338  PresenceJidHandlerList::iterator it = m_presenceJidHandlers.begin();
1339  while( it != m_presenceJidHandlers.end() )
1340  {
1341  t = it;
1342  ++it;
1343  if( ( !ph || (*t).ph == ph ) && (*t).jid->bare() == jid.bare() )
1344  {
1345  delete (*t).jid;
1346  m_presenceJidHandlers.erase( t );
1347  }
1348  }
1349  }
1350 
1352  {
1353  IqTrackMap::iterator t;
1354  m_iqHandlerMapMutex.lock();
1355  IqTrackMap::iterator it = m_iqIDHandlers.begin();
1356  while( it != m_iqIDHandlers.end() )
1357  {
1358  t = it;
1359  ++it;
1360  if( ih == (*t).second.ih )
1361  m_iqIDHandlers.erase( t );
1362  }
1363  m_iqHandlerMapMutex.unlock();
1364  }
1365 
1366  void ClientBase::registerIqHandler( IqHandler* ih, int exttype )
1367  {
1368  if( !ih )
1369  return;
1370 
1371  util::MutexGuard m( m_iqExtHandlerMapMutex );
1372  typedef IqHandlerMap::const_iterator IQci;
1373  std::pair<IQci, IQci> g = m_iqExtHandlers.equal_range( exttype );
1374  for( IQci it = g.first; it != g.second; ++it )
1375  {
1376  if( (*it).second == ih )
1377  return;
1378  }
1379 
1380  m_iqExtHandlers.insert( std::make_pair( exttype, ih ) );
1381  }
1382 
1383  void ClientBase::removeIqHandler( IqHandler* ih, int exttype )
1384  {
1385  if( !ih )
1386  return;
1387 
1388  util::MutexGuard m( m_iqExtHandlerMapMutex );
1389  typedef IqHandlerMap::iterator IQi;
1390  std::pair<IQi, IQi> g = m_iqExtHandlers.equal_range( exttype );
1391  IQi it2;
1392  IQi it = g.first;
1393  while( it != g.second )
1394  {
1395  it2 = it++;
1396  if( (*it2).second == ih )
1397  m_iqExtHandlers.erase( it2 );
1398  }
1399  }
1400 
1402  {
1403  if( session )
1404  m_messageSessions.push_back( session );
1405  }
1406 
1408  {
1409  if( !session )
1410  return;
1411 
1412  MessageSessionList::iterator it = std::find( m_messageSessions.begin(),
1413  m_messageSessions.end(),
1414  session );
1415  if( it != m_messageSessions.end() )
1416  {
1417  delete (*it);
1418  m_messageSessions.erase( it );
1419  }
1420  }
1421 
1423  {
1424  if( mh )
1425  m_messageHandlers.push_back( mh );
1426  }
1427 
1429  {
1430  if( mh )
1431  m_messageHandlers.remove( mh );
1432  }
1433 
1435  {
1436  if( sh )
1437  m_subscriptionHandlers.push_back( sh );
1438  }
1439 
1441  {
1442  if( sh )
1443  m_subscriptionHandlers.remove( sh );
1444  }
1445 
1446  void ClientBase::registerTagHandler( TagHandler* th, const std::string& tag, const std::string& xmlns )
1447  {
1448  if( th && !tag.empty() )
1449  {
1450  TagHandlerStruct ths;
1451  ths.tag = tag;
1452  ths.xmlns = xmlns;
1453  ths.th = th;
1454  m_tagHandlers.push_back( ths );
1455  }
1456  }
1457 
1458  void ClientBase::removeTagHandler( TagHandler* th, const std::string& tag, const std::string& xmlns )
1459  {
1460  if( th )
1461  {
1462  for( TagHandlerList::iterator it = m_tagHandlers.begin(); it != m_tagHandlers.end(); )
1463  {
1464  if( (*it).th == th && (*it).tag == tag && (*it).xmlns == xmlns )
1465  {
1466  // Normally we'd just assign it to the return value of the .erase() call,
1467  // which is either the next element, or .end(). However,
1468  // it's only since C++11 that this works; C++03 version returns void.
1469  // So instead, we do a post-increment. this increments the iterator to point
1470  // to the next element, then passes a copy of the old iterator (that is to the item to be deleted)
1471  m_tagHandlers.erase( it++ );
1472  }
1473  else
1474  {
1475  ++it;
1476  }
1477  }
1478  }
1479  }
1480 
1482  {
1483  if( sh )
1484  m_statisticsHandler = sh;
1485  }
1486 
1488  {
1489  m_statisticsHandler = 0;
1490  }
1491 
1493  {
1494  if( mih )
1495  {
1496  m_mucInvitationHandler = mih;
1498  }
1499  }
1500 
1502  {
1503  m_mucInvitationHandler = 0;
1505  }
1506 
1508  {
1509  if( cl )
1510  m_connectionListeners.push_back( cl );
1511  }
1512 
1514  {
1515  if( cl )
1516  m_connectionListeners.remove( cl );
1517  }
1518 
1520  {
1521  util::ForEach( m_connectionListeners, &ConnectionListener::onConnect );
1522  }
1523 
1524  void ClientBase::notifyOnDisconnect( ConnectionError e )
1525  {
1526  util::ForEach( m_connectionListeners, &ConnectionListener::onDisconnect, e );
1527  init();
1528  }
1529 
1531  {
1532  ConnectionListenerList::const_iterator it = m_connectionListeners.begin();
1533  for( ; it != m_connectionListeners.end() && (*it)->onTLSConnect( info ); ++it )
1534  ;
1535  return m_stats.encryption = ( it == m_connectionListeners.end() );
1536  }
1537 
1539  {
1540  util::ForEach( m_connectionListeners, &ConnectionListener::onResourceBindError, error );
1541  }
1542 
1543  void ClientBase::notifyOnResourceBind( const std::string& resource )
1544  {
1545  util::ForEach( m_connectionListeners, &ConnectionListener::onResourceBind, resource );
1546  }
1547 
1549  {
1550  util::ForEach( m_connectionListeners, &ConnectionListener::onSessionCreateError, error );
1551  }
1552 
1554  {
1555  util::ForEach( m_connectionListeners, &ConnectionListener::onStreamEvent, event );
1556  }
1557 
1558  void ClientBase::notifyPresenceHandlers( Presence& pres )
1559  {
1560  bool match = false;
1561  PresenceJidHandlerList::const_iterator t;
1562  PresenceJidHandlerList::const_iterator itj = m_presenceJidHandlers.begin();
1563  while( itj != m_presenceJidHandlers.end() )
1564  {
1565  t = itj++;
1566  if( (*t).jid->bare() == pres.from().bare() && (*t).ph )
1567  {
1568  (*t).ph->handlePresence( pres );
1569  match = true;
1570  }
1571  }
1572  if( match )
1573  return;
1574 
1575  // FIXME remove this for() for 1.1:
1576  PresenceHandlerList::const_iterator it = m_presenceHandlers.begin();
1577  for( ; it != m_presenceHandlers.end(); ++it )
1578  {
1579  (*it)->handlePresence( pres );
1580  }
1581  // FIXME and reinstantiate this:
1582 // util::ForEach( m_presenceHandlers, &PresenceHandler::handlePresence, pres );
1583  }
1584 
1585  void ClientBase::notifySubscriptionHandlers( Subscription& s10n )
1586  {
1587  // FIXME remove this for() for 1.1:
1588  SubscriptionHandlerList::const_iterator it = m_subscriptionHandlers.begin();
1589  for( ; it != m_subscriptionHandlers.end(); ++it )
1590  {
1591  (*it)->handleSubscription( s10n );
1592  }
1593  // FIXME and reinstantiate this:
1594 // util::ForEach( m_subscriptionHandlers, &SubscriptionHandler::handleSubscription, s10n );
1595  }
1596 
1597  void ClientBase::notifyIqHandlers( IQ& iq )
1598  {
1599  m_iqHandlerMapMutex.lock();
1600  IqTrackMap::iterator it_id = m_iqIDHandlers.find( iq.id() );
1601  bool haveIdHandler = ( it_id != m_iqIDHandlers.end() );
1602  m_iqHandlerMapMutex.unlock();
1603  if( haveIdHandler && ( iq.subtype() == IQ::Result || iq.subtype() == IQ::Error ) )
1604  {
1605  (*it_id).second.ih->handleIqID( iq, (*it_id).second.context );
1606  if( (*it_id).second.del )
1607  delete (*it_id).second.ih;
1608  m_iqHandlerMapMutex.lock();
1609  m_iqIDHandlers.erase( it_id );
1610  m_iqHandlerMapMutex.unlock();
1611  return;
1612  }
1613 
1614  if( iq.extensions().empty() )
1615  {
1616  if ( iq.subtype() == IQ::Get || iq.subtype() == IQ::Set )
1617  {
1618  IQ re( IQ::Error, iq.from(), iq.id() );
1619  re.addExtension( new Error( StanzaErrorTypeCancel, StanzaErrorFeatureNotImplemented ) );
1620  send( re );
1621  }
1622  return;
1623  }
1624 
1625  bool handled = false;
1626 
1627  // FIXME remove for 1.1
1628 // typedef IqHandlerMapXmlns::const_iterator IQciXmlns
1629 // Tag *tag = iq.tag()->xmlns();
1630 // std::pair<IQciXmlns, IQciXmlns> g = m_iqNSHandlers.equal_range( tag->xmlns() );
1631 // for( IQciXmlns it = g.first; it != g.second; ++it )
1632 // {
1633 // if( (*it).second->handleIq( iq ) )
1634 // res = true;
1635 // }
1636 // delete tag;
1637 
1638  m_iqExtHandlerMapMutex.lock();
1639  typedef IqHandlerMap::const_iterator IQci;
1640  const StanzaExtensionList& sel = iq.extensions();
1641  StanzaExtensionList::const_iterator itse = sel.begin();
1642  for( ; !handled && itse != sel.end(); ++itse )
1643  {
1644  std::pair<IQci, IQci> g = m_iqExtHandlers.equal_range( (*itse)->extensionType() );
1645  for( IQci it = g.first; !handled && it != g.second; ++it )
1646  {
1647  if( (*it).second->handleIq( iq ) )
1648  handled = true;
1649  }
1650  }
1651  m_iqExtHandlerMapMutex.unlock();
1652 
1653  if( !handled && ( iq.subtype() == IQ::Get || iq.subtype() == IQ::Set ) )
1654  {
1655  IQ re( IQ::Error, iq.from(), iq.id() );
1656  re.addExtension( new Error( StanzaErrorTypeCancel, StanzaErrorServiceUnavailable ) );
1657  send( re );
1658  }
1659  }
1660 
1661  void ClientBase::notifyMessageHandlers( Message& msg )
1662  {
1663  if( m_mucInvitationHandler )
1664  {
1665  const MUCRoom::MUCUser* mu = msg.findExtension<MUCRoom::MUCUser>( ExtMUCUser );
1666  if( mu && mu->operation() != MUCRoom::OpInviteTo )
1667  {
1668 
1669  m_mucInvitationHandler->handleMUCInvitation( msg.from(),
1670  mu->jid() ? JID( *(mu->jid()) ) : JID(),
1671  mu->reason() ? *(mu->reason()) : EmptyString,
1672  msg.body(),
1673  mu->password() ? *(mu->password()) : EmptyString,
1674  mu->continued(),
1675  mu->thread() ? *(mu->thread()) : EmptyString );
1676  return;
1677  }
1678  }
1679 
1680  MessageSessionList::const_iterator it1 = m_messageSessions.begin();
1681  for( ; it1 != m_messageSessions.end(); ++it1 )
1682  {
1683  if( (*it1)->target().full() == msg.from().full() &&
1684  ( msg.thread().empty()
1685  || (*it1)->threadID() == msg.thread()
1686  || (*it1)->honorThreadID() ) &&
1687 // FIXME don't use '== 0' here
1688  ( (*it1)->types() & msg.subtype() || (*it1)->types() == 0 ) )
1689  {
1690  (*it1)->handleMessage( msg );
1691  return;
1692  }
1693  }
1694 
1695  it1 = m_messageSessions.begin();
1696  for( ; it1 != m_messageSessions.end(); ++it1 )
1697  {
1698  if( (*it1)->target().bare() == msg.from().bare() &&
1699  ( msg.thread().empty()
1700  || (*it1)->threadID() == msg.thread()
1701  || (*it1)->honorThreadID() ) &&
1702 // FIXME don't use '== 0' here
1703  ( (*it1)->types() & msg.subtype() || (*it1)->types() == 0 ) )
1704  {
1705  (*it1)->handleMessage( msg );
1706  return;
1707  }
1708  }
1709 
1710  MessageSessionHandler* msHandler = 0;
1711 
1712  switch( msg.subtype() )
1713  {
1714  case Message::Chat:
1715  msHandler = m_messageSessionHandlerChat;
1716  break;
1717  case Message::Normal:
1718  msHandler = m_messageSessionHandlerNormal;
1719  break;
1720  case Message::Groupchat:
1721  msHandler = m_messageSessionHandlerGroupchat;
1722  break;
1723  case Message::Headline:
1724  msHandler = m_messageSessionHandlerHeadline;
1725  break;
1726  default:
1727  break;
1728  }
1729 
1730  if( msHandler )
1731  {
1732  MessageSession* session = new MessageSession( this, msg.from(), true, msg.subtype() );
1733  msHandler->handleMessageSession( session );
1734  session->handleMessage( msg );
1735  }
1736  else
1737  {
1738  // FIXME remove this for() for 1.1:
1739  MessageHandlerList::const_iterator it = m_messageHandlers.begin();
1740  for( ; it != m_messageHandlers.end(); ++it )
1741  {
1742  (*it)->handleMessage( msg );
1743  }
1744  // FIXME and reinstantiate this:
1745 // util::ForEach( m_messageHandlers, &MessageHandler::handleMessage, msg ); // FIXME remove for 1.1
1746  }
1747  }
1748 
1749  void ClientBase::notifyTagHandlers( Tag* tag )
1750  {
1751  TagHandlerList::const_iterator it = m_tagHandlers.begin();
1752  for( ; it != m_tagHandlers.end(); ++it )
1753  {
1754  if( (*it).tag == tag->name() && tag->hasAttribute( XMLNS, (*it).xmlns ) )
1755  (*it).th->handleTag( tag );
1756  }
1757  }
1758 
1760  {
1761  if( !se )
1762  return;
1763 
1765  m_presenceExtensions.push_back( se );
1766  }
1767 
1769  {
1770  StanzaExtensionList::iterator it = m_presenceExtensions.begin();
1771  for( ; it != m_presenceExtensions.end(); ++it )
1772  {
1773  if( (*it)->extensionType() == type )
1774  {
1775  delete (*it);
1776  m_presenceExtensions.erase( it );
1777  return true;
1778  }
1779  }
1780 
1781  return false;
1782  }
1783 
1785  {
1786  char cn[4*8+1];
1787  for( int i = 0; i < 4; ++i )
1788  sprintf( cn + i*8, "%08x", rand() );
1789  return std::string( cn, 4*8 );;
1790  }
1791 
1792  CompressionBase* ClientBase::getDefaultCompression()
1793  {
1794  if( !m_compress )
1795  return 0;
1796 
1797 #ifdef HAVE_ZLIB
1798  CompressionBase* cmp = new CompressionZlib( this );
1799  if( cmp->init() )
1800  return cmp;
1801 
1802  delete cmp;
1803 #endif
1804  return 0;
1805  }
1806 
1807  TLSBase* ClientBase::getDefaultEncryption()
1808  {
1809  if( m_tls == TLSDisabled || !hasTls() )
1810  return 0;
1811 
1812  TLSDefault* tls = new TLSDefault( this, m_server );
1813  if( tls->init( m_clientKey, m_clientCerts, m_cacerts ) )
1814  return tls;
1815  else
1816  {
1817  delete tls;
1818  return 0;
1819  }
1820  }
1821 
1822 }