gloox 1.0

clientbase.cpp

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