00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #ifdef _WIN32
00016 # include "../config.h.win"
00017 #elif defined( _WIN32_WCE )
00018 # include "../config.h.win"
00019 #else
00020 # include "config.h"
00021 #endif
00022
00023 #include "clientbase.h"
00024 #include "connectionbase.h"
00025 #include "tlsbase.h"
00026 #include "compressionbase.h"
00027 #include "connectiontcpclient.h"
00028 #include "disco.h"
00029 #include "messagesessionhandler.h"
00030 #include "parser.h"
00031 #include "tag.h"
00032 #include "stanza.h"
00033 #include "connectionlistener.h"
00034 #include "iqhandler.h"
00035 #include "messagehandler.h"
00036 #include "presencehandler.h"
00037 #include "rosterlistener.h"
00038 #include "subscriptionhandler.h"
00039 #include "loghandler.h"
00040 #include "taghandler.h"
00041 #include "mucinvitationhandler.h"
00042 #include "jid.h"
00043 #include "base64.h"
00044 #include "md5.h"
00045 #include "tlsdefault.h"
00046 #include "compressionzlib.h"
00047
00048 #include <cstdlib>
00049 #include <string>
00050 #include <map>
00051 #include <list>
00052 #include <algorithm>
00053
00054 #ifndef _WIN32_WCE
00055 # include <sstream>
00056 # include <iomanip>
00057 #endif
00058
00059 namespace gloox
00060 {
00061
00062 ClientBase::ClientBase( const std::string& ns, const std::string& server, int port )
00063 : m_connection( 0 ), m_encryption( 0 ), m_compression( 0 ), m_disco( 0 ), m_namespace( ns ),
00064 m_xmllang( "en" ), m_server( server ), m_compressionActive( false ), m_encryptionActive( false ),
00065 m_compress( true ), m_authed( false ), m_sasl( true ), m_tls( TLSOptional ), m_port( port ),
00066 m_availableSaslMechs( SaslMechAll ),
00067 m_statisticsHandler( 0 ), m_mucInvitationHandler( 0 ),
00068 m_messageSessionHandlerChat( 0 ), m_messageSessionHandlerGroupchat( 0 ),
00069 m_messageSessionHandlerHeadline( 0 ), m_messageSessionHandlerNormal( 0 ),
00070 m_parser( 0 ), m_authError( AuthErrorUndefined ), m_streamError( StreamErrorUndefined ),
00071 m_streamErrorAppCondition( 0 ), m_selectedSaslMech( SaslMechNone ),
00072 m_idCount( 0 ), m_autoMessageSession( false )
00073 {
00074 init();
00075 }
00076
00077 ClientBase::ClientBase( const std::string& ns, const std::string& password,
00078 const std::string& server, int port )
00079 : m_connection( 0 ), m_encryption( 0 ), m_compression( 0 ), m_disco( 0 ), m_namespace( ns ),
00080 m_password( password ),
00081 m_xmllang( "en" ), m_server( server ), m_compressionActive( false ), m_encryptionActive( false ),
00082 m_compress( true ), m_authed( false ), m_block( false ), m_sasl( true ), m_tls( TLSOptional ),
00083 m_port( port ), m_availableSaslMechs( SaslMechAll ),
00084 m_statisticsHandler( 0 ), m_mucInvitationHandler( 0 ),
00085 m_messageSessionHandlerChat( 0 ), m_messageSessionHandlerGroupchat( 0 ),
00086 m_messageSessionHandlerHeadline( 0 ), m_messageSessionHandlerNormal( 0 ),
00087 m_parser( 0 ), m_authError( AuthErrorUndefined ), m_streamError( StreamErrorUndefined ),
00088 m_streamErrorAppCondition( 0 ), m_selectedSaslMech( SaslMechNone ),
00089 m_idCount( 0 ), m_autoMessageSession( false )
00090 {
00091 init();
00092 }
00093
00094 void ClientBase::init()
00095 {
00096 if( !m_disco )
00097 {
00098 m_disco = new Disco( this );
00099 m_disco->setVersion( "based on gloox", GLOOX_VERSION );
00100 }
00101
00102 m_streamError = StreamErrorUndefined;
00103
00104 m_block = false;
00105
00106 m_stats.totalBytesSent = 0;
00107 m_stats.totalBytesReceived = 0;
00108 m_stats.compressedBytesSent = 0;
00109 m_stats.compressedBytesReceived = 0;
00110 m_stats.uncompressedBytesSent = 0;
00111 m_stats.uncompressedBytesReceived = 0;
00112 m_stats.totalStanzasSent = 0;
00113 m_stats.totalStanzasReceived = 0;
00114 m_stats.iqStanzasSent = 0;
00115 m_stats.iqStanzasReceived = 0;
00116 m_stats.messageStanzasSent = 0;
00117 m_stats.messageStanzasReceived = 0;
00118 m_stats.s10nStanzasSent = 0;
00119 m_stats.s10nStanzasReceived = 0;
00120 m_stats.presenceStanzasSent = 0;
00121 m_stats.presenceStanzasReceived = 0;
00122 m_stats.encryption = false;
00123 m_stats.compression = false;
00124
00125 cleanup();
00126 }
00127
00128 ClientBase::~ClientBase()
00129 {
00130 delete m_connection;
00131 delete m_encryption;
00132 delete m_compression;
00133 delete m_parser;
00134 delete m_disco;
00135
00136 MessageSessionList::const_iterator it = m_messageSessions.begin();
00137 for( ; it != m_messageSessions.end(); ++it )
00138 delete (*it);
00139
00140 PresenceJidHandlerList::const_iterator it1 = m_presenceJidHandlers.begin();
00141 for( ; it1 != m_presenceJidHandlers.end(); ++it1 )
00142 delete (*it1).jid;
00143 }
00144
00145 ConnectionError ClientBase::recv( int timeout )
00146 {
00147 if( !m_connection || m_connection->state() == StateDisconnected )
00148 return ConnNotConnected;
00149
00150 return m_connection->recv( timeout );
00151 }
00152
00153 bool ClientBase::connect( bool block )
00154 {
00155 if( m_server.empty() )
00156 return false;
00157
00158 if( !m_parser )
00159 m_parser = new Parser( this );
00160
00161 if( !m_connection )
00162 m_connection = new ConnectionTCPClient( this, m_logInstance, m_server, m_port );
00163
00164 if( m_connection->state() >= StateConnecting )
00165 return true;
00166
00167 if( !m_encryption )
00168 m_encryption = getDefaultEncryption();
00169
00170 if( m_encryption )
00171 {
00172 m_encryption->setCACerts( m_cacerts );
00173 m_encryption->setClientCert( m_clientKey, m_clientCerts );
00174 }
00175
00176 if( !m_compression )
00177 m_compression = getDefaultCompression();
00178
00179 m_block = block;
00180 ConnectionError ret = m_connection->connect();
00181 return ret == ConnNoError;
00182 }
00183
00184 void ClientBase::handleTag( Tag *tag )
00185 {
00186 if( !tag )
00187 {
00188 logInstance().log( LogLevelDebug, LogAreaClassClientbase, "stream closed" );
00189 disconnect( ConnStreamClosed );
00190 return;
00191 }
00192
00193 Stanza *stanza = new Stanza( tag );
00194
00195 logInstance().log( LogLevelDebug, LogAreaXmlIncoming, stanza->xml() );
00196 ++m_stats.totalStanzasReceived;
00197
00198 if( tag->name() == "stream:stream" )
00199 {
00200 const std::string& version = stanza->findAttribute( "version" );
00201 if( !checkStreamVersion( version ) )
00202 {
00203 logInstance().log( LogLevelDebug, LogAreaClassClientbase, "This server is not XMPP-compliant"
00204 " (it does not send a 'version' attribute). Please fix it or try another one.\n" );
00205 disconnect( ConnStreamVersionError );
00206 }
00207
00208 m_sid = stanza->findAttribute( "id" );
00209 handleStartNode();
00210 }
00211 else if( tag->name() == "stream:error" )
00212 {
00213 handleStreamError( stanza );
00214 disconnect( ConnStreamError );
00215 }
00216 else
00217 {
00218 if( !handleNormalNode( stanza ) )
00219 {
00220 switch( stanza->type() )
00221 {
00222 case StanzaIq:
00223 notifyIqHandlers( stanza );
00224 ++m_stats.iqStanzasReceived;
00225 break;
00226 case StanzaPresence:
00227 notifyPresenceHandlers( stanza );
00228 ++m_stats.presenceStanzasReceived;
00229 break;
00230 case StanzaS10n:
00231 notifySubscriptionHandlers( stanza );
00232 ++m_stats.s10nStanzasReceived;
00233 break;
00234 case StanzaMessage:
00235 notifyMessageHandlers( stanza );
00236 ++m_stats.messageStanzasReceived;
00237 break;
00238 default:
00239 notifyTagHandlers( tag );
00240 break;
00241 }
00242 }
00243 }
00244
00245 if( m_statisticsHandler )
00246 m_statisticsHandler->handleStatistics( getStatistics() );
00247
00248 delete stanza;
00249 }
00250
00251 void ClientBase::handleCompressedData( const std::string& data )
00252 {
00253 if( m_encryption && m_encryptionActive )
00254 m_encryption->encrypt( data );
00255 else if( m_connection )
00256 m_connection->send( data );
00257 else
00258 m_logInstance.log( LogLevelError, LogAreaClassClientbase, "Compression finished, but chain broken" );
00259 }
00260
00261 void ClientBase::handleDecompressedData( const std::string& data )
00262 {
00263 if( m_parser )
00264 parse( data );
00265 else
00266 m_logInstance.log( LogLevelError, LogAreaClassClientbase, "Decompression finished, but chain broken" );
00267 }
00268
00269 void ClientBase::handleEncryptedData( const TLSBase* , const std::string& data )
00270 {
00271 if( m_connection )
00272 m_connection->send( data );
00273 else
00274 m_logInstance.log( LogLevelError, LogAreaClassClientbase, "Encryption finished, but chain broken" );
00275 }
00276
00277 void ClientBase::handleDecryptedData( const TLSBase* , const std::string& data )
00278 {
00279 if( m_compression && m_compressionActive )
00280 m_compression->decompress( data );
00281 else if( m_parser )
00282 parse( data );
00283 else
00284 m_logInstance.log( LogLevelError, LogAreaClassClientbase, "Decryption finished, but chain broken" );
00285 }
00286
00287 void ClientBase::handleHandshakeResult( const TLSBase* , bool success, CertInfo &certinfo )
00288 {
00289 if( success )
00290 {
00291 if( !notifyOnTLSConnect( certinfo ) )
00292 {
00293 logInstance().log( LogLevelError, LogAreaClassClientbase, "Server's certificate rejected!" );
00294 disconnect( ConnTlsFailed );
00295 }
00296 else
00297 {
00298 logInstance().log( LogLevelDebug, LogAreaClassClientbase, "connection encryption active" );
00299 header();
00300 }
00301 }
00302 else
00303 {
00304 logInstance().log( LogLevelError, LogAreaClassClientbase, "TLS handshake failed!" );
00305 disconnect( ConnTlsFailed );
00306 }
00307 }
00308
00309 void ClientBase::handleReceivedData( const ConnectionBase* , const std::string& data )
00310 {
00311 if( m_encryption && m_encryptionActive )
00312 m_encryption->decrypt( data );
00313 else if( m_compression && m_compressionActive )
00314 m_compression->decompress( data );
00315 else if( m_parser )
00316 parse( data );
00317 else
00318 m_logInstance.log( LogLevelError, LogAreaClassClientbase, "Received data, but chain broken" );
00319 }
00320
00321 void ClientBase::handleConnect( const ConnectionBase* )
00322 {
00323 header();
00324 if( m_block && m_connection )
00325 {
00326 m_connection->receive();
00327 }
00328 }
00329
00330 void ClientBase::handleDisconnect( const ConnectionBase* , ConnectionError reason )
00331 {
00332 if( m_connection )
00333 m_connection->cleanup();
00334 notifyOnDisconnect( reason );
00335 }
00336
00337 void ClientBase::disconnect( ConnectionError reason )
00338 {
00339 if( m_connection && m_connection->state() >= StateConnecting )
00340 {
00341 if( reason != ConnTlsFailed )
00342 send( "</stream:stream>" );
00343
00344 m_connection->disconnect();
00345 m_connection->cleanup();
00346
00347 if( m_encryption )
00348 m_encryption->cleanup();
00349
00350 m_encryptionActive = false;
00351 m_compressionActive = false;
00352
00353 notifyOnDisconnect( reason );
00354 }
00355 }
00356
00357 void ClientBase::parse( const std::string& data )
00358 {
00359 if( m_parser && !m_parser->feed( data ) )
00360 {
00361 m_logInstance.log( LogLevelError, LogAreaClassClientbase, "parse error: " + data );
00362 Tag* e = new Tag( "stream:error" );
00363 new Tag( e, "restricted-xml", "xmlns", XMLNS_XMPP_STREAM );
00364 send( e );
00365 disconnect( ConnParseError );
00366 }
00367 }
00368
00369 void ClientBase::header()
00370 {
00371 std::string head = "<?xml version='1.0' ?>";
00372 head += "<stream:stream to='" + m_jid.server() + "' xmlns='" + m_namespace + "' ";
00373 head += "xmlns:stream='http://etherx.jabber.org/streams' xml:lang='" + m_xmllang + "' ";
00374 head += "version='" + XMPP_STREAM_VERSION_MAJOR + "." + XMPP_STREAM_VERSION_MINOR + "'>";
00375 send( head );
00376 }
00377
00378 bool ClientBase::hasTls()
00379 {
00380 #if defined( HAVE_GNUTLS ) || defined( HAVE_OPENSSL ) || defined( HAVE_WINTLS )
00381 return true;
00382 #else
00383 return false;
00384 #endif
00385 }
00386
00387 void ClientBase::startTls()
00388 {
00389 Tag *start = new Tag( "starttls" );
00390 start->addAttribute( "xmlns", XMLNS_STREAM_TLS );
00391 send( start );
00392 }
00393
00394 void ClientBase::setServer( const std::string &server )
00395 {
00396 m_server = server;
00397 if( m_connection )
00398 m_connection->setServer( server );
00399 }
00400
00401 void ClientBase::setClientCert( const std::string& clientKey, const std::string& clientCerts )
00402 {
00403 m_clientKey = clientKey;
00404 m_clientCerts = clientCerts;
00405 }
00406
00407 void ClientBase::startSASL( SaslMechanism type )
00408 {
00409 m_selectedSaslMech = type;
00410
00411 Tag *a = new Tag( "auth" );
00412 a->addAttribute( "xmlns", XMLNS_STREAM_SASL );
00413
00414 switch( type )
00415 {
00416 case SaslMechDigestMd5:
00417 a->addAttribute( "mechanism", "DIGEST-MD5" );
00418 break;
00419 case SaslMechPlain:
00420 {
00421 a->addAttribute( "mechanism", "PLAIN" );
00422
00423 std::string tmp;
00424 if( m_authzid )
00425 tmp += m_authzid.bare();
00426
00427 tmp += '\0';
00428 tmp += m_jid.username();
00429 tmp += '\0';
00430 tmp += m_password;
00431 a->setCData( Base64::encode64( tmp ) );
00432 break;
00433 }
00434 case SaslMechAnonymous:
00435 a->addAttribute( "mechanism", "ANONYMOUS" );
00436 a->setCData( getID() );
00437 break;
00438 case SaslMechExternal:
00439 a->addAttribute( "mechanism", "EXTERNAL" );
00440 if( m_authzid )
00441 a->setCData( Base64::encode64( m_authzid.bare() ) );
00442 else
00443 a->setCData( Base64::encode64( m_jid.bare() ) );
00444 break;
00445 case SaslMechGssapi:
00446 {
00447 #ifdef _WIN32
00448 a->addAttribute( "mechanism", "GSSAPI" );
00449
00450
00451
00452
00453
00454
00455
00456
00457 std::string token;
00458 a->setCData( Base64::encode64( token ) );
00459
00460 #else
00461 logInstance().log( LogLevelError, LogAreaClassClientbase,
00462 "GSSAPI is not supported on this platform. You should never see this." );
00463 #endif
00464 break;
00465 }
00466 default:
00467 break;
00468 }
00469
00470 send( a );
00471 }
00472
00473 void ClientBase::processSASLChallenge( const std::string& challenge )
00474 {
00475 Tag *t = new Tag( "response" );
00476 t->addAttribute( "xmlns", XMLNS_STREAM_SASL );
00477
00478 const std::string& decoded = Base64::decode64( challenge );
00479
00480 switch( m_selectedSaslMech )
00481 {
00482 case SaslMechDigestMd5:
00483 {
00484 if( decoded.substr( 0, 7 ) == "rspauth" )
00485 {
00486 break;
00487 }
00488 std::string realm;
00489 size_t r_pos = decoded.find( "realm=" );
00490 if( r_pos != std::string::npos )
00491 {
00492 size_t r_end = decoded.find( "\"", r_pos + 7 );
00493 realm = decoded.substr( r_pos + 7, r_end - (r_pos + 7 ) );
00494 }
00495 else
00496 realm = m_jid.server();
00497
00498 size_t n_pos = decoded.find( "nonce=" );
00499 if( n_pos == std::string::npos )
00500 {
00501 return;
00502 }
00503
00504 size_t n_end = decoded.find( "\"", n_pos + 7 );
00505 while( decoded.substr( n_end-1, 1 ) == "\\" )
00506 n_end = decoded.find( "\"", n_end + 1 );
00507 std::string nonce = decoded.substr( n_pos + 7, n_end - ( n_pos + 7 ) );
00508
00509 std::string cnonce;
00510 #ifdef _WIN32_WCE
00511 char cn[4*8+1];
00512 for( int i = 0; i < 4; ++i )
00513 sprintf( cn + i*8, "%08x", rand() );
00514 cnonce.assign( cn, 4*8 );
00515 #else
00516 std::ostringstream cn;
00517 for( int i = 0; i < 4; ++i )
00518 cn << std::hex << std::setw( 8 ) << std::setfill( '0' ) << rand();
00519 cnonce = cn.str();
00520 #endif
00521
00522 MD5 md5;
00523 md5.feed( m_jid.username() );
00524 md5.feed( ":" );
00525 md5.feed( realm );
00526 md5.feed( ":" );
00527 md5.feed( m_password );
00528 md5.finalize();
00529 const std::string& a1_h = md5.binary();
00530 md5.reset();
00531 md5.feed( a1_h );
00532 md5.feed( ":" );
00533 md5.feed( nonce );
00534 md5.feed( ":" );
00535 md5.feed( cnonce );
00536 md5.finalize();
00537 const std::string& a1 = md5.hex();
00538 md5.reset();
00539 md5.feed( "AUTHENTICATE:xmpp/" );
00540 md5.feed( m_jid.server() );
00541 md5.finalize();
00542 const std::string& a2 = md5.hex();
00543 md5.reset();
00544 md5.feed( a1 );
00545 md5.feed( ":" );
00546 md5.feed( nonce );
00547 md5.feed( ":00000001:" );
00548 md5.feed( cnonce );
00549 md5.feed( ":auth:" );
00550 md5.feed( a2 );
00551 md5.finalize();
00552 const std::string& response_value = md5.hex();
00553
00554 std::string response = "username=\"" + m_jid.username() + "\",realm=\"" + realm;
00555 response += "\",nonce=\""+ nonce + "\",cnonce=\"" + cnonce;
00556 response += "\",nc=00000001,qop=auth,digest-uri=\"xmpp/" + m_jid.server() + "\",response=";
00557 response += response_value;
00558 response += ",charset=utf-8";
00559
00560 if( m_authzid )
00561 response += ",authzid=" + m_authzid.bare();
00562
00563 t->setCData( Base64::encode64( response ) );
00564
00565 break;
00566 }
00567 case SaslMechGssapi:
00568 #ifdef _WIN32
00569
00570 #else
00571 m_logInstance.log( LogLevelError, LogAreaClassClientbase,
00572 "Huh, received GSSAPI challenge?! This should have never happened!" );
00573 #endif
00574 break;
00575 default:
00576
00577 break;
00578 }
00579
00580 send( t );
00581 }
00582
00583 void ClientBase::processSASLError( Stanza *stanza )
00584 {
00585 if( stanza->hasChild( "aborted" ) )
00586 m_authError = SaslAborted;
00587 else if( stanza->hasChild( "incorrect-encoding" ) )
00588 m_authError = SaslIncorrectEncoding;
00589 else if( stanza->hasChild( "invalid-authzid" ) )
00590 m_authError = SaslInvalidAuthzid;
00591 else if( stanza->hasChild( "invalid-mechanism" ) )
00592 m_authError = SaslInvalidMechanism;
00593 else if( stanza->hasChild( "mechanism-too-weak" ) )
00594 m_authError = SaslMechanismTooWeak;
00595 else if( stanza->hasChild( "not-authorized" ) )
00596 m_authError = SaslNotAuthorized;
00597 else if( stanza->hasChild( "temporary-auth-failure" ) )
00598 m_authError = SaslTemporaryAuthFailure;
00599 }
00600
00601 void ClientBase::send( Tag *tag )
00602 {
00603 if( !tag )
00604 return;
00605
00606 send( tag->xml() );
00607
00608 switch( tag->type() )
00609 {
00610 case StanzaIq:
00611 ++m_stats.iqStanzasSent;
00612 break;
00613 case StanzaMessage:
00614 ++m_stats.messageStanzasSent;
00615 break;
00616 case StanzaS10n:
00617 ++m_stats.s10nStanzasSent;
00618 break;
00619 case StanzaPresence:
00620 ++m_stats.presenceStanzasSent;
00621 break;
00622 default:
00623 break;
00624 }
00625 ++m_stats.totalStanzasSent;
00626
00627 delete tag;
00628
00629 if( m_statisticsHandler )
00630 m_statisticsHandler->handleStatistics( getStatistics() );
00631 }
00632
00633 void ClientBase::send( const std::string& xml )
00634 {
00635 if( m_connection && m_connection->state() == StateConnected )
00636 {
00637 if( m_compression && m_compressionActive )
00638 m_compression->compress( xml );
00639 else if( m_encryption && m_encryptionActive )
00640 m_encryption->encrypt( xml );
00641 else
00642 m_connection->send( xml );
00643
00644 logInstance().log( LogLevelDebug, LogAreaXmlOutgoing, xml );
00645 }
00646 }
00647
00648 StatisticsStruct ClientBase::getStatistics()
00649 {
00650
00651
00652
00653
00654
00655 return m_stats;
00656 }
00657
00658 ConnectionState ClientBase::state() const
00659 {
00660 return m_connection ? m_connection->state() : StateDisconnected;
00661 }
00662
00663 void ClientBase::whitespacePing()
00664 {
00665 send( " " );
00666 }
00667
00668 void ClientBase::xmppPing( const JID& to )
00669 {
00670 const std::string& id = getID();
00671
00672 Tag *iq = new Tag( "iq" );
00673 iq->addAttribute( "to", to.full() );
00674 iq->addAttribute( "id", id );
00675 iq->addAttribute( "type", "get" );
00676 Tag *p = new Tag( iq, "ping" );
00677 p->addAttribute( "xmlns", XMLNS_XMPP_PING );
00678
00679 send( iq );
00680 }
00681
00682 const std::string ClientBase::getID()
00683 {
00684 #ifdef _WIN32_WCE
00685 char r[8+1];
00686 sprintf( r, "%08x", rand() );
00687 std::string ret( r, 8 );
00688 return std::string( "uid" ) + ret;
00689 #else
00690 std::ostringstream oss;
00691 oss << ++m_idCount;
00692 return std::string( "uid" ) + oss.str();
00693 #endif
00694 }
00695
00696 bool ClientBase::checkStreamVersion( const std::string& version )
00697 {
00698 if( version.empty() )
00699 return false;
00700
00701 int major = 0;
00702 int minor = 0;
00703 int myMajor = atoi( XMPP_STREAM_VERSION_MAJOR.c_str() );
00704
00705 size_t dot = version.find( "." );
00706 if( !version.empty() && dot && dot != std::string::npos )
00707 {
00708 major = atoi( version.substr( 0, dot ).c_str() );
00709 minor = atoi( version.substr( dot ).c_str() );
00710 }
00711
00712 return myMajor >= major;
00713 }
00714
00715 LogSink& ClientBase::logInstance()
00716 {
00717 return m_logInstance;
00718 }
00719
00720 void ClientBase::setConnectionImpl( ConnectionBase *cb )
00721 {
00722 if( m_connection )
00723 {
00724 delete m_connection;
00725 }
00726 m_connection = cb;
00727 }
00728
00729 void ClientBase::setEncryptionImpl( TLSBase *tb )
00730 {
00731 if( m_encryption )
00732 {
00733 delete m_encryption;
00734 }
00735 m_encryption = tb;
00736 }
00737
00738 void ClientBase::setCompressionImpl( CompressionBase *cb )
00739 {
00740 if( m_compression )
00741 {
00742 delete m_compression;
00743 }
00744 m_compression = cb;
00745 }
00746
00747 void ClientBase::handleStreamError( Stanza *stanza )
00748 {
00749 StreamError err = StreamErrorUndefined;
00750 const Tag::TagList& c = stanza->children();
00751 Tag::TagList::const_iterator it = c.begin();
00752 for( ; it != c.end(); ++it )
00753 {
00754 if( (*it)->name() == "bad-format" )
00755 err = StreamErrorBadFormat;
00756 else if( (*it)->name() == "bad-namespace-prefix" )
00757 err = StreamErrorBadNamespacePrefix;
00758 else if( (*it)->name() == "conflict" )
00759 err = StreamErrorConflict;
00760 else if( (*it)->name() == "connection-timeout" )
00761 err = StreamErrorConnectionTimeout;
00762 else if( (*it)->name() == "host-gone" )
00763 err = StreamErrorHostGone;
00764 else if( (*it)->name() == "host-unknown" )
00765 err = StreamErrorHostUnknown;
00766 else if( (*it)->name() == "improper-addressing" )
00767 err = StreamErrorImproperAddressing;
00768 else if( (*it)->name() == "internal-server-error" )
00769 err = StreamErrorInternalServerError;
00770 else if( (*it)->name() == "invalid-from" )
00771 err = StreamErrorInvalidFrom;
00772 else if( (*it)->name() == "invalid-id" )
00773 err = StreamErrorInvalidId;
00774 else if( (*it)->name() == "invalid-namespace" )
00775 err = StreamErrorInvalidNamespace;
00776 else if( (*it)->name() == "invalid-xml" )
00777 err = StreamErrorInvalidXml;
00778 else if( (*it)->name() == "not-authorized" )
00779 err = StreamErrorNotAuthorized;
00780 else if( (*it)->name() == "policy-violation" )
00781 err = StreamErrorPolicyViolation;
00782 else if( (*it)->name() == "remote-connection-failed" )
00783 err = StreamErrorRemoteConnectionFailed;
00784 else if( (*it)->name() == "resource-constraint" )
00785 err = StreamErrorResourceConstraint;
00786 else if( (*it)->name() == "restricted-xml" )
00787 err = StreamErrorRestrictedXml;
00788 else if( (*it)->name() == "see-other-host" )
00789 {
00790 err = StreamErrorSeeOtherHost;
00791 m_streamErrorCData = stanza->findChild( "see-other-host" )->cdata();
00792 }
00793 else if( (*it)->name() == "system-shutdown" )
00794 err = StreamErrorSystemShutdown;
00795 else if( (*it)->name() == "undefined-condition" )
00796 err = StreamErrorUndefinedCondition;
00797 else if( (*it)->name() == "unsupported-encoding" )
00798 err = StreamErrorUnsupportedEncoding;
00799 else if( (*it)->name() == "unsupported-stanza-type" )
00800 err = StreamErrorUnsupportedStanzaType;
00801 else if( (*it)->name() == "unsupported-version" )
00802 err = StreamErrorUnsupportedVersion;
00803 else if( (*it)->name() == "xml-not-well-formed" )
00804 err = StreamErrorXmlNotWellFormed;
00805 else if( (*it)->name() == "text" )
00806 {
00807 const std::string& lang = (*it)->findAttribute( "xml:lang" );
00808 if( !lang.empty() )
00809 m_streamErrorText[lang] = (*it)->cdata();
00810 else
00811 m_streamErrorText["default"] = (*it)->cdata();
00812 }
00813 else
00814 m_streamErrorAppCondition = (*it);
00815
00816 if( err != StreamErrorUndefined && (*it)->hasAttribute( "xmlns", XMLNS_XMPP_STREAM ) )
00817 m_streamError = err;
00818 }
00819 }
00820
00821 const std::string ClientBase::streamErrorText( const std::string& lang ) const
00822 {
00823 StringMap::const_iterator it = m_streamErrorText.find( lang );
00824 return ( it != m_streamErrorText.end() ) ? (*it).second : std::string();
00825 }
00826
00827 void ClientBase::registerMessageSessionHandler( MessageSessionHandler *msh, int types )
00828 {
00829 if( types & StanzaMessageChat || types == 0 )
00830 m_messageSessionHandlerChat = msh;
00831
00832 if( types & StanzaMessageNormal || types == 0 )
00833 m_messageSessionHandlerNormal = msh;
00834
00835 if( types & StanzaMessageGroupchat || types == 0 )
00836 m_messageSessionHandlerGroupchat = msh;
00837
00838 if( types & StanzaMessageHeadline || types == 0 )
00839 m_messageSessionHandlerHeadline = msh;
00840 }
00841
00842 void ClientBase::registerPresenceHandler( PresenceHandler *ph )
00843 {
00844 if( ph )
00845 m_presenceHandlers.push_back( ph );
00846 }
00847
00848 void ClientBase::removePresenceHandler( PresenceHandler *ph )
00849 {
00850 if( ph )
00851 m_presenceHandlers.remove( ph );
00852 }
00853
00854 void ClientBase::registerPresenceHandler( const JID& jid, PresenceHandler *ph )
00855 {
00856 if( ph && jid )
00857 {
00858 JidPresHandlerStruct jph;
00859 jph.jid = new JID( jid.bare() );
00860 jph.ph = ph;
00861 m_presenceJidHandlers.push_back( jph );
00862 }
00863 }
00864
00865 void ClientBase::removePresenceHandler( const JID& jid, PresenceHandler *ph )
00866 {
00867 PresenceJidHandlerList::iterator t;
00868 PresenceJidHandlerList::iterator it = m_presenceJidHandlers.begin();
00869 while( it != m_presenceJidHandlers.end() )
00870 {
00871 t = it;
00872 ++it;
00873 if( ( !ph || (*t).ph == ph ) && (*t).jid->bare() == jid.bare() )
00874 {
00875 delete (*t).jid;
00876 m_presenceJidHandlers.erase( t );
00877 }
00878 }
00879 }
00880
00881 void ClientBase::trackID( IqHandler *ih, const std::string& id, int context )
00882 {
00883 if( ih && !id.empty() )
00884 {
00885 TrackStruct track;
00886 track.ih = ih;
00887 track.context = context;
00888 m_iqIDHandlers[id] = track;
00889 }
00890 }
00891
00892 void ClientBase::removeIDHandler( IqHandler *ih )
00893 {
00894 IqTrackMap::iterator t;
00895 IqTrackMap::iterator it = m_iqIDHandlers.begin();
00896 while( it != m_iqIDHandlers.end() )
00897 {
00898 t = it;
00899 ++it;
00900 if( ih == (*t).second.ih )
00901 m_iqIDHandlers.erase( t );
00902 }
00903 }
00904
00905 void ClientBase::registerIqHandler( IqHandler *ih, const std::string& xmlns )
00906 {
00907 if( ih && !xmlns.empty() )
00908 m_iqNSHandlers[xmlns] = ih;
00909 }
00910
00911 void ClientBase::removeIqHandler( const std::string& xmlns )
00912 {
00913 if( !xmlns.empty() )
00914 m_iqNSHandlers.erase( xmlns );
00915 }
00916
00917 void ClientBase::registerMessageSession( MessageSession *session )
00918 {
00919 if( session )
00920 m_messageSessions.push_back( session );
00921 }
00922
00923 void ClientBase::disposeMessageSession( MessageSession *session )
00924 {
00925 if( !session )
00926 return;
00927
00928 MessageSessionList::iterator it = std::find( m_messageSessions.begin(), m_messageSessions.end(),
00929 session );
00930 if( it != m_messageSessions.end() )
00931 {
00932 delete (*it);
00933 m_messageSessions.erase( it );
00934 }
00935 }
00936
00937 void ClientBase::registerMessageHandler( MessageHandler *mh )
00938 {
00939 if( mh )
00940 m_messageHandlers.push_back( mh );
00941 }
00942
00943 void ClientBase::removeMessageHandler( MessageHandler *mh )
00944 {
00945 if( mh )
00946 m_messageHandlers.remove( mh );
00947 }
00948
00949 void ClientBase::registerSubscriptionHandler( SubscriptionHandler *sh )
00950 {
00951 if( sh )
00952 m_subscriptionHandlers.push_back( sh );
00953 }
00954
00955 void ClientBase::removeSubscriptionHandler( SubscriptionHandler *sh )
00956 {
00957 if( sh )
00958 m_subscriptionHandlers.remove( sh );
00959 }
00960
00961 void ClientBase::registerTagHandler( TagHandler *th, const std::string& tag, const std::string& xmlns )
00962 {
00963 if( th && !tag.empty() )
00964 {
00965 TagHandlerStruct ths;
00966 ths.tag = tag;
00967 ths.xmlns = xmlns;
00968 ths.th = th;
00969 m_tagHandlers.push_back( ths );
00970 }
00971 }
00972
00973 void ClientBase::removeTagHandler( TagHandler *th, const std::string& tag, const std::string& xmlns )
00974 {
00975 if( th )
00976 {
00977 TagHandlerList::iterator it = m_tagHandlers.begin();
00978 for( ; it != m_tagHandlers.end(); ++it )
00979 {
00980 if( (*it).th == th && (*it).tag == tag && (*it).xmlns == xmlns )
00981 m_tagHandlers.erase( it );
00982 }
00983 }
00984 }
00985
00986 void ClientBase::registerStatisticsHandler( StatisticsHandler *sh )
00987 {
00988 if( sh )
00989 m_statisticsHandler = sh;
00990 }
00991
00992 void ClientBase::removeStatisticsHandler()
00993 {
00994 m_statisticsHandler = 0;
00995 }
00996
00997 void ClientBase::registerMUCInvitationHandler( MUCInvitationHandler *mih )
00998 {
00999 if( mih )
01000 {
01001 m_mucInvitationHandler = mih;
01002 m_disco->addFeature( XMLNS_MUC );
01003 }
01004 }
01005
01006 void ClientBase::removeMUCInvitationHandler()
01007 {
01008 m_mucInvitationHandler = 0;
01009 m_disco->removeFeature( XMLNS_MUC );
01010 }
01011
01012 void ClientBase::registerConnectionListener( ConnectionListener *cl )
01013 {
01014 if( cl )
01015 m_connectionListeners.push_back( cl );
01016 }
01017
01018 void ClientBase::removeConnectionListener( ConnectionListener *cl )
01019 {
01020 if( cl )
01021 m_connectionListeners.remove( cl );
01022 }
01023
01024 void ClientBase::notifyOnConnect()
01025 {
01026 ConnectionListenerList::const_iterator it = m_connectionListeners.begin();
01027 for( ; it != m_connectionListeners.end(); ++it )
01028 {
01029 (*it)->onConnect();
01030 }
01031 }
01032
01033 void ClientBase::notifyOnDisconnect( ConnectionError e )
01034 {
01035 ConnectionListenerList::const_iterator it = m_connectionListeners.begin();
01036 for( ; it != m_connectionListeners.end(); ++it )
01037 {
01038 (*it)->onDisconnect( e );
01039 }
01040 init();
01041 }
01042
01043 bool ClientBase::notifyOnTLSConnect( const CertInfo& info )
01044 {
01045 ConnectionListenerList::const_iterator it = m_connectionListeners.begin();
01046 for( ; it != m_connectionListeners.end() && (*it)->onTLSConnect( info ); ++it )
01047 ;
01048 return m_stats.encryption = ( it == m_connectionListeners.end() );
01049 }
01050
01051 void ClientBase::notifyOnResourceBindError( ResourceBindError error )
01052 {
01053 ConnectionListenerList::const_iterator it = m_connectionListeners.begin();
01054 for( ; it != m_connectionListeners.end(); ++it )
01055 {
01056 (*it)->onResourceBindError( error );
01057 }
01058 }
01059
01060 void ClientBase::notifyOnSessionCreateError( SessionCreateError error )
01061 {
01062 ConnectionListenerList::const_iterator it = m_connectionListeners.begin();
01063 for( ; it != m_connectionListeners.end(); ++it )
01064 {
01065 (*it)->onSessionCreateError( error );
01066 }
01067 }
01068
01069 void ClientBase::notifyStreamEvent( StreamEvent event )
01070 {
01071 ConnectionListenerList::const_iterator it = m_connectionListeners.begin();
01072 for( ; it != m_connectionListeners.end(); ++it )
01073 {
01074 (*it)->onStreamEvent( event );
01075 }
01076 }
01077
01078 void ClientBase::notifyPresenceHandlers( Stanza *stanza )
01079 {
01080 bool match = false;
01081 PresenceJidHandlerList::const_iterator itj = m_presenceJidHandlers.begin();
01082 for( ; itj != m_presenceJidHandlers.end(); ++itj )
01083 {
01084 if( (*itj).jid->bare() == stanza->from().bare() && (*itj).ph )
01085 {
01086 (*itj).ph->handlePresence( stanza );
01087 match = true;
01088 }
01089 }
01090 if( match )
01091 return;
01092
01093 PresenceHandlerList::const_iterator it = m_presenceHandlers.begin();
01094 for( ; it != m_presenceHandlers.end(); ++it )
01095 {
01096 (*it)->handlePresence( stanza );
01097 }
01098 }
01099
01100 void ClientBase::notifySubscriptionHandlers( Stanza *stanza )
01101 {
01102 SubscriptionHandlerList::const_iterator it = m_subscriptionHandlers.begin();
01103 for( ; it != m_subscriptionHandlers.end(); ++it )
01104 {
01105 (*it)->handleSubscription( stanza );
01106 }
01107 }
01108
01109 void ClientBase::notifyIqHandlers( Stanza *stanza )
01110 {
01111 bool res = false;
01112
01113 IqHandlerMap::const_iterator it = m_iqNSHandlers.begin();
01114 for( ; it != m_iqNSHandlers.end(); ++it )
01115 {
01116 if( stanza->hasChildWithAttrib( "xmlns", (*it).first ) )
01117 {
01118 if( (*it).second->handleIq( stanza ) )
01119 res = true;
01120 }
01121 }
01122
01123 IqTrackMap::iterator it_id = m_iqIDHandlers.find( stanza->id() );
01124 if( it_id != m_iqIDHandlers.end() )
01125 {
01126 if( (*it_id).second.ih->handleIqID( stanza, (*it_id).second.context ) )
01127 res = true;
01128 m_iqIDHandlers.erase( it_id );
01129 }
01130
01131 if( !res && ( stanza->type() == StanzaIq ) &&
01132 ( ( stanza->subtype() == StanzaIqGet ) || ( stanza->subtype() == StanzaIqSet ) ) )
01133 {
01134 Tag *iq = new Tag( "iq" );
01135 iq->addAttribute( "type", "error" );
01136 iq->addAttribute( "id", stanza->id() );
01137 iq->addAttribute( "to", stanza->from().full() );
01138 Tag *e = new Tag( iq, "error", "type", "cancel", false );
01139 new Tag( e, "service-unavailable", "xmlns", XMLNS_XMPP_STANZAS );
01140 send( iq );
01141 }
01142 }
01143
01144 void ClientBase::notifyMessageHandlers( Stanza *stanza )
01145 {
01146 if( m_mucInvitationHandler )
01147 {
01148 Tag *x = stanza->findChild( "x", "xmlns", XMLNS_MUC_USER );
01149 if( x && x->hasChild( "invite" ) )
01150 {
01151 Tag *i = x->findChild( "invite" );
01152 JID invitee( i->findAttribute( "from" ) );
01153
01154 Tag * t = i->findChild( "reason" );
01155 std::string reason ( t ? t->cdata() : "" );
01156
01157 t = x->findChild( "password" );
01158 std::string password ( t ? t->cdata() : "" );
01159
01160 m_mucInvitationHandler->handleMUCInvitation( stanza->from(), invitee,
01161 reason, stanza->body(), password,
01162 i->hasChild( "continue" ) );
01163 return;
01164 }
01165 }
01166
01167 MessageSessionList::const_iterator it1 = m_messageSessions.begin();
01168 for( ; it1 != m_messageSessions.end(); ++it1 )
01169 {
01170 if( (*it1)->target().full() == stanza->from().full() &&
01171 ( stanza->thread().empty() || (*it1)->threadID() == stanza->thread() ) &&
01172 ( (*it1)->types() & stanza->subtype() || (*it1)->types() == StanzaSubUndefined ) )
01173 {
01174 (*it1)->handleMessage( stanza );
01175 return;
01176 }
01177 }
01178
01179 it1 = m_messageSessions.begin();
01180 for( ; it1 != m_messageSessions.end(); ++it1 )
01181 {
01182 if( (*it1)->target().bare() == stanza->from().bare() &&
01183 ( stanza->thread().empty() || (*it1)->threadID() == stanza->thread() ) &&
01184 ( (*it1)->types() & stanza->subtype() || (*it1)->types() == StanzaSubUndefined ) )
01185 {
01186 (*it1)->handleMessage( stanza );
01187 return;
01188 }
01189 }
01190
01191 MessageSessionHandler *msHandler = 0;
01192
01193 switch( stanza->subtype() )
01194 {
01195 case StanzaMessageChat:
01196 msHandler = m_messageSessionHandlerChat;
01197 break;
01198 case StanzaMessageNormal:
01199 msHandler = m_messageSessionHandlerNormal;
01200 break;
01201 case StanzaMessageGroupchat:
01202 msHandler = m_messageSessionHandlerGroupchat;
01203 break;
01204 case StanzaMessageHeadline:
01205 msHandler = m_messageSessionHandlerHeadline;
01206 break;
01207 default:
01208 break;
01209 }
01210
01211 if( msHandler )
01212 {
01213 MessageSession *session = new MessageSession( this, stanza->from(), true, stanza->subtype() );
01214 msHandler->handleMessageSession( session );
01215 session->handleMessage( stanza );
01216 }
01217 else
01218 {
01219 MessageHandlerList::const_iterator it = m_messageHandlers.begin();
01220 for( ; it != m_messageHandlers.end(); ++it )
01221 {
01222 (*it)->handleMessage( stanza );
01223 }
01224 }
01225 }
01226
01227 void ClientBase::notifyTagHandlers( Tag *tag )
01228 {
01229 TagHandlerList::const_iterator it = m_tagHandlers.begin();
01230 for( ; it != m_tagHandlers.end(); ++it )
01231 {
01232 if( (*it).tag == tag->name() && tag->hasAttribute( "xmlns", (*it).xmlns ) )
01233 (*it).th->handleTag( tag );
01234 }
01235 }
01236
01237 CompressionBase* ClientBase::getDefaultCompression()
01238 {
01239 if( !m_compress )
01240 return 0;
01241
01242 #ifdef HAVE_ZLIB
01243 return new CompressionZlib( this );
01244 #else
01245 return 0;
01246 #endif
01247 }
01248
01249 TLSBase* ClientBase::getDefaultEncryption()
01250 {
01251 if( m_tls == TLSDisabled || !hasTls() )
01252 return 0;
01253
01254 return new TLSDefault( this, m_server );
01255 }
01256
01257 }