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