/***************************************************************************
                          cclient.cpp  -  description
                             -------------------
    begin                : Sun Sep 30 2001
    copyright            : (C) 2001-2004 by Mathias Küster
    email                : mathen@users.berlios.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "cclient.h"

#include <stdio.h>
#include <sys/types.h>

#ifndef WIN32
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#endif

#include "core/clist.h"
#include "dcobject.h"
#include "cquerymanager.h"
#include "cdownloadmanager.h"
#include "cconnectionmanager.h"
#include "cencrypt.h"
#include "cmessagehandler.h"
#include "cconfig.h"
#include "csearchmanager.h"
#include "clistenmanager.h"
#include "dclib-ssl-use.h"

// need to decompress zpipe data
#include "core/czlib.h"
#include "core/cbytearray.h"

// for DPRINTF
#include "dclib.h"

// IsPrivateI4() and IsValidI4()
#include "core/cnetaddr.h"

CClient::CClient( CString remoteEncoding ) : CDCProto( remoteEncoding )
{
	m_pCallback = 0;
	
	m_bHandshake       = true;
	m_bHandleUserList  = true;
	m_bHandleSearch    = true;
	m_bHandleMyinfo    = true;
	m_bHandleForceMove = true;
	m_bHandleTransfer  = true;
	m_bUpdateMyinfo    = false;
	m_bSendMyinfo      = true;
	m_bSSLMode         = false;
	m_nNickListHandler = 0;
	m_bUsedPassword    = false;

	// set reconnect parameter
	m_eReconnectState      = ersNONE;
	m_nReconnectCount      = 0;
	m_timeReconnectTimeout = 0;
	m_timeMyinfoTimeout    = 0;
	
	// initialise settings used since hub supports message cannot be used
	m_bExtProtocol = false;
	m_bGotIP       = false;
	
	// zpipe handling
	m_pZLib = 0;
	m_bZMode = false;
	
	// message parser
	m_pMessageHandler = new CMessageHandler( remoteEncoding );

#if DCLIB_HAS_SSL == 1	
	if ( CConfig::Instance() )
	{
		m_MyInfo.m_bTLSFlag = ( CConfig::Instance()->GetTransferCert().NotEmpty() && CConfig::Instance()->GetTransferKey().NotEmpty() );
	}
#endif
}

CClient::~CClient()
{
	SetCallBackFunction(0);
	
	delete m_pZLib;
	m_pZLib = 0;
	
	delete m_pMessageHandler;
	m_pMessageHandler = 0;
}

/** */
void CClient::ConnectionState( eConnectionState state )
{
	int err = -1;

	CMessageConnectionState *Object = new CMessageConnectionState();

	if ( (state == estCONNECTED) ||
	     (state == estDISCONNECTED) )
	{
		// reset all values
		m_sBuffer.Empty();
		m_bHandshake        = true;
		m_timeMyinfoTimeout = time(0);
		m_nNickListHandler  = 0;

		m_bExtProtocol = false;
		m_bGotIP       = false;

		m_UserList.Clear();
		
		m_bZMode = false;
		delete m_pZLib;
		m_pZLib = 0;
#if DCLIB_HAS_SSL == 1
		m_pMessageSSL.Init();
#endif
		// update reconnect state
		if ( state == estCONNECTED )
		{
			UpdateReconnect( ersNONE );

			if ( m_bSSLMode && CConfig::Instance() )
			{
				if ( ChangeSocketMode( esmSSLCLIENT, CConfig::Instance()->GetTransferCert(), CConfig::Instance()->GetTransferKey() ) == false )
				{
					printf("change ssl mode failed\n");
				}
			}
		}
		else
		{
			if ( m_eReconnectState == ersFORCEMOVE )
			{
				UpdateReconnect( ersNONE );
			}
			else
			{
				UpdateReconnect( ersENABLED );
			}
		}
	}

	Object->m_eState   = state;
	Object->m_sMessage = GetSocketError();

	if ( m_pCallback != 0 )
	{
		err = m_pCallback->notify( this, Object );
	}
	else
	{
		err = DC_CallBack( Object );
	}

	// callback failed
	if ( err == -1 )
	{
		delete Object;
	}

	// update myinfo
	if ( m_bHandleMyinfo && CConnectionManager::Instance() )
	{
		CConnectionManager::Instance()->SendMyInfoToConnectedServers();
	}
}

