📄 clientbase.cpp
字号:
/* Copyright (c) 2005-2008 by Jakob Schroeter <js@camaya.net> This file is part of the gloox library. http://camaya.net/gloox This software is distributed under a license. The full license agreement can be found in the file LICENSE in this distribution. This software may not be copied, modified, sold or distributed other than expressed in the named license agreement. This software is distributed without any warranty.*/#ifdef _WIN32# include "../config.h.win"#elif defined( _WIN32_WCE )# include "../config.h.win"#else# include "config.h"#endif#include "clientbase.h"#include "connectionbase.h"#include "tlsbase.h"#include "compressionbase.h"#include "connectiontcpclient.h"#include "disco.h"#include "messagesessionhandler.h"#include "parser.h"#include "tag.h"#include "stanza.h"#include "connectionlistener.h"#include "iqhandler.h"#include "messagehandler.h"#include "presencehandler.h"#include "rosterlistener.h"#include "subscriptionhandler.h"#include "loghandler.h"#include "taghandler.h"#include "mucinvitationhandler.h"#include "jid.h"#include "base64.h"#include "md5.h"#include "tlsdefault.h"#include "compressionzlib.h"#include <cstdlib>#include <string>#include <map>#include <list>#include <algorithm>#ifndef _WIN32_WCE# include <sstream># include <iomanip>#endifnamespace gloox{ ClientBase::ClientBase( const std::string& ns, const std::string& server, int port ) : m_connection( 0 ), m_encryption( 0 ), m_compression( 0 ), m_disco( 0 ), m_namespace( ns ), m_xmllang( "en" ), m_server( server ), m_compressionActive( false ), m_encryptionActive( false ), m_compress( true ), m_authed( false ), m_sasl( true ), m_tls( TLSOptional ), m_port( port ), m_availableSaslMechs( SaslMechAll ), m_statisticsHandler( 0 ), m_mucInvitationHandler( 0 ), m_messageSessionHandlerChat( 0 ), m_messageSessionHandlerGroupchat( 0 ), m_messageSessionHandlerHeadline( 0 ), m_messageSessionHandlerNormal( 0 ), m_parser( 0 ), m_authError( AuthErrorUndefined ), m_streamError( StreamErrorUndefined ), m_streamErrorAppCondition( 0 ), m_selectedSaslMech( SaslMechNone ), m_idCount( 0 ), m_autoMessageSession( false ) { init(); } ClientBase::ClientBase( const std::string& ns, const std::string& password, const std::string& server, int port ) : m_connection( 0 ), m_encryption( 0 ), m_compression( 0 ), m_disco( 0 ), m_namespace( ns ), m_password( password ), m_xmllang( "en" ), m_server( server ), m_compressionActive( false ), m_encryptionActive( false ), m_compress( true ), m_authed( false ), m_block( false ), m_sasl( true ), m_tls( TLSOptional ), m_port( port ), m_availableSaslMechs( SaslMechAll ), m_statisticsHandler( 0 ), m_mucInvitationHandler( 0 ), m_messageSessionHandlerChat( 0 ), m_messageSessionHandlerGroupchat( 0 ), m_messageSessionHandlerHeadline( 0 ), m_messageSessionHandlerNormal( 0 ), m_parser( 0 ), m_authError( AuthErrorUndefined ), m_streamError( StreamErrorUndefined ), m_streamErrorAppCondition( 0 ), m_selectedSaslMech( SaslMechNone ), m_idCount( 0 ), m_autoMessageSession( false ) { init(); } void ClientBase::init() { if( !m_disco ) { m_disco = new Disco( this ); m_disco->setVersion( "based on gloox", GLOOX_VERSION ); } m_streamError = StreamErrorUndefined; m_block = false; m_stats.totalBytesSent = 0; m_stats.totalBytesReceived = 0; m_stats.compressedBytesSent = 0; m_stats.compressedBytesReceived = 0; m_stats.uncompressedBytesSent = 0; m_stats.uncompressedBytesReceived = 0; m_stats.totalStanzasSent = 0; m_stats.totalStanzasReceived = 0; m_stats.iqStanzasSent = 0; m_stats.iqStanzasReceived = 0; m_stats.messageStanzasSent = 0; m_stats.messageStanzasReceived = 0; m_stats.s10nStanzasSent = 0; m_stats.s10nStanzasReceived = 0; m_stats.presenceStanzasSent = 0; m_stats.presenceStanzasReceived = 0; m_stats.encryption = false; m_stats.compression = false; cleanup(); } ClientBase::~ClientBase() { delete m_connection; delete m_encryption; delete m_compression; delete m_parser; delete m_disco; MessageSessionList::const_iterator it = m_messageSessions.begin(); for( ; it != m_messageSessions.end(); ++it ) delete (*it); PresenceJidHandlerList::const_iterator it1 = m_presenceJidHandlers.begin(); for( ; it1 != m_presenceJidHandlers.end(); ++it1 ) delete (*it1).jid; } ConnectionError ClientBase::recv( int timeout ) { if( !m_connection || m_connection->state() == StateDisconnected ) return ConnNotConnected; return m_connection->recv( timeout ); } bool ClientBase::connect( bool block ) { if( m_server.empty() ) return false; if( !m_parser ) m_parser = new Parser( this ); if( !m_connection ) m_connection = new ConnectionTCPClient( this, m_logInstance, m_server, m_port ); if( m_connection->state() >= StateConnecting ) return true; if( !m_encryption ) m_encryption = getDefaultEncryption(); if( m_encryption ) { m_encryption->setCACerts( m_cacerts ); m_encryption->setClientCert( m_clientKey, m_clientCerts ); } if( !m_compression ) m_compression = getDefaultCompression(); m_logInstance.log( LogLevelDebug, LogAreaClassClientbase, "This is gloox " + GLOOX_VERSION + ", connecting..." ); m_block = block; ConnectionError ret = m_connection->connect(); return ret == ConnNoError; } void ClientBase::handleTag( Tag *tag ) { if( !tag ) { logInstance().log( LogLevelDebug, LogAreaClassClientbase, "stream closed" ); disconnect( ConnStreamClosed ); return; } Stanza *stanza = new Stanza( tag ); logInstance().log( LogLevelDebug, LogAreaXmlIncoming, stanza->xml() ); ++m_stats.totalStanzasReceived; if( tag->name() == "stream:stream" ) { const std::string& version = stanza->findAttribute( "version" ); if( !checkStreamVersion( version ) ) { logInstance().log( LogLevelDebug, LogAreaClassClientbase, "This server is not XMPP-compliant" " (it does not send a 'version' attribute). Please fix it or try another one.\n" ); disconnect( ConnStreamVersionError ); } m_sid = stanza->findAttribute( "id" ); handleStartNode(); } else if( tag->name() == "stream:error" ) { handleStreamError( stanza ); disconnect( ConnStreamError ); } else { if( !handleNormalNode( stanza ) ) { switch( stanza->type() ) { case StanzaIq: notifyIqHandlers( stanza ); ++m_stats.iqStanzasReceived; break; case StanzaPresence: notifyPresenceHandlers( stanza ); ++m_stats.presenceStanzasReceived; break; case StanzaS10n: notifySubscriptionHandlers( stanza ); ++m_stats.s10nStanzasReceived; break; case StanzaMessage: notifyMessageHandlers( stanza ); ++m_stats.messageStanzasReceived; break; default: notifyTagHandlers( tag ); break; } } } if( m_statisticsHandler ) m_statisticsHandler->handleStatistics( getStatistics() ); delete stanza; } void ClientBase::handleCompressedData( const std::string& data ) { if( m_encryption && m_encryptionActive ) m_encryption->encrypt( data ); else if( m_connection ) m_connection->send( data ); else m_logInstance.log( LogLevelError, LogAreaClassClientbase, "Compression finished, but chain broken" ); } void ClientBase::handleDecompressedData( const std::string& data ) { if( m_parser ) parse( data ); else m_logInstance.log( LogLevelError, LogAreaClassClientbase, "Decompression finished, but chain broken" ); } void ClientBase::handleEncryptedData( const TLSBase* /*base*/, const std::string& data ) { if( m_connection ) m_connection->send( data ); else m_logInstance.log( LogLevelError, LogAreaClassClientbase, "Encryption finished, but chain broken" ); } void ClientBase::handleDecryptedData( const TLSBase* /*base*/, const std::string& data ) { if( m_compression && m_compressionActive ) m_compression->decompress( data ); else if( m_parser ) parse( data ); else m_logInstance.log( LogLevelError, LogAreaClassClientbase, "Decryption finished, but chain broken" ); } void ClientBase::handleHandshakeResult( const TLSBase* /*base*/, bool success, CertInfo &certinfo ) { if( success ) { if( !notifyOnTLSConnect( certinfo ) ) { logInstance().log( LogLevelError, LogAreaClassClientbase, "Server's certificate rejected!" ); disconnect( ConnTlsFailed ); } else { logInstance().log( LogLevelDebug, LogAreaClassClientbase, "connection encryption active" ); header(); } } else { logInstance().log( LogLevelError, LogAreaClassClientbase, "TLS handshake failed!" ); disconnect( ConnTlsFailed ); } } void ClientBase::handleReceivedData( const ConnectionBase* /*connection*/, const std::string& data ) { if( m_encryption && m_encryptionActive ) m_encryption->decrypt( data ); else if( m_compression && m_compressionActive ) m_compression->decompress( data ); else if( m_parser ) parse( data ); else m_logInstance.log( LogLevelError, LogAreaClassClientbase, "Received data, but chain broken" ); } void ClientBase::handleConnect( const ConnectionBase* /*connection*/ ) { header(); if( m_block && m_connection ) { m_connection->receive(); } } void ClientBase::handleDisconnect( const ConnectionBase* /*connection*/, ConnectionError reason ) { if( m_connection ) m_connection->cleanup(); notifyOnDisconnect( reason ); } void ClientBase::disconnect( ConnectionError reason ) { if( m_connection && m_connection->state() >= StateConnecting ) { if( reason != ConnTlsFailed ) send( "</stream:stream>" ); m_connection->disconnect(); m_connection->cleanup(); if( m_encryption ) m_encryption->cleanup(); m_encryptionActive = false; m_compressionActive = false; notifyOnDisconnect( reason ); } } void ClientBase::parse( const std::string& data ) { if( m_parser && !m_parser->feed( data ) ) { m_logInstance.log( LogLevelError, LogAreaClassClientbase, "parse error: " + data ); Tag* e = new Tag( "stream:error" ); new Tag( e, "restricted-xml", "xmlns", XMLNS_XMPP_STREAM ); send( e ); disconnect( ConnParseError ); } } void ClientBase::header() { std::string head = "<?xml version='1.0' ?>"; head += "<stream:stream to='" + m_jid.server() + "' xmlns='" + m_namespace + "' "; head += "xmlns:stream='http://etherx.jabber.org/streams' xml:lang='" + m_xmllang + "' "; head += "version='" + XMPP_STREAM_VERSION_MAJOR + "." + XMPP_STREAM_VERSION_MINOR + "'>"; send( head ); } bool ClientBase::hasTls() {#if defined( HAVE_GNUTLS ) || defined( HAVE_OPENSSL ) || defined( HAVE_WINTLS ) return true;#else return false;#endif } void ClientBase::startTls() { Tag *start = new Tag( "starttls" ); start->addAttribute( "xmlns", XMLNS_STREAM_TLS ); send( start ); } void ClientBase::setServer( const std::string &server ) { m_server = server; if( m_connection ) m_connection->setServer( server ); } void ClientBase::setClientCert( const std::string& clientKey, const std::string& clientCerts ) { m_clientKey = clientKey; m_clientCerts = clientCerts; } void ClientBase::startSASL( SaslMechanism type ) { m_selectedSaslMech = type; Tag *a = new Tag( "auth" ); a->addAttribute( "xmlns", XMLNS_STREAM_SASL ); switch( type ) { case SaslMechDigestMd5: a->addAttribute( "mechanism", "DIGEST-MD5" ); break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -