clientbase.cpp

00001 /*
00002   Copyright (c) 2005-2008 by Jakob Schroeter <js@camaya.net>
00003   This file is part of the gloox library. http://camaya.net/gloox
00004 
00005   This software is distributed under a license. The full license
00006   agreement can be found in the file LICENSE in this distribution.
00007   This software may not be copied, modified, sold or distributed
00008   other than expressed in the named license agreement.
00009 
00010   This software is distributed without any warranty.
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* /*base*/, 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* /*base*/, 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* /*base*/, 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* /*connection*/, 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* /*connection*/ )
00324   {
00325     header();
00326     if( m_block && m_connection )
00327     {
00328       m_connection->receive();
00329     }
00330   }
00331 
00332   void ClientBase::handleDisconnect( const ConnectionBase* /*connection*/, 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 // The client calls GSS_Init_sec_context, passing in 0 for
00453 // input_context_handle (initially) and a targ_name equal to output_name
00454 // from GSS_Import_Name called with input_name_type of
00455 // GSS_C_NT_HOSTBASED_SERVICE and input_name_string of
00456 // "service@hostname" where "service" is the service name specified in
00457 // the protocol's profile, and "hostname" is the fully qualified host
00458 // name of the server.  The client then responds with the resulting
00459 // output_token.
00460         std::string token;
00461         a->setCData( Base64::encode64( token ) );
00462 //         etc... see gssapi-sasl-draft.txt
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         // see gssapi-sasl-draft.txt
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         // should never happen.
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 //     if( m_connection )
00654 //       m_connection->getStatistics( m_stats.totalBytesReceived, m_stats.totalBytesSent,
00655 //                                    m_stats.compressedBytesReceived, m_stats.compressedBytesSent,
00656 //                                    m_stats.uncompressedBytesReceived, m_stats.uncompressedBytesSent,
00657 //                                    m_stats.compression );
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 }

Generated on Sun Apr 27 11:08:13 2008 for gloox by  doxygen 1.5.5