clientbase.cpp

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

Generated on Sat Nov 10 08:50:26 2007 for gloox by  doxygen 1.5.3-20071008