/** */
void CClient::DataAvailable( const char * buffer, int len )
{
	CString s;
	int i = 0;

	if ( len <= 0 )
	{
		return;
	}

	// add traffic control
	CSocket::m_Traffic.AddTraffic(ettCONTROLRX,len);

	if ( m_bZMode )
	{
		int inlen = len;
		CString trailing;
		
		if ( m_pZLib == 0 )
		{
			m_pZLib = new CZLib();
		}
		
		int outlen = 1024*100; // same as what CTransfer uses
		CByteArray * out = new CByteArray(outlen);
		
		//printf( "before inlen=%d outlen=%d ", inlen, outlen );
		const int zlibstatus = m_pZLib->InflateZBlock( buffer, &inlen, (char*)out->Data(), &outlen );
		//printf( "after inlen=%d outlen=%d\n", inlen, outlen );
		
		if ( zlibstatus == -1 )
		{
			printf("CClient: ZPipe decompress error, returning to normal mode\n");
			delete m_pZLib;
			m_pZLib = 0;
			m_bZMode = false;
		}
		else if ( zlibstatus == 0 )
		{
			//printf("ZPipe: finished decompressing data, leaving zmode\n");
			delete m_pZLib;
			m_pZLib = 0;
			m_bZMode = false;
			
			/*
			 * Check for uncompressed data after compressed data.
			 * In 0.3.19 there was usually data after the compressed data
			 * which got ignored, because it put the compressed data
			 * into m_sBuffer and did not send it to zlib until
			 * some more data was received.
			 */
			if ( inlen < len )
			{
				trailing.Set( buffer+inlen, len-inlen );
			}
		}
		
		if ( ((zlibstatus == 0) || (zlibstatus == 1)) && (outlen > 0) )
		{
			s = m_sBuffer + CString().Set((char*)out->Data(),outlen);
			m_sBuffer.Empty();
			
			if ( trailing.NotEmpty() )
			{
				s += trailing;
			}
			
			//printf("ZPipe result: '%s'\n",s.Data());
		}
		
		delete out;
	}
	else
	{
		s = m_sBuffer + CString().Set(buffer,len);
		
		/* contents of buffer have been used, empty it to make sure it does not get used again */
		m_sBuffer.Empty();
	}
	
	/* If ZPipe is enabled, we must search forwards for $ZOn|
	 * in case there is a | within the compressed data.
	 *
	 * And since hub supports negotiation is useless, we had better
	 * assume ZPipe is always enabled.
	 */
	i = s.Find("$ZOn|");
	if ( i >= 0 )
	{
		/* Handle all messages up to but not including $ZOn| */
		HandleMessage( s.Mid(0,i) );
		
		/* Handle $ZOn| and advance i */
		m_bZMode = true;
		i += 5;
		
		if ( i < s.Length() )
		{
			/*
			 * Handle the compressed data.
			 */
			DataAvailable( s.Data()+i, s.Length()-i );
		}
		
		return;
	}
	
	/* No ZPipe support or $ZOn| not found */
	i = s.FindRev('|') + 1;
	
	if ( i > 0 )
	{
		HandleMessage( s.Mid(0,i) );
	}
	
	if ( i < s.Length() )
	{
		m_sBuffer = s.Mid( i, s.Length()-i );
	}
}

/** */
void CClient::DataTimeout()
{
	SendString("|");
}

/** */
void CClient::Notify()
{
	if ( !CConfig::Instance() )
		return;

	// check away mode
	if ( (m_bHandshake == false) && ((time(0)-m_timeMyinfoTimeout) >= 30) )
	{
		if ( CConfig::Instance()->GetAwayMode() != m_MyInfo.m_eAwayMode)
		{
			m_MyInfo.m_eAwayMode = CConfig::Instance()->GetAwayMode();
			m_bUpdateMyinfo = true;
		}
	}

	if ( (m_bHandshake == false) && m_bUpdateMyinfo && ((time(0)-m_timeMyinfoTimeout) >= 30) )
	{
		m_bUpdateMyinfo     = false;
		m_timeMyinfoTimeout = time(0);

		if ( m_bSendMyinfo )
			SendMyInfo( &m_MyInfo );
	}

	if ( m_eReconnectState == ersENABLED )
	{
		if ( m_nReconnectCount >= CConfig::Instance()->GetReconnectCount() )
		{
			UpdateReconnect( ersNONE, 0 );
		}
		else
		{
			if ( GetConnectionState() != estNONE )
			{
				printf("warning, wrong reconnect state, you are connected !\n");
			}

			// init timer
			if ( m_timeReconnectTimeout == 0 )
			{
				m_timeReconnectTimeout = time(0);
			}

			if ( (time(0)-m_timeReconnectTimeout) >= CConfig::Instance()->GetReconnectTimeout() )
			{
				UpdateReconnect( ersNONE );

				if ( CConfig::Instance()->GetReconnectCount() != 9999 )
				{
					m_nReconnectCount++;
				}

				Connect();
			}
		}
	}
}

