/*
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>
#endif
namespace 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_pa