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