/** */
void CClient::UpdateReconnect( eReconnectState state, int count )
{
	if ( !CConfig::Instance() )
		return;

	// reconnect disabled
	if ( CConfig::Instance()->GetReconnectCount() == 0 )
	{
		m_eReconnectState = ersNONE;
		return;
	}

	// don't change state if reconnect disables
	if ( (m_eReconnectState == ersDISABLED) && (state != ersNONE) )
	{
		return;
	}

	// update state
	m_eReconnectState      = state;
	// reset timeout
	m_timeReconnectTimeout = 0;
	// update counter
	if ( count != -1 )
	{
		m_nReconnectCount = count;
	}
}

/** */
int CClient::HandleMessage( const CString & message )
{
	int err;
	int pointer;
	eDCMessage type;
	CDCMessage * Object = 0;
	CString sAnswer,s;

	if ( message.IsEmpty() )
	{
		return 0;
	}

	pointer = 0;

	while( (type=m_pMessageHandler->Parse(&message,pointer,&Object)) != DC_MESSAGE_PARSE_ERROR )
	{
		if ( !Object )
		{
			continue;
		}

		switch (type)
		{
			case DC_MESSAGE_LOCK:
			{
				CMessageLock * msg = (CMessageLock*)Object;

				// only handle the first lock in handshake mode
				if ( m_bHandshake )
				{
#if DCLIB_HAS_SSL == 1
					if ( m_bSSLMode )
					{
						SendSSLInfo();
					}
#endif
					CEncrypt::Encrypt(msg->m_sData,sAnswer);

					if ( msg->m_bExtProtocol )
					{
						/*
						 * Assumes NoHello NoGetINFO are supported.
						 * The hub may not inform us what is actually enabled in its supports.
						 */
						m_bExtProtocol = true;
						
						// HubTopic No $Hellos No $GetINFO UserCommand
						/*
						 * UserIP2 now enabled always, if disabled we just do not use
						 * the value the hub gives us, but it gets stored in case
						 * the setting is changed without reconnecting to the hub.
						 *
						 * Similarly TTHSearch is always enabled because CConfig::Instance()->GetDisableHashList()
						 * only affects the hashing of new files. However NOTTH is added to the tag.
						 */
						std::vector<CString> supported;
						supported.push_back("HubTopic");
						supported.push_back("NoHello");
						supported.push_back("NoGetINFO");
						supported.push_back("TTHSearch");
						supported.push_back("UserCommand");
						supported.push_back("UserIP2");
						
						if ( CConfig::Instance() != 0 )
						{
							if ( CConfig::Instance()->GetZPipeEnabled() )
							{
								// this is what DC++ 0.699 sends
								// it may change later after testing finishes
								supported.push_back("ZPipe0");
							}
							
							if ( CConfig::Instance()->GetCompressedTransfers() )
							{
								supported.push_back("GetZBlock");
							}
						}

						bool shuffle = true;
						if ( CConfig::Instance() )
						{
							shuffle = CConfig::Instance()->GetShuffleSupports();
						}
						SendSupports( supported, shuffle );
						// quicklist ??? http://forum.dcstats.net/showthread.php?s=&threadid=802
					}

					SendKey( sAnswer );

					SendValidateNick( m_MyInfo.m_sNick );
				}

				break;
			}

			case DC_MESSAGE_LOGEDIN:
			{
				// update myinfo
//				if ( m_bHandleMyinfo )
//					CConnectionManager::Instance()->SendMyInfoToConnectedServers();

				break;
			}

			case DC_MESSAGE_HUBNAME:
			{
				CMessageHubName * msg = (CMessageHubName*)Object;

				// set the old hubname in this message
				if ( GetHubName() != msg->m_sHubName )
					msg->m_sOldHubName = GetHubName();
				SetHubName(msg->m_sHubName);

				break;
			}

			case DC_MESSAGE_HUB_TOPIC:
			{
				CMessageHubTopic * msg = (CMessageHubTopic*) Object;

				SetHubTopic(msg->m_sTopic);

				break;
			}

			case DC_MESSAGE_HELLO:
			{
				CMessageHello * msg = (CMessageHello*)Object;

				if ( (msg->m_sNick == m_MyInfo.m_sNick) && m_bHandshake )
				{
					SendVersion();

					// get nicklist
					if ( m_bHandleUserList )
						RequestNickList();

					if ( m_bSendMyinfo )
						SendMyInfo( &m_MyInfo );

					m_bHandshake = false;

					// update myinfo
					if ( m_bHandleMyinfo && CConnectionManager::Instance() )
						CConnectionManager::Instance()->SendMyInfoToConnectedServers();
				}
				else if ( m_bHandleUserList )
				{
					m_UserList.AppendUser(msg->m_sNick);
				}

				break;
			}

			case DC_MESSAGE_MYINFO:
			{
				CMessageMyInfo * msg = (CMessageMyInfo*)Object;

				if ( m_bHandleUserList )
				{
					if ( m_UserList.UpdateUser(msg) == false )
					{
						delete Object;
						Object = 0;
					}
				}

				break;
			}

			case DC_MESSAGE_QUIT:
			{
				CMessageQuit * msg = (CMessageQuit*)Object;

				if ( m_bHandleUserList )
				{
					m_UserList.RemoveUser(msg->m_sNick);
#if DCLIB_HAS_SSL == 1
					m_pMessageSSL.LeaveHub(this,msg->m_sNick);
#endif
				}

				break;
			}

			case DC_MESSAGE_NICKLIST:
			{
				CMessageNickList * msg = (CMessageNickList*)Object;

				if ( m_bHandleUserList )
				{
					// 0: update nicklist 1: requested nicklist
					if ( m_nNickListHandler == 1 )
					{
						m_UserList.Clear();

						m_nNickListHandler = 0;
					}

					m_UserList.InitUserList(msg);
					
					if ( m_bExtProtocol == false )
					{
						for ( CString * nick = 0; (nick=msg->m_NickList.Next(nick))!=0; )
						{
							SendGetInfo(*nick,GetNick());
						}
					}
				}

				break;
			}

			case DC_MESSAGE_OPLIST:
			{
				CMessageOpList * msg = (CMessageOpList*)Object;

				// operators allready in the userlist
				if ( m_bHandleUserList )
				{
					// init operator list
					m_UserList.InitOperatorList(msg);
				}
				break;
			}

			case DC_MESSAGE_SEARCH_FILE:
			{
				bool search = true;
				CMessageSearchFile * msg = (CMessageSearchFile*)Object;

				if ( m_bHandleSearch )
				{
					// check for loopback search
					if ( msg->m_bLocal )
					{
						if ( msg->m_sSource == GetNick() )
						{
							search = false;
						}
					}
					else
					{
						// disable global search, compare search with own ip and port
						// also check private address space settings and IP
						if ( CConfig::Instance() )
						{
							if ( (msg->m_nPort == CConfig::Instance()->GetUDPListenPort()) &&
							     (msg->m_sSource == GetExternalIP(false)) )
							{
								search = false;
							}
							else if ( ( CConfig::Instance()->GetCheckPrivateAddressSpace() && 
							            CNetAddr::IsPrivateI4(msg->m_sSource.Data()) ) ||
								  ( CConfig::Instance()->GetPrivateAddressSpaceOnly() &&
								    (CNetAddr::IsPrivateI4(msg->m_sSource.Data()) == false)) )
							{
								DPRINTF("Ignoring search from %s due to private address space settings\n",msg->m_sSource.Data());
								search = false;
							}
						}
					}

					if ( search && (CQueryManager::Instance() != 0) )
					{
						CQueryManager::Instance()->SearchQuery( GetHubName(), GetResolvedIP(), GetNick(), msg );
					}
				}

				break;
			}

			case DC_MESSAGE_SEARCHRESULT:
			{
				// fix hub name
				CMessageSearchResult * msg = (CMessageSearchResult *) Object;
				if ( msg->m_sHubName == msg->m_sHubHost )
				{
					msg->m_sHubName = GetHubName();
				}
				
				if ( CSearchManager::Instance() )
					if ( CSearchManager::Instance()->HandleSearch( msg ) )
						Object = 0;
				break;
			}

			case DC_MESSAGE_CONNECTTOME:
			{
				CMessageConnectToMe * msg = (CMessageConnectToMe*) Object;

				if ( m_bHandleTransfer )
				{
					if ( CDownloadManager::Instance() )
						CDownloadManager::Instance()->DLM_AddTransferRequest( msg->m_sHost, msg->m_nPort, msg->m_bCrypto, GetHubName(), GetHost() );
				}

				break;
			}

			case DC_MESSAGE_REVCONNECTTOME:
			{
				CMessageRevConnectToMe * msg = (CMessageRevConnectToMe*) Object;

				if ( CConfig::Instance() && (GetMode() == ecmACTIVE) && m_bHandleTransfer )
				{
#if DCLIB_HAS_SSL == 1
					CMessageMyInfo usermyinfo;
					bool crypto = ( m_MyInfo.m_bTLSFlag && m_UserList.GetUserMyInfo(msg->m_sDstNick,&usermyinfo) && usermyinfo.m_bTLSFlag );
#else
					bool crypto = false;
#endif
					CString s = GetExternalIP( true, crypto );

					if ( s.NotEmpty() )
					{
						if ( CDownloadManager::Instance() &&
						     CDownloadManager::Instance()->DLM_AddTransferRequest( msg->m_sDstNick, CString(), GetHubName(), GetHost() ) )
						{
							SendConnectToMe(msg->m_sDstNick,s,crypto);
						}
					}
				}

				break;
			}

			case DC_MESSAGE_FORCEMOVE:
			{
				CMessageForceMove * msg = (CMessageForceMove*) Object;

				if ( CConfig::Instance() &&
				     CConfig::Instance()->GetForceMoveEnabled() && m_bHandleForceMove )
				{
					SetHubName(msg->m_sHost);

					UpdateReconnect( ersFORCEMOVE, 0 );

					Connect(msg->m_sHost,msg->m_nPort);
				}

				break;
			}

			case DC_MESSAGE_PRIVATECHAT:
			{
				CMessagePrivateChat * msg = (CMessagePrivateChat*) Object;

				/* default to receiving all messages if no CConfig */
				if ( CConfig::Instance() &&
				     (CConfig::Instance()->GetChatRecvFromOffline() == false) &&
				     (m_UserList.IsUserOnline(msg->m_sSrcNick) == false) )
				{
					delete Object;
					Object = 0;
					msg = 0;
				}
#if DCLIB_HAS_SSL == 1
				if ( msg )
				{
					m_pMessageSSL.PrivateChat(this,msg);
				}
#endif
				break;
			}

			case DC_MESSAGE_SUPPORTS:
			{
				/*
				 * Interesting. Since hubs may not send all the strings they support
				 * it turns out there is currently nothing we can do with the supports message.
				 */
				
				break;
			}
			
			case DC_MESSAGE_USERIP:
			{
				CMessageUserIP * msg = (CMessageUserIP*) Object;
				
				std::list<CString>::const_iterator nick_it = msg->m_lNicks.begin();
				std::list<CString>::const_iterator ip_it = msg->m_lIPs.begin();
				
				while ( (nick_it != msg->m_lNicks.end()) && (ip_it != msg->m_lIPs.end()) )
				{
					if ( m_bHandleUserList )
					{
						m_UserList.SetUserIP( *nick_it, *ip_it );
					}
					
					if ( GetNick() == *nick_it )
					{
						if ( CNetAddr::IsValidI4( ip_it->Data() ) )
						{
							m_sExternalIP = *ip_it;
							m_bGotIP = true;
						}
					}
					
					++nick_it;
					++ip_it;
				}
				
				break;
			}
			
			default:
			{
				break;
			}
		}

		if (Object)
		{
			if ( m_pCallback != 0 )
			{
				err = m_pCallback->notify( this, Object );
			}
			else
			{
				err = DC_CallBack( Object );
			}

			if ( err == -1 )
			{
				delete Object;
			}
		}
	}

	return 0;
}

