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