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