/** */
bool CClient::SetUserTransferInfo( CString nick, CDCMessage * msg )
{
	int err;
	bool res = false;
	CMessageMyInfo myinfo,*mi;

	if ( m_UserList.SetUserTransferInfo(nick,msg) )
	{
		if ( m_UserList.GetUserMyInfo( nick, &myinfo ) )
		{
			mi = new CMessageMyInfo();
			*mi = myinfo;

			if ( m_pCallback != 0 )
			{
				err = m_pCallback->notify( this, mi );
			}
			else
			{
				err = DC_CallBack( mi );
			}

			if ( err == -1 )
			{
				delete mi;
			}
			
			res = true;
		}
	}
	
	return res;
}

/** send private message */
int CClient::SendPrivateMessage( CString sNick, CString sTo, CString sMsg, CString sFromNick )
{
	int i=-1;
	CString s;

	if ( (CConfig::Instance() && CConfig::Instance()->GetChatSendToOffline()) ||
	     m_UserList.IsUserOnline( sTo ) )
	{
#if DCLIB_HAS_SSL == 1
		s = m_pMessageSSL.EncryptMessage( this, sTo, sMsg );

		if ( s.NotEmpty() )
		{
			sMsg = s;
		}
#endif

		i = CDCProto::SendPrivateMessage( sNick, sTo, sMsg, sFromNick );
	}

	return i;
}

/** */
int CClient::RequestNickList()
{
	int res = 0;
	
	if ( m_nNickListHandler == 0 )
	{
		res = CDCProto::RequestNickList();
		
		// if set, we will never recv a nicklist
		// hubs may not put NoGetINFO into their supports even if it is enabled
		if ( m_bExtProtocol == false )
		{
			m_nNickListHandler = 1;
		}
	}
	else
	{
		printf("We are still waiting for a NickList.\n");
	}
	
	return res;
}

/** */
int CClient::Disconnect( bool force )
{
	UpdateReconnect( ersDISABLED );
	
	SetUsedPassword(false);

	return CConnection::Disconnect(force);
}

/** */
CString CClient::GetExternalIP( bool addport, bool crypto ) const
{
	/*
	 * If the hub does not support UserIP2 then the value we got from CConfig
	 * will be used.
	 */
	if ( CConfig::Instance() && (CConfig::Instance()->GetUserIP2Enabled() == false) )
	{
		return CConfig::Instance()->GetTCPHostString( addport, crypto );
	}
	else if ( m_bGotIP )
	{
		if ( addport )
		{
			unsigned int port = 0;
			
			if ( crypto )
			{
				if ( CCryptoListenManager::Instance() )
				{
					port = CCryptoListenManager::Instance()->GetListenPort();
				}
			}
			else
			{
				if ( CListenManager::Instance() )
				{
					port = CListenManager::Instance()->GetListenPort();
				}
			}
			
			if ( port != 0 )
			{
				return m_sExternalIP + ":" + CString::number(port);
			}
			else
			{
				return m_sExternalIP;
			}
		}
		else
		{
			return m_sExternalIP;
		}
	}
	else if ( CConfig::Instance() )
	{
		return CConfig::Instance()->GetTCPHostString( addport, crypto );
	}
	else
	{
		/* no IP from hub and no CConfig to get one from! */
		return "127.0.0.1";
	}
}

/** */
int CClient::SendSearch( CMessageSearchFile * msg )
{
	
	if ( msg->m_bLocal )
	{
		/* fix nick for passive search */
		msg->m_sSource = GetNick();
	}
	else
	{
		/*
		 * GetExternalIP() handles if we have an IP from the hub and if we are supposed to use it or not
		 * it uses GetTCPHostString() but without adding the port this is the same as GetUDPHostString()
		 */
		msg->m_sSource = GetExternalIP(false,false) + ":" + CString::number(CConfig::Instance()->GetUDPListenPort());
	}
	
	return CDCProto::SendSearch( msg );
}

/** */
void CClient::SendSSLInfo()
{
	CMessageLog * log1 = new CMessageLog();
	
	log1->sMessage  = GetSSLVersion();
	log1->sMessage += " connection using ";
	log1->sMessage += GetSSLCipher();
	
	int err;
	if ( m_pCallback )
	{
		err = m_pCallback->notify( this, log1 );
	}
	else
	{
		err = DC_CallBack( log1 );
	}
	
	if ( err == -1 )
	{
		delete log1;
	}
	
	CMessageLog * log2 = new CMessageLog();
	log2->sMessage = VerifyPeerCertificate();
	
	if ( m_pCallback )
	{
		err = m_pCallback->notify( this, log2 );
	}
	else
	{
		err = DC_CallBack( log2 );
	}
	
	if ( err == -1 )
	{
		delete log2;
	}
}
