//
// This file is part of the imule Project.
//
// Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org )
// Copyright (c) 2002-2011 Merkur ( devs@emule-project.net / http://www.emule-project.net )
//
// Any parts of this program derived from the xMule, lMule or eMule project,
// or contributed by third-party developers are copyrighted by their
// respective authors.
//
// 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.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
//

#include <wx/wx.h>
#include <wx/mstream.h>
#include <wx/tokenzr.h>

#include "updownclient.h"	// Needed for CUpDownClient
#include "SharedFileList.h"	// Needed for CSharedFileList

#include <protocol/Protocols.h>
#include <protocol/ed2k/Client2Client/TCP.h>
#include <protocol/ed2k/ClientSoftware.h>
#include <protocol/kad/Client2Client/UDP.h>
#include <protocol/kad2/Constants.h>
#include <protocol/kad2/Client2Client/TCP.h>
#include <protocol/kad2/Client2Client/UDP.h>

#include <common/ClientVersion.h>

#include <tags/ClientTags.h>

#include <zlib.h>		// Needed for inflateEnd

#include <common/Format.h>	// Needed for CFormat

#include "SearchList.h"		// Needed for CSearchList
#include "DownloadQueue.h"	// Needed for CDownloadQueue
#include "UploadQueue.h"	// Needed for CUploadQueue
#include "IPFilter.h"		// Needed for CIPFilter
#include "ServerConnect.h"	// Needed for CServerConnect
#include "ClientCredits.h"	// Needed for CClientCredits
#include "ClientCreditsList.h"	// Needed for CClientCreditsList
#include "Server.h"		// Needed for CServer
#include "Preferences.h"	// Needed for CPreferences
#include "MemFile.h"		// Needed for CMemFile
#include "Packet.h"		// Needed for CPacket
#include "Friend.h"		// Needed for CFriend
#include "ClientList.h"		// Needed for CClientList
#ifndef AMULE_DAEMON
#include "amuleDlg.h"		// Needed for CamuleDlg
#include "CaptchaDialog.h"	// Needed for CCaptchaDialog
#include "CaptchaGenerator.h"
#include "ChatWnd.h"		// Needed for CChatWnd
#endif
#include "amule.h"		// Needed for theApp
#include "PartFile.h"		// Needed for CPartFile
#include "ClientTCPSocket.h"	// Needed for CClientTCPSocket
#include "ListenSocket.h"	// Needed for CListenSocket
#include "FriendList.h"		// Needed for CFriendList
#include "Statistics.h"		// Needed for theStats
#include "ClientUDPSocket.h"
#include "Logger.h"
#include "DataToText.h"		// Needed for GetSoftName()
#include "GuiEvents.h"		// Needed for Notify_
#include "ServerList.h"		// For CServerList

#include "kademlia/kademlia/Kademlia.h"
#include "kademlia/kademlia/Prefs.h"
#include "kademlia/kademlia/Search.h"
#include "kademlia/routing/RoutingZone.h"


//#define __PACKET_DEBUG__


// some client testing variables
static wxString crash_name = wxT("[Invalid User Name]");
static wxString empty_name = wxT("[Empty User Name]");

//	members of CUpDownClient
//	which are used by down and uploading functions


CUpDownClient::CUpDownClient(CClientTCPSocket* sender)
{
#ifdef __DEBUG__
        m_socket = NULL;
        SetSocket(sender);
#else
        m_socket = sender;
#endif
        Init();
}

CUpDownClient::CUpDownClient ( const CI2PAddress & in_dest, const CI2PAddress & in_serverdest,
                               CPartFile* in_reqfile, bool ed2kID, bool checkfriend )
{
        m_socket = NULL;
        Init();
        SetUserIDHybrid( in_dest);

        if (!HasLowID()) {
                m_nConnectIP = in_dest;
        }
        m_nServerDest = in_serverdest;
        SetRequestFile ( in_reqfile );
        ReGetClientSoft();

        if (checkfriend) {
                if ( ( m_Friend = theApp->friendlist->FindFriend ( CMD4Hash(), m_dwUserIP ) ) ) {
                        m_Friend->LinkClient(CCLIENTREF(this, wxT("CUpDownClient::CUpDownClient m_Friend->LinkClient")));
                } else {
                        // avoid that an unwanted client instance keeps a friend slot
                        m_bFriendSlot = false;
                }
        }

}

void CUpDownClient::Init()
{
        m_linked = 0;
#ifdef DEBUG_ZOMBIE_CLIENTS
        m_linkedDebug = false;
#endif
        m_bAddNextConnect = false;
        credits = NULL;
        m_byChatstate = MS_NONE;
        //m_nKadState = KS_NONE;
        m_nChatCaptchaState = CA_NONE;
        m_cShowDR = 0;
        m_reqfile = NULL;	 // No file required yet
        m_nTransferredUp = 0;
        m_cSendblock = 0;
        m_cAsked = 0;
        msReceivedPrev = 0;
        kBpsDown = 0.0;
        bytesReceivedCycle = 0;
        m_nServerDest = CI2PAddress::null;
        m_iFileListRequested = 0;
        m_dwLastUpRequest = 0;
        m_bEmuleProtocol = false;
        m_bCompleteSource = false;
        m_bFriendSlot = false;
        m_bCommentDirty = false;
        m_bReaskPending = false;
        m_bUDPPending = false;
        m_nUserPort = 0;
        m_nPartCount = 0;
        m_dwLastAskedTime = 0;
        m_nDownloadState = DS_NONE;
        m_dwUploadTime = 0;
        m_nTransferredDown = 0;
        m_nUploadState = US_NONE;
        m_dwLastBlockReceived = 0;
        m_bUnicodeSupport = true;

        m_fSentOutOfPartReqs = 0;
        m_nCurQueueSessionPayloadUp = 0;
        m_addedPayloadQueueSession = 0;
        m_nUpDatarate = 0;
        m_nSumForAvgUpDataRate = 0;

        m_nRemoteQueueRank = 0;
        m_nOldRemoteQueueRank = 0;
        m_dwLastSourceRequest = 0;
        m_dwLastSourceAnswer = 0;
        m_dwLastAskedForSources = 0;

        m_SecureIdentState = IS_UNAVAILABLE;
        m_dwLastSignatureDest = CI2PAddress::null;

        m_byInfopacketsReceived = IP_NONE;

        m_bIsHybrid = false;
        m_bIsML = false;
        m_Friend = NULL;
        m_iRating = 0;
        m_nCurSessionUp = 0;
        m_clientSoft=SO_UNKNOWN;

        m_bRemoteQueueFull = false;
        m_HasValidHash = false;
        SetWaitStartTime();

        m_fHashsetRequesting = 0;
        m_fSharedDirectories = 0;
        m_lastPartAsked = CPartFile::NO_PART;
        m_nUpCompleteSourcesCount = 0;
        m_waitingPosition = 0;
        m_score = 0;
        m_lastRefreshedDLDisplay = 0;
        m_bHelloAnswerPending = false;
        m_fSentCancelTransfer = 0;
        m_Aggressiveness = 0;
        m_LastFileRequest = 0;

        m_clientState = CS_NEW;

        ClearHelloProperties();

        m_pReqFileAICHHash = NULL;
        m_fSupportsAICH = 0;
        m_fAICHRequested = 0;
        m_fSupportsLargeFiles = 0;
        m_fExtMultiPacket = 0;
        m_fIsSpammer = 0;

        m_dwUserIP = CI2PAddress::null;
        m_nConnectIP = CI2PAddress::null;
        m_nServerDest  = CI2PAddress::null;

        m_fNeedOurPublicIP = false;
        m_bHashsetRequested = false;

        m_lastDownloadingPart = 0;

        m_uploadingfile = NULL;

        m_OSInfo_sent = false;

        /* Kad stuff */
        m_nUserIDHybrid = CI2PAddress::null;
        m_nSourceFrom = SF_NONE;

        if ( m_socket ) {
                CI2PAddress address;
                m_socket->GetPeer ( address );
                SetTCPDest ( address );
        } else {
                SetTCPDest ( CI2PAddress::null );
        }

        /* Statistics */
        m_lastClientSoft = (uint32)(-1);
        m_lastClientVersion = 0;

        /* Creation time (for buddies timeout) */
        m_nCreationTime = ::GetTickCount();

        m_MaxBlockRequests = STANDARD_BLOCKS_REQUEST; // Safe starting amount

        m_last_block_start = 0;
        m_lastaverage = 0;

        m_fRequestsCryptLayer = 0;
        m_fSupportsCryptLayer = 0;
        m_fRequiresCryptLayer = 0;
        m_fSupportsSourceEx2 = 0;
        m_fSupportsCaptcha = 0;

        m_hasbeenobfuscatinglately = false;

        m_cCaptchasSent = 0;
        m_cMessagesReceived = 0;
        m_cMessagesSent = 0;

}


CUpDownClient::~CUpDownClient()
{
#ifdef __DEBUG__
        if (!connection_reason.IsEmpty()) {
                AddDebugLogLineN(logClient, wxT("Client to check for ") + connection_reason + wxT(" was deleted without connection."));
        }
#endif


        if (m_lastClientSoft == SO_UNKNOWN) {
                theStats::RemoveUnknownClient();
        } else if (m_lastClientSoft != (uint32)(-1)) {
                theStats::RemoveKnownClient(m_lastClientSoft, m_lastClientVersion, m_lastOSInfo);
        }

        // Indicate that we are not anymore on stats
        m_lastClientSoft = (uint32)(-1);

        // The socket should have been removed in Safe_Delete, but it
        // doesn't hurt to have an extra check.
        if (m_socket) {
                m_socket->Safe_Delete();
                // Paranoia
                SetSocket(NULL);
        }

        ClearUploadBlockRequests();
        ClearDownloadBlockRequests();

        DeleteContents(m_WaitingPackets_list);

        // Allow detection of deleted clients that didn't go through Safe_Delete
        m_clientState = CS_DYING;
}


void CUpDownClient::ClearHelloProperties()
{
        m_nUDPDest = CI2PAddress::null;
        m_byUDPVer = 0;
        m_byDataCompVer = 0;
        m_byEmuleVersion = 0;
        m_bySourceExchange1Ver = 0;
        m_byAcceptCommentVer = 0;
        m_byExtendedRequestsVer = 0;
        m_byCompatibleClient = 0;
        m_bySupportSecIdent = 0;
        m_bSupportsPreview = 0;
        m_nClientVersion = 0;
        m_fSharedDirectories = 0;
        m_bMultiPacket = 0;
        m_fOsInfoSupport = 0;
        m_fValueBasedTypeTags = 0;
        SecIdentSupRec = 0;
        m_byKadVersion = 0;
        m_fRequestsCryptLayer = 0;
        m_fSupportsCryptLayer = 0;
        m_fRequiresCryptLayer = 0;
        m_fSupportsSourceEx2 = 0;
        m_fSupportsCaptcha = 0;
        m_bIsHybrid = false;
        m_bIsML = false;
        m_fNoViewSharedFiles = true;	// that's a sensible default to assume until we get the real value
        m_bUnicodeSupport = false;
}

bool CUpDownClient::ProcessHelloPacket(const byte* pachPacket, uint32 nSize)
{
        const CMemFile data(pachPacket,nSize);
        uint8 hashsize = data.ReadUInt8();
        if ( 16 != hashsize ) {
                /*
                 * Hint: We can not accept other sizes here because:
                 *       - the magic number is spread all over the source
                 *       - the answer packet lacks the size field
                 */
                throw wxString(wxT("Invalid Hello packet: Other userhash sizes than 16 are not implemented"));
        }
        // eMule 0.42: reset all client properties; a client may not send a particular emule tag any longer
        ClearHelloProperties();

        return ProcessHelloTypePacket(data);
}

void CUpDownClient::Safe_Delete()
{
        // Because we are delaying the deletion, we might end up trying to delete
        // it twice, however, this is normal and shouldn't trigger any failures
        if ( m_clientState == CS_DYING ) {
                return;
        }

        // If called from background, post an event to process it in main thread
        if (!wxThread::IsMain()) {
                CoreNotify_Client_Delete(CCLIENTREF(this, wxT("CUpDownClient::Safe_Delete CoreNotify_Client_Delete")));
                return;
        }

        m_clientState = CS_DYING;

        // Make sure client doesn't get deleted until this method is finished
        CClientRef ref(CCLIENTREF(this, wxT("CUpDownClient::Safe_Delete reflocker")));

        // Close the socket to avoid any more connections and related events
        if ( m_socket ) {
                m_socket->Safe_Delete();
                // Paranoia
                SetSocket(NULL);
        }

        // Remove the client from the clientlist if we still have it
        if ( theApp->clientlist ) {
                theApp->clientlist->RemoveClient( this );
        }

        // Doing what RemoveClient used to do. Just to be sure...
        if (theApp->uploadqueue) {
                theApp->uploadqueue->RemoveFromUploadQueue(this);
                theApp->uploadqueue->RemoveFromWaitingQueue(this);
        }
        if (theApp->downloadqueue) {
                theApp->downloadqueue->RemoveSource(this);
        }

        // For security, remove it from the lists unconditionally.
        Notify_SharedCtrlRemoveClient(ECID(), (CKnownFile*)NULL);
        Notify_SourceCtrlRemoveSource(ECID(), (CPartFile*)NULL);

        if (IsAICHReqPending()) {
                m_fAICHRequested = FALSE;
                CAICHHashSet::ClientAICHRequestFailed(this);
        }

        if (m_Friend) {
                m_Friend->UnLinkClient();	// this notifies
                m_Friend = NULL;
        }

        if (m_iRating>0 || !m_strComment.IsEmpty()) {
                m_iRating = 0;
                m_strComment.Clear();
                if (m_reqfile) {
                        m_reqfile->UpdateFileRatingCommentAvail();
                }
        }

        // Ensure that source-counts gets updated in case
        // of a source not on the download-queue
        SetRequestFile( NULL );

        SetUploadFileID(NULL);

        delete m_pReqFileAICHHash;
        m_pReqFileAICHHash = NULL;

#ifdef DEBUG_ZOMBIE_CLIENTS
        if (m_linked > 1) {
                AddLogLineC(CFormat(wxT("Client %d still linked in %d places: %s")) % ECID() % (m_linked - 1) % GetLinkedFrom());
                m_linkedDebug = true;
        }
#endif
}


bool CUpDownClient::ProcessHelloAnswer(const byte* pachPacket, uint32 nSize)
{
        const CMemFile data(pachPacket,nSize);
        bool bIsMule = ProcessHelloTypePacket(data);
        m_bHelloAnswerPending = false;
        return bIsMule;
}

/**
 *   Reads :
 *            Hash
 *            CI2PAddress
 *            tagcount
 *            tagcount CTag
 *            server address
 */

bool CUpDownClient::ProcessHelloTypePacket ( const CMemFile& data )
{
        uint32 dwEmuleTags = 0;

        CMD4Hash hash = data.ReadHash();
        SetUserHash( hash );
        uint32 tagcount = data.ReadUInt32();
        for (uint32 i = 0; i < tagcount; i++) {
                CTag temptag(data);
                switch(temptag.GetID()) {
                case CT_NAME:
                        m_Username = temptag.GetStr();
                        break;

                case CT_VERSION:
                        m_nClientVersion = temptag.GetInt();
                        break;

                case ET_MOD_VERSION:
                        if (temptag.IsStr()) {
                                m_strModVersion = temptag.GetStr();
                        } else if (temptag.IsInt()) {
                                m_strModVersion = CFormat(wxT("ModID=%u")) % temptag.GetInt();
                        } else {
                                m_strModVersion = wxT("ModID=<Unknown>");
                        }

                        break;

                case ET_UDPDEST:
                        // UDP Dest
                        m_nUDPDest = temptag.GetAddr();

                        dwEmuleTags |= 1;
#ifdef __PACKET_DEBUG__
                        AddLogLineNS(CFormat(wxT("Hello type packet processing with eMule ports UDP=%i KAD=%i")) % m_nUDPPort % m_nKadPort);
#endif
                        break;


                case CT_EMULE_MISCOPTIONS1: {
                        //  3 AICH Version (0 = not supported)
                        //  1 Unicode
                        //  4 UDP version
                        //  4 Data compression version
                        //  4 Secure Ident
                        //  4 Source Exchange
                        //  4 Ext. Requests
                        //  4 Comments
                        //	 1 PeerCache supported
                        //	 1 No 'View Shared Files' supported
                        //	 1 MultiPacket
                        //  1 Preview
                        uint32 flags = temptag.GetInt();
                        m_fSupportsAICH			= (flags >> (4*7+1)) & 0x07;
                        m_bUnicodeSupport		= (flags >> 4*7) & 0x01;
                        m_byUDPVer				= (flags >> 4*6) & 0x0f;
                        m_byDataCompVer			= (flags >> 4*5) & 0x0f;
                        m_bySupportSecIdent		= (flags >> 4*4) & 0x0f;
                        m_bySourceExchange1Ver	= (flags >> 4*3) & 0x0f;
                        m_byExtendedRequestsVer	= (flags >> 4*2) & 0x0f;
                        m_byAcceptCommentVer	= (flags >> 4*1) & 0x0f;
                        m_fNoViewSharedFiles	= (flags >> 1*2) & 0x01;
                        m_bMultiPacket			= (flags >> 1*1) & 0x01;
                        m_fSupportsPreview		= (flags >> 1*0) & 0x01;
                        dwEmuleTags |= 2;
#ifdef __PACKET_DEBUG__
                        AddLogLineNS(wxT("Hello type packet processing with eMule Misc Options:"));
                        AddLogLineNS(CFormat(wxT("m_byUDPVer = %i")) % m_byUDPVer);
                        AddLogLineNS(CFormat(wxT("m_byDataCompVer = %i")) % m_byDataCompVer);
                        AddLogLineNS(CFormat(wxT("m_bySupportSecIdent = %i")) % m_bySupportSecIdent);
                        AddLogLineNS(CFormat(wxT("m_bySourceExchangeVer = %i")) % m_bySourceExchange1Ver);
                        AddLogLineNS(CFormat(wxT("m_byExtendedRequestsVer = %i")) % m_byExtendedRequestsVer);
                        AddLogLineNS(CFormat(wxT("m_byAcceptCommentVer = %i")) % m_byAcceptCommentVer);
                        AddLogLineNS(CFormat(wxT("m_fNoViewSharedFiles = %i")) % m_fNoViewSharedFiles);
                        AddLogLineNS(CFormat(wxT("m_bMultiPacket = %i")) % m_bMultiPacket);
                        AddLogLineNS(CFormat(wxT("m_fSupportsPreview = %i")) % m_fSharedDirectories);
                        AddLogLineNS(wxT("That's all."));
#endif
                        SecIdentSupRec +=  1;
                        break;
                }

                case CT_EMULE_MISCOPTIONS2:
                        //  19 Reserved
                        //   1 Direct UDP Callback supported and available
                        //   1 Supports ChatCaptchas
                        //   1 Supports SourceExachnge2 Packets, ignores SX1 Packet Version
                        //   1 Requires CryptLayer
                        //   1 Requests CryptLayer
                        //   1 Supports CryptLayer
                        //   1 Reserved (ModBit)
                        //   1 Ext Multipacket (Hash+Size instead of Hash)
                        //   1 Large Files (includes support for 64bit tags)
                        //   4 Kad Version - will go up to version 15 only (may need to add another field at some point in the future)
                        m_fDirectUDPCallback	= (temptag.GetInt() >> 12) & 0x01;
                        m_fSupportsCaptcha	    = (temptag.GetInt() >> 11) & 0x01;
                        m_fSupportsSourceEx2	= (temptag.GetInt() >> 10) & 0x01;
                        m_fRequiresCryptLayer	= (temptag.GetInt() >>  9) & 0x01;
                        m_fRequestsCryptLayer	= (temptag.GetInt() >>  8) & 0x01;
                        m_fSupportsCryptLayer	= (temptag.GetInt() >>  7) & 0x01;
                        // reserved 1
                        m_fExtMultiPacket	= (temptag.GetInt() >>  5) & 0x01;
                        m_fSupportsLargeFiles   = (temptag.GetInt() >>  4) & 0x01;
                        m_byKadVersion		= (temptag.GetInt() >>  0) & 0x0f;
                        dwEmuleTags |= 8;

                        m_fRequestsCryptLayer &= m_fSupportsCryptLayer;
                        m_fRequiresCryptLayer &= m_fRequestsCryptLayer;

#ifdef __PACKET_DEBUG__
                        AddLogLineNS(wxT("Hello type packet processing with eMule Misc Options 2:"));
                        AddLogLineNS(CFormat(wxT("	m_fDirectUDPCallback	= %i")) % m_fDirectUDPCallback);
                        AddLogLineNS(CFormat(wxT("	m_fSupportsCaptcha		= %i")) % m_fSupportsCaptcha);
                        AddLogLineNS(CFormat(wxT("	m_fSupportsSourceEx2	= %i")) % m_fSupportsSourceEx2);
                        AddLogLineNS(CFormat(wxT("	m_fRequiresCryptLayer	= %i")) % m_fRequiresCryptLayer);
                        AddLogLineNS(CFormat(wxT("	m_fRequestsCryptLayer	= %i")) % m_fRequestsCryptLayer);
                        AddLogLineNS(CFormat(wxT("	m_fSupportsCryptLayer	= %i")) % m_fSupportsCryptLayer);
                        AddLogLineNS(CFormat(wxT("	m_fExtMultiPacket	= %i")) % m_fExtMultiPacket);
                        AddLogLineNS(CFormat(wxT("	m_fSupportsLargeFiles	= %i")) % m_fSupportsLargeFiles);
                        AddLogLineNS(CFormat(wxT("	KadVersion		= %u")) % m_byKadVersion);
                        AddLogLineNS(wxT("That's all."));
#endif
                        break;

                        // Special tag for Compat. Clients Misc options.
                case CT_EMULECOMPAT_OPTIONS:
                        //  1 Operative System Info
                        //	1 Value-based-type int tags (experimental!)
                        m_fValueBasedTypeTags	= (temptag.GetInt() >> 1*1) & 0x01;
                        m_fOsInfoSupport		= (temptag.GetInt() >> 1*0) & 0x01;
                        break;

                case CT_EMULE_VERSION:
                        //  8 Compatible Client ID
                        //  7 Mjr Version (Doesn't really matter..)
                        //  7 Min Version (Only need 0-99)
                        //  3 Upd Version (Only need 0-5)
                        //  7 Bld Version (Only need 0-99)
                        m_byCompatibleClient = (temptag.GetInt() >> 24);
                        m_nClientVersion = temptag.GetInt() & 0x00ffffff;
                        m_byEmuleVersion = 0x99;
                        m_fSharedDirectories = 1;
                        dwEmuleTags |= 4;
                        break;
                }
        }

        // Hybrid now has an extra uint32.. What is it for?
        // Also, many clients seem to send an extra 6? These are not eDonkeys or Hybrids..
        if ( data.GetLength() - data.GetPosition() == sizeof(uint32) ) {
                uint32 test = data.ReadUInt32();
                /*if (test == 'KDLM') below kdlm is converted to ascii values.
                This fixes a warning with gcc 3.4.
                K=4b D=44 L=4c M=4d
                */
                if (test == 0x4b444c4d) { //if it's == "KDLM"
                        m_bIsML=true;
                } else {
                        m_bIsHybrid = true;
                        m_fSharedDirectories = 1;
                }
        }

        if ( m_socket ) {
                CI2PAddress dest;
                m_socket->GetPeer ( dest );
                SetTCPDest ( dest );
        } else {
                throw wxString(wxT("Huh, socket failure. Avoided crash this time."));
        }

        if (thePrefs::AddServersFromClient()) {
                CServer* addsrv = new CServer ( m_nServerDest );
                addsrv->SetListName ( addsrv->GetAddress() );
                if (!theApp->AddServer(addsrv)) {
                        delete addsrv;
                }
        }

        //(a)If this is a highID user, store the ID in the Hybrid format.
        //(b)Some older clients will not send a ID, these client are HighID users that are not connected to a server.
        //(c)Kad users with a *.*.*.0 IPs will look like a lowID user they are actually a highID user.. They can be detected easily
        //because they will send a ID that is the same as their IP..
        if(!HasLowID() || !m_nUserIDHybrid.isValid() || m_nUserIDHybrid == m_dwUserIP )  {
                SetUserIDHybrid(m_dwUserIP);
        }
        // get client credits
        CClientCredits* pFoundCredits = theApp->clientcredits->GetCredit(m_UserHash);
        if (credits == NULL) {
                credits = pFoundCredits;
                if ( !theApp->clientlist->ComparePriorUserhash ( m_dwUserIP, m_nUserPort, pFoundCredits ) ) {
                        AddDebugLogLineN(logClient, CFormat ( wxT ( "Client: %s (%x) Ban. reason: Userhash changed (Found in TrackedClientsList)" ) ) % ( wxString ) GetUserName() % GetIP().hashCode() );
                        Ban();
                }
        } else if (credits != pFoundCredits) {
                // userhash change ok, however two hours "waittime" before it can be used
                credits = pFoundCredits;
                AddDebugLogLineN(logClient, CFormat ( wxT ( "Client: %s (%x) Banreason: Userhash changed" ) ) % ( wxString ) GetUserName() % GetIP().hashCode() ); //I2P: GetFullIP() );
                Ban();
        }

        if ( ( m_Friend = theApp->friendlist->FindFriend ( m_UserHash, m_dwUserIP) ) != NULL ) {
                m_Friend->LinkClient(CCLIENTREF(this, wxT("CUpDownClient::ProcessHelloTypePacket m_Friend->LinkClient")));
        } else {
                // avoid that an unwanted client instance keeps a friend slot
                SetFriendSlot(false);
        }


        ReGetClientSoft();

        m_byInfopacketsReceived |= IP_EDONKEYPROTPACK;

        // check if at least CT_EMULEVERSION was received, all other tags are optional
        bool bIsMule = (dwEmuleTags & 0x04) == 0x04;
        if (bIsMule) {
                m_bEmuleProtocol = true;
                m_byInfopacketsReceived |= IP_EMULEPROTPACK;
        }

        if ( GetUDPDest().isValid() ) {
                Kademlia::CKademlia::Bootstrap ( GetUDPDest() );
        }

        return bIsMule;
}


bool CUpDownClient::SendHelloPacket()
{
        if (m_socket == NULL) {
                wxFAIL;
                return true;
        }

        // if IP is filtered, don't greet him but disconnect...
        CI2PAddress address; //I2P: amuleIPV4Address address;
        m_socket->GetPeer ( address );
        if ( theApp->ipfilter->IsFiltered ( address/*StringIPtoUint32(address.IPAddress())*/ ) ) {
                if (Disconnected(wxT("IPFilter"))) {
                        Safe_Delete();
                        return false;
                }
                return true;
        }

        CMemFile data(128);
        data.WriteUInt8(16); // size of userhash
        SendHelloTypePacket(&data);

        CPacket* packet = new CPacket(data, OP_EDONKEYPROT, OP_HELLO);
        theStats::AddUpOverheadOther(packet->GetPacketSize());
        SendPacket(packet,true);
        m_bHelloAnswerPending = true;
        AddDebugLogLineN(logLocalClient, CFormat ( wxT ( "%x (%s --> %s): Local client %x : OP_HELLO to %x" ) ) % ( long ) m_socket % m_socket->GetLocal().humanReadable() % m_socket->GetPeer().humanReadable() % ( long ) this % GetIP().hashCode() ); //I2P : IP() );
        return true;
}

void CUpDownClient::SendMuleInfoPacket(bool bAnswer, bool OSInfo)
{

        if (m_socket == NULL) {
                wxFAIL;
                return;
        }

        CPacket* packet = NULL;
        CMemFile data;

        data.WriteUInt8(CURRENT_VERSION_SHORT);

        if (OSInfo) {

                // Special MuleInfo packet for clients supporting it.
                // This means aMule >= 2.0.0 and Hydranode

                // Violently mark it as special Mule Info packet
                // Sending this makes non-supporting-osinfo clients to refuse to read this
                // packet. Anyway, this packet should NEVER get to non-supporting clients.

                data.WriteUInt8(/*EMULE_PROTOCOL*/ 0xFF);

                data.WriteUInt32(1); // One Tag (OS_INFO)

                CTagString tag1(ET_OS_INFO,theApp->GetOSType());
                tag1.WriteTo(&data);

                m_OSInfo_sent = true; // So we don't send it again

        } else {

                // Normal MuleInfo packet

                // Kry - There's no point on upgrading to VBT tags here
                // as no client supporting it uses mule info packet.

                data.WriteUInt8(EMULE_PROTOCOL);

                // Tag number
                data.WriteUInt32(9);

                CTag tag1(ET_COMPRESSION,1);
                tag1.WriteTo(&data);
                CTag tag2(ET_UDPVER,4);
                tag2.WriteTo(&data);
                CTag tag3(ET_UDPDEST, theApp->GetUdpDest());
                tag3.WriteTo(&data);
                CTag tag4(ET_SOURCEEXCHANGE,3);
                tag4.WriteTo(&data);
                CTag tag5(ET_COMMENTS,1);
                tag5.WriteTo(&data);
                CTag tag6(ET_EXTENDEDREQUEST,2);
                tag6.WriteTo(&data);

                uint32 dwTagValue = (theApp->CryptoAvailable() ? 3 : 0);
                // Kry - Needs the preview code from eMule
                /*
                // set 'Preview supported' only if 'View Shared Files' allowed
                if (thePrefs::CanSeeShares() != vsfaNobody) {
                	dwTagValue |= 128;
                }
                */
                CTag tag7(ET_FEATURES, dwTagValue);
                tag7.WriteTo(&data);

                CTag tag8(ET_COMPATIBLECLIENT,SO_IMULE);
                tag8.WriteTo(&data);

                // Support for tag ET_MOD_VERSION
                wxString mod_name(MOD_VERSION_LONG);
                CTag tag9(ET_MOD_VERSION, mod_name);
                tag9.WriteTo(&data);
                // Maella end

        }

        packet = new CPacket(data, OP_EMULEPROT, (bAnswer ? OP_EMULEINFOANSWER : OP_EMULEINFO));

        if (m_socket) {
                theStats::AddUpOverheadOther(packet->GetPacketSize());
                SendPacket(packet,true,true);

                if ( !bAnswer ) {
                        if ( !OSInfo ) {
                                AddDebugLogLineN(logLocalClient, CFormat ( wxT ( "%x (%s --> %s): Local client %x : OP_EMULEINFO to %x" ) ) % ( long ) m_socket % m_socket->GetLocal().humanReadable() % m_socket->GetPeer().humanReadable() % ( long ) this % GetIP().hashCode() ); //I2P : GetFullIP() );
                        } else {
                                AddDebugLogLineN(logLocalClient, CFormat ( wxT ( "%x (%s --> %s): Local client %x : OP_EMULEINFO/OS_INFO to %s" ) ) % ( long ) m_socket % m_socket->GetLocal().humanReadable() % m_socket->GetPeer().humanReadable() % ( long ) this % GetIP().humanReadable() ); //I2P : GetFullIP() );
                        }
                } else {
                        AddDebugLogLineN(logLocalClient, CFormat ( wxT ( "%x (%s --> %s): Local client %x : OP_EMULEINFOANSWER to " ) ) % ( long ) m_socket % m_socket->GetLocal().humanReadable() % m_socket->GetPeer().humanReadable() % ( long ) this % GetIP().hashCode() ); //I2P : GetFullIP() );
                }
        }
}

bool CUpDownClient::ProcessMuleInfoPacket(const byte* pachPacket, uint32 nSize)
{
        uint8 protocol_version;

        const CMemFile data(pachPacket,nSize);

        // The version number part of this packet will soon be useless since
        // it is only able to go to v.99. Why the version is a uint8 and why
        // it was not done as a tag like the eDonkey hello packet is not known.
        // Therefore, sooner or later, we are going to have to switch over to
        // using the eDonkey hello packet to set the version. No sense making
        // a third value sent for versions.
        uint8 mule_version = data.ReadUInt8();
        protocol_version = data.ReadUInt8();
        uint32 tagcount = data.ReadUInt32();
        if (protocol_version == 0xFF) {
                // OS Info supporting clients sending a recycled Mule info packet
                for (uint32 i = 0; i < tagcount; i++) {
                        CTag temptag(data);
                        switch(temptag.GetID()) {
                        case ET_OS_INFO:
                                // Special tag, only supporting clients (aMule/Hydranode)
                                // It was recycled from a mod's tag, so if the other side
                                // is not supporting OS Info, we're seriously fucked up :)
                                m_sClientOSInfo = temptag.GetStr();

                                // If we didn't send our OSInfo to this client, just send it
                                if (!m_OSInfo_sent) {
                                        SendMuleInfoPacket(false,true);
                                }

                                UpdateStats();

                                break;

                                // Your ad... er... I mean TAG, here

                        default:
                                break;
                        }
                }
        } else {
                // Old eMule sending tags
                m_byCompatibleClient = 0;
                m_byEmuleVersion = mule_version;

                if( m_byEmuleVersion == 0x2B ) {
                        m_byEmuleVersion = 0x22;
                }

                if (!(m_bEmuleProtocol = (protocol_version == EMULE_PROTOCOL))) {
                        return false;
                }

                for (uint32 i = 0; i < tagcount; i++) {
                        CTag temptag(data);
                        switch(temptag.GetID()) {
                        case ET_COMPRESSION:
                                // Bits 31- 8: 0 - reserved
                                // Bits  7- 0: data compression version
                                m_byDataCompVer = temptag.GetInt();
                                break;

                        case ET_UDPDEST:
                                // Bits 31-16: 0 - reserved
                                // Bits 15- 0: UDP port
                                m_nUDPDest = temptag.GetAddr();
                                break;

                        case ET_UDPVER:
                                // Bits 31- 8: 0 - reserved
                                // Bits  7- 0: UDP protocol version
                                m_byUDPVer = temptag.GetInt();
                                break;

                        case ET_SOURCEEXCHANGE:
                                // Bits 31- 8: 0 - reserved
                                // Bits  7- 0: source exchange protocol version
                                m_bySourceExchange1Ver = temptag.GetInt();
                                break;

                        case ET_COMMENTS:
                                // Bits 31- 8: 0 - reserved
                                // Bits  7- 0: comments version
                                m_byAcceptCommentVer = temptag.GetInt();
                                break;

                        case ET_EXTENDEDREQUEST:
                                // Bits 31- 8: 0 - reserved
                                // Bits  7- 0: extended requests version
                                m_byExtendedRequestsVer = temptag.GetInt();
                                break;

                        case ET_COMPATIBLECLIENT:
                                // Bits 31- 8: 0 - reserved
                                // Bits  7- 0: compatible client ID
                                m_byCompatibleClient = temptag.GetInt();
                                break;

                        case ET_FEATURES:
                                // Bits 31- 8: 0 - reserved
                                // Bit	    7: Preview
                                // Bit   6- 0: secure identification
                                m_bySupportSecIdent = temptag.GetInt() & 3;
                                m_bSupportsPreview = (temptag.GetInt() & 128) > 0;
                                SecIdentSupRec +=  2;
                                break;

                        case ET_MOD_VERSION:
                                if (temptag.IsStr()) {
                                        m_strModVersion = temptag.GetStr();
                                } else if (temptag.IsInt()) {
                                        m_strModVersion = CFormat(wxT("ModID=%u")) % temptag.GetInt();
                                } else {
                                        m_strModVersion = wxT("ModID=<Unknown>");
                                }

                                break;

                        default:
                                AddDebugLogLineN( logPacketErrors,
                                                  CFormat( wxT("Unknown Mule tag (%s) from client: %s") )
                                                  % temptag.GetFullInfo()
                                                  % GetClientFullInfo()
                                                );

                                break;
                        }
                }

                if( m_byDataCompVer == 0 ) {
                        m_bySourceExchange1Ver = 0;
                        m_byExtendedRequestsVer = 0;
                        m_byAcceptCommentVer = 0;
                        m_nUDPDest = CI2PAddress::null;
                }

                //implicitly supported options by older clients
                //in the future do not use version to guess about new features
                if(m_byEmuleVersion < 0x25 && m_byEmuleVersion > 0x22) {
                        m_byUDPVer = 1;
                }

                if(m_byEmuleVersion < 0x25 && m_byEmuleVersion > 0x21) {
                        m_bySourceExchange1Ver = 1;
                }

                if(m_byEmuleVersion == 0x24) {
                        m_byAcceptCommentVer = 1;
                }

                // Shared directories are requested from eMule 0.28+ because eMule 0.27 has a bug in
                // the OP_ASKSHAREDFILESDIR handler, which does not return the shared files for a
                // directory which has a trailing backslash.
                if(m_byEmuleVersion >= 0x28 && !m_bIsML) {// MLdonkey currently does not support shared directories
                        m_fSharedDirectories = 1;
                }

                ReGetClientSoft();

                m_byInfopacketsReceived |= IP_EMULEPROTPACK;
        }

        return (protocol_version == 0xFF); // This was a OS_Info?
}


void CUpDownClient::SendHelloAnswer()
{
        if (m_socket == NULL) {
                wxFAIL;
                return;
        }

        CMemFile data(128);
        SendHelloTypePacket(&data);
        CPacket* packet = new CPacket(data, OP_EDONKEYPROT, OP_HELLOANSWER);
        theStats::AddUpOverheadOther(packet->GetPacketSize());
        AddDebugLogLineN(logLocalClient, wxT("Local Client: OP_HELLOANSWER to ") + GetIP().humanReadable() );
        SendPacket (packet,true);
}



/**
 * sends :
 *          Hash
 *          TcpAddress
 *          tagcount (7 or 8 or 9)
 *          tags (CTag)
 *          ServerAddress
 */

void CUpDownClient::SendHelloTypePacket(CMemFile* data)
{
        data->WriteHash(thePrefs::GetUserHash());

        uint32 tagcount = 6;

        tagcount ++; // eMule misc flags 2 (kad version)

#ifdef __SVN__
        // Kry - This is the tagcount!!! Be sure to update it!!
        // Last update: CT_EMULECOMPAT_OPTIONS included
        data->WriteUInt32(tagcount + 1);
#else
        data->WriteUInt32(tagcount);  // NO MOD_VERSION
#endif


        CTagString tagname(CT_NAME,thePrefs::GetUserNick());
        tagname.WriteTo(data, utf8strRaw);

        CTag tagversion (CT_VERSION, (uint64_t) EDONKEYVERSION);
        tagversion.WriteTo ( data );                                    // 2

        // eMule UDP Dest
        CTag tagUdpDests ( ET_UDPDEST, theApp->GetUdpDest() );
        tagUdpDests.WriteTo ( data );                                    // 3

        // imule Version
        CTag tagMuleVersion ( CT_EMULE_VERSION, (uint64_t) (
                                      ( SO_IMULE	<< 24 ) |
                                      get_full_ed2k_version (  )
                                      // | (RESERVED			     )
                              ) );
        tagMuleVersion.WriteTo ( data );                                   // 4 + Firewalled


        // eMule Misc. Options #1
        const uint32 uUdpVer			= 4;
        const uint32 uDataCompVer		= 1;
        const uint32 uSupportSecIdent		= theApp->CryptoAvailable() ? 3 : 0;
        const uint32 uSourceExchangeVer		= 3;
        const uint32 uExtendedRequestsVer	= 2;
        const uint32 uAcceptCommentVer		= 1;
        const uint32 uNoViewSharedFiles		= (thePrefs::CanSeeShares() == vsfaNobody) ? 1 : 0; // for backward compatibility this has to be a 'negative' flag
        const uint32 uMultiPacket		= 1;
        const uint32 uSupportPreview		= 0; // No network preview at all.
        const uint32 uPeerCache			= 0; // No peercache for aMule, baby
        const uint32 uUnicodeSupport		= 1;
        const uint32 nAICHVer			= 1; // AICH is ENABLED right now.

        CTagVarInt tagMisOptions(CT_EMULE_MISCOPTIONS1,
                                 (nAICHVer			<< ((4*7)+1)) |
                                 (uUnicodeSupport		<< 4*7) |
                                 (uUdpVer			<< 4*6) |
                                 (uDataCompVer			<< 4*5) |
                                 (uSupportSecIdent		<< 4*4) |
                                 (uSourceExchangeVer		<< 4*3) |
                                 (uExtendedRequestsVer		<< 4*2) |
                                 (uAcceptCommentVer		<< 4*1) |
                                 (uPeerCache			<< 1*3) |
                                 (uNoViewSharedFiles		<< 1*2) |
                                 (uMultiPacket			<< 1*1) |
                                 (uSupportPreview		<< 1*0)
                                 , GetVBTTags() ? 0 : 32);
        tagMisOptions.WriteTo(data);

        // eMule Misc. Options #2
        const uint32 uKadVersion		= KADEMLIA_VERSION;
        const uint32 uSupportLargeFiles		= 1;
        const uint32 uExtMultiPacket		= 1;
        const uint32 uReserved			= 0; // mod bit
        const uint32 uSupportsCryptLayer	= thePrefs::IsClientCryptLayerSupported() ? 1 : 0;
        const uint32 uRequestsCryptLayer	= thePrefs::IsClientCryptLayerRequested() ? 1 : 0;
        const uint32 uRequiresCryptLayer	= thePrefs::IsClientCryptLayerRequired() ? 1 : 0;
        const uint32 uSupportsSourceEx2		= 1;
#ifdef AMULE_DAEMON
// captcha for daemon/remotegui not supported for now
        const uint32 uSupportsCaptcha		= 0;
#else
        const uint32 uSupportsCaptcha		= 1;
#endif

        CTagVarInt tagMisOptions2(CT_EMULE_MISCOPTIONS2,
//				(RESERVED				     )
                                  (uSupportsCaptcha	<< 11) |
                                  (uSupportsSourceEx2	<< 10) |
                                  (uRequiresCryptLayer	<<  9) |
                                  (uRequestsCryptLayer	<<  8) |
                                  (uSupportsCryptLayer	<<  7) |
                                  (uReserved		<<  6) |
                                  (uExtMultiPacket	<<  5) |
                                  (uSupportLargeFiles	<<  4) |
                                  (uKadVersion		<<  0)
                                  , GetVBTTags() ? 0 : 32	);
        tagMisOptions2.WriteTo(data);

        const uint32 nOSInfoSupport			= 1; // We support OS_INFO
        const uint32 nValueBasedTypeTags	= 0; // Experimental, disabled

        CTagVarInt tagMisCompatOptions(CT_EMULECOMPAT_OPTIONS,
                                       (nValueBasedTypeTags	<< 1*1) |
                                       (nOSInfoSupport			<< 1*0)
                                       , GetVBTTags() ? 0 : 32);

        tagMisCompatOptions.WriteTo(data);

#ifdef __SVN__
        wxString mod_name(MOD_VERSION_LONG);
        CTagString tagModName(ET_MOD_VERSION, mod_name);
        tagModName.WriteTo(data);
#endif
}


void CUpDownClient::ProcessMuleCommentPacket(const byte* pachPacket, uint32 nSize)
{
        if (!m_reqfile) {
                throw CInvalidPacket(wxT("Comment packet for unknown file"));
        }

        if (!m_reqfile->IsPartFile()) {
                throw CInvalidPacket(wxT("Comment packet for completed file"));
        }

        const CMemFile data(pachPacket, nSize);

        uint8 rating = data.ReadUInt8();
        if (rating > 5) {
                AddDebugLogLineN( logClient, wxString(wxT("Invalid Rating for file '")) << m_clientFilename << wxT("' received: ") << rating);
                m_iRating = 0;
        } else {
                m_iRating = rating;
                AddDebugLogLineN( logClient, wxString(wxT("Rating for file '")) << m_clientFilename << wxT("' received: ") << m_iRating);
        }

        // The comment is unicoded, with a uin32 len and safe read
        // (won't break if string size is < than advertised len)
        // Truncated to MAXFILECOMMENTLEN size
        m_strComment = data.ReadString((GetUnicodeSupport() != utf8strNone), 4 /* bytes (it's a uint32)*/, true).Left(MAXFILECOMMENTLEN);

        AddDebugLogLineN( logClient, wxString(wxT("Description for file '")) << m_clientFilename << wxT("' received: ") << m_strComment);

        // Update file rating
        m_reqfile->UpdateFileRatingCommentAvail();
}


/**
 * Clears the list of block requests a client has put on a file
 */
void CUpDownClient::ClearDownloadBlockRequests()
{
        {
                std::list<Requested_Block_Struct*>::iterator it = m_DownloadBlocks_list.begin();
                for (; it != m_DownloadBlocks_list.end(); ++it) {
                        Requested_Block_Struct* cur_block = *it;

                        if (m_reqfile) {
                                m_reqfile->RemoveBlockFromList(cur_block->StartOffset, cur_block->EndOffset);
                        }

                        delete cur_block;
                }

                m_DownloadBlocks_list.clear();
        }

        {
                std::list<Pending_Block_Struct*>::iterator it = m_PendingBlocks_list.begin();
                for (; it != m_PendingBlocks_list.end(); ++it) {
                        Pending_Block_Struct* pending = *it;

                        if (m_reqfile) {
                                m_reqfile->RemoveBlockFromList(pending->block->StartOffset, pending->block->EndOffset);
                        }

                        delete pending->block;
                        // Not always allocated
                        if (pending->zStream) {
                                inflateEnd(pending->zStream);
                                delete pending->zStream;
                        }

                        delete pending;
                }

                m_PendingBlocks_list.clear();
        }
}


bool CUpDownClient::Disconnected(const wxString& DEBUG_ONLY(strReason), bool bFromSocket)
{
        //wxASSERT(theApp->clientlist->IsValidClient(this));

        if (HasBeenDeleted()) {
                AddDebugLogLineN(logClient, wxT("Disconnected() called for already deleted client on ip ") + GetIP().humanReadable());
                return false;
        }

        if (GetUploadState() == US_UPLOADING) {
                // sets US_NONE
                theApp->uploadqueue->RemoveFromUploadQueue(this);
        }

        if (GetDownloadState() == DS_DOWNLOADING) {
                SetDownloadState(DS_ONQUEUE);
        } else {
                // ensure that all possible block requests are removed from the partfile
                ClearDownloadBlockRequests();

                if (GetDownloadState() == DS_CONNECTED) {
                        // successfully connected, but probably didn't respond to our filerequest
                        theApp->clientlist->AddDeadSource(this);
                        theApp->downloadqueue->RemoveSource(this);
                }
        }

        // we had still an AICH request pending, handle it
        if (IsAICHReqPending()) {
                m_fAICHRequested = FALSE;
                CAICHHashSet::ClientAICHRequestFailed(this);
        }

        // The remote client does not have to answer with OP_HASHSETANSWER *immediatly*
        // after we've sent OP_HASHSETREQUEST. It may occure that a (buggy) remote client
        // is sending use another OP_FILESTATUS which would let us change to DL-state to DS_ONQUEUE.
        if (((GetDownloadState() == DS_REQHASHSET) || m_fHashsetRequesting) && (m_reqfile)) {
                m_reqfile->SetHashSetNeeded(true);
        }

        SourceItemType source_type = UNAVAILABLE_SOURCE;
        SourceItemType peer_type = UNAVAILABLE_SOURCE;

        //check if this client is needed in any way, if not delete it
        bool bDelete = true;
        switch (m_nUploadState) {
        case US_ONUPLOADQUEUE:
                bDelete = false;
                peer_type = AVAILABLE_SOURCE;
                break;
        };

        switch (m_nDownloadState) {
        case DS_ONQUEUE:
                source_type = A4AF_SOURCE; // Will be checked.
        case DS_TOOMANYCONNS:
        case DS_NONEEDEDPARTS:
        case DS_LOWTOLOWIP:
                bDelete = false;
                break;
        };

        switch (m_nUploadState) {
        case US_CONNECTING:
        case US_ERROR:
                theApp->clientlist->AddDeadSource(this);
                bDelete = true;
        };

        switch (m_nDownloadState) {
        case DS_CONNECTING:
        case DS_ERROR:
        case DS_BANNED:
                theApp->clientlist->AddDeadSource(this);
                bDelete = true;
        };


        // We keep chat partners in any case
        if (GetChatState() != MS_NONE) {
                bDelete = false;
                m_pendingMessage.Clear();
                Notify_ChatConnResult(false,GUI_ID(GetIP(),GetUserPort()),wxEmptyString);
        }

        // Delete socket
        if (!bFromSocket && m_socket) {
                wxASSERT ( theApp->listensocket and theApp->listensocket->IsValidSocket ( m_socket ) );
                m_socket->Safe_Delete();
        }

        SetSocket ( NULL );

        if (m_iFileListRequested) {
                AddLogLineC(CFormat(_("Failed to retrieve shared files from user '%s'")) % GetUserName() );
                m_iFileListRequested = 0;
        }


        if (bDelete) {
                if (m_Friend) {
                        // Remove the friend linkage
                        m_Friend->UnLinkClient();	// this notifies
                }
        } else {
                Notify_SharedCtrlRefreshClient(ECID(), peer_type);
                Notify_SourceCtrlUpdateSource(ECID(), source_type);

                m_fHashsetRequesting = 0;
                SetSentCancelTransfer(0);
                m_bHelloAnswerPending = false;
                m_fSentOutOfPartReqs = 0;
        }
        AddDebugLogLineN(logClient, CFormat(wxT("--- %s client D:%d U:%d \"%s\"; Reason was %s"))
                         % (bDelete ? wxT("Deleted") : wxT("Disconnected"))
                         % m_nDownloadState % m_nUploadState % GetClientFullInfo() % strReason );

        return bDelete;
}

//Returned bool is not if the TryToConnect is successful or not..
//false means the client was deleted!
//true means the client was not deleted!
bool CUpDownClient::TryToConnect(bool bIgnoreMaxCon)
{
        // Kad reviewed
        if ( theApp->listensocket and (theApp->listensocket->TooManySockets() && !bIgnoreMaxCon) ) {
                if (!(m_socket && m_socket->IsConnected())) {
                        if(Disconnected(wxT("Too many connections"))) {
                                Safe_Delete();
                                return false;
                        }
                        return true;
                }
        }

        // Do not try to connect to source which are incompatible with our encryption setting (one requires it, and the other one doesn't supports it)
        if ( (RequiresCryptLayer() && !thePrefs::IsClientCryptLayerSupported()) || (thePrefs::IsClientCryptLayerRequired() && !SupportsCryptLayer()) ) {
                if(Disconnected(wxT("CryptLayer-Settings (Obfuscation) incompatible"))) {
                        Safe_Delete();
                        return false;
                } else {
                        return true;
                }
        }

        // Ipfilter check
        uint32 uClientIP = GetIP();
        if (uClientIP == 0 && !HasLowID()) {
                uClientIP = m_nUserIDHybrid;
        }

        if (uClientIP) {
                // Although we filter all received IPs (server sources, source exchange) and all incomming connection attempts,
                // we do have to filter outgoing connection attempts here too, because we may have updated the ip filter list
                if ( theApp->ipfilter->IsFiltered(uClientIP)) {
                        AddDebugLogLineN(logIPFilter, CFormat ( wxT ( "Filtered dest %x on TryToConnect\n" ) ) % uClientIP );
                        if (Disconnected(wxT("IPFilter"))) {
                                Safe_Delete();
                                return false;
                        }
                        return true;
                }

                // for safety: check again whether that IP is banned
                if ( theApp->clientlist->IsBannedClient(uClientIP)) {
                        AddDebugLogLineN(logClient, CFormat(wxT("Refused to connect to banned client %x")) % uClientIP);
                        if (Disconnected(wxT("Banned IP"))) {
                                Safe_Delete();
                                return false;
                        }
                        return true;
                }
        }

        if (HasLowID()) { /* iMule : nothing to do here : CanDoCallback true when Kad connected
		if (!theApp->CanDoCallback(GetServerIP(), GetServerPort())) {
			//We cannot do a callback!
			if (GetDownloadState() == DS_CONNECTING) {
			        SetDownloadState(DS_LOWTOLOWIP);
			} else if (GetDownloadState() == DS_REQHASHSET) {
			        SetDownloadState(DS_ONQUEUE);
			        m_reqfile->SetHashSetNeeded(true);
			}
			if (GetUploadState() == US_CONNECTING) {
			        if(Disconnected(wxT("LowID->LowID and US_CONNECTING"))) {
			                Safe_Delete();
			                return false;
			        }
			}
			return true;
		}
                //We already know we are not firewalled here as the above condition already detected LowID->LowID and returned.
                //If ANYTHING changes with the "if(!theApp->CanDoCallback(this))" above that will let you fall through
                //with the condition that the source is firewalled and we are firewalled, we must
                //recheck it before the this check..
                if (HasValidBuddyID() && !GetBuddyIP() && !GetBuddyPort() && !theApp->serverconnect->IsLocalServer(GetServerIP(), GetServerPort())
                                && !(SupportsDirectUDPCallback() && thePrefs::GetEffectiveUDPPort() != 0)) {
                        //This is a Kad firewalled source that we want to do a special callback because it has no buddyIP or buddyPort.
                        if( Kademlia::CKademlia::IsConnected() ) {
                                //We are connect to Kad
                                if( Kademlia::CKademlia::GetPrefs()->GetTotalSource() > 0 || Kademlia::CSearchManager::AlreadySearchingFor(Kademlia::CUInt128(GetBuddyID()))) {
                                        //There are too many source lookups already or we are already searching this key.
                                        SetDownloadState(DS_TOOMANYCONNSKAD);
                                        return true;
                                }
                        }
                }
		// iMule : end of nothing to do ! */
        }
        if (!m_socket || !m_socket->IsConnected()) {
                if (m_socket) {
                        m_socket->Safe_Delete();
                }
                m_socket = ::new CClientTCPSocket(this);
        } else {
                ConnectionEstablished();
                return true;
        }

        if (!Connect()) {
                return false;
        }
        return true;
}

bool CUpDownClient::Connect()
{
        m_hasbeenobfuscatinglately = false;

        if (!m_socket->IsOk()) {
                // Enable or disable crypting based on our and the remote clients preference
                if (HasValidHash() && SupportsCryptLayer() && thePrefs::IsClientCryptLayerSupported() && (RequestsCryptLayer() || thePrefs::IsClientCryptLayerRequested())) {
                        m_socket->SetConnectionEncryption(true, GetUserHash().GetHash(), false);
                } else {
                        m_socket->SetConnectionEncryption(false, NULL, false);
                }
                CI2PAddress tmp(GetConnectIP());
                AddDebugLogLineN(logClient, wxT("Trying to connect to ") + GetConnectIP().humanReadable());
                m_socket->Connect(tmp, false);
                // We should send hello packets AFTER connecting!
                // so I moved it to OnConnect
                return true;
        } else {
                return false;
        }
}

void CUpDownClient::ConnectionEstablished()
{
        /* Kry - First thing, check if this client was just used to retrieve
           info. That's some debug thing for myself... check connection_reason
           definition */

        m_hasbeenobfuscatinglately = (m_socket && m_socket->IsConnected() && m_socket->IsObfusicating());

#ifdef __DEBUG__
        if (!connection_reason.IsEmpty()) {
                AddLogLineN(CFormat(wxT("Got client info checking for %s: %s\nDisconnecting and deleting.")) % connection_reason % GetClientFullInfo());
                connection_reason.Clear(); // So we don't re-print on destructor.
                Safe_Delete();
                return;
        }
#endif



        // ok we have a connection, lets see if we want anything from this client
        if (GetChatState() == MS_CONNECTING) {
                SetChatState( MS_CHATTING );
        }

        if (GetChatState() == MS_CHATTING) {
                bool result = true;
                if (!m_pendingMessage.IsEmpty()) {
                        result = SendChatMessage(m_pendingMessage);
                }
                Notify_ChatConnResult(result, GUI_ID(GetIP(), GetUserPort()), m_pendingMessage);
                m_pendingMessage.Clear();
        }

        switch(GetDownloadState()) {
        case DS_CONNECTING:
                m_bReaskPending = false;
                SetDownloadState(DS_CONNECTED);
                SendFileRequest();
        }
        if (m_bReaskPending) {
                m_bReaskPending = false;
                if (GetDownloadState() != DS_NONE && GetDownloadState() != DS_DOWNLOADING) {
                        SetDownloadState(DS_CONNECTED);
                        SendFileRequest();
                }
        }
        switch(GetUploadState()) {
        case US_CONNECTING:
                if (theApp->uploadqueue->IsDownloading(this)) {
                        SetUploadState(US_UPLOADING);
                        CPacket* packet = new CPacket(OP_ACCEPTUPLOADREQ, 0, OP_EDONKEYPROT);
                        theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
                        SendPacket(packet, true);
                        AddDebugLogLineN(logLocalClient, wxT("Local Client: OP_ACCEPTUPLOADREQ to ") + GetIP().humanReadable());
                }
        }
        if (m_iFileListRequested == 1) {
                CPacket* packet = new CPacket(m_fSharedDirectories ? OP_ASKSHAREDDIRS : OP_ASKSHAREDFILES, 0, OP_EDONKEYPROT);
                theStats::AddUpOverheadOther(packet->GetPacketSize());
                SendPacket(packet,true,true);
                if (m_fSharedDirectories) {
                        AddDebugLogLineN(logLocalClient, wxT("Local Client: OP_ASKSHAREDDIRS to ") + GetIP().humanReadable());
                } else {
                        AddDebugLogLineN(logLocalClient, wxT("Local Client: OP_ASKSHAREDFILES to ") + GetIP().humanReadable());
                }
        }

        while (!m_WaitingPackets_list.empty()) {
                CPacket* packet = m_WaitingPackets_list.front();
                m_WaitingPackets_list.pop_front();

                SendPacket(packet);
        }
}


int CUpDownClient::GetHashType() const
{
        if ( m_UserHash[5] == 13  && m_UserHash[14] == 110 ) {
                return SO_OLDEMULE;
        }

        if ( m_UserHash[5] == 14  && m_UserHash[14] == 111 ) {
                return SO_EMULE;
        }

        if ( m_UserHash[5] == 'M' && m_UserHash[14] == 'L' ) {
                return SO_MLDONKEY;
        }

        return SO_UNKNOWN;
}


void CUpDownClient::SetSocket(CClientTCPSocket* socket)
{
#ifdef __DEBUG__
        if (m_socket == NULL && socket != NULL) {
                theStats::SocketAssignedToClient();
        } else if (m_socket != NULL && socket == NULL) {
                theStats::SocketUnassignedFromClient();
        }
#endif
        m_socket = socket;
}


void CUpDownClient::ReGetClientSoft()
{
        if (m_Username.IsEmpty()) {
                m_clientSoft=SO_UNKNOWN;
                m_clientVerString = m_clientSoftString = m_clientVersionString = m_fullClientVerString = _("Unknown");
                UpdateStats();
                return;
        }

        int iHashType = GetHashType();
        wxString clientModString;
        if (iHashType == SO_EMULE) {

                m_clientSoft = m_byCompatibleClient;
                m_clientSoftString = GetSoftName(m_clientSoft);
                // Special issues:
                if(!GetClientModString().IsEmpty() && (m_clientSoft != SO_EMULE)) {
                        m_clientSoftString = GetClientModString();
                }
                // Isn't xMule annoying?
                if ((m_clientSoft == SO_LXMULE) && (GetMuleVersion() > 0x26) && (GetMuleVersion() != 0x99)) {
                        m_clientSoftString += CFormat(_(" (Fake eMule version %#x)")) % GetMuleVersion();
                }
                if ((m_clientSoft == SO_EMULE) &&
                                (
                                        wxString(GetClientModString()).MakeLower().Find(wxT("xmule")) != -1
                                        || GetUserName().Find(wxT("xmule.")) != -1
                                )
                   ) {
                        // FAKE eMule -a newer xMule faking is ident.
                        m_clientSoft = SO_LXMULE;
                        if (GetClientModString().IsEmpty() == false) {
                                m_clientSoftString = GetClientModString() + _(" (Fake eMule)");
                        } else {
                                m_clientSoftString = _("xMule (Fake eMule)"); // don't use GetSoftName, it's not lmule.
                        }
                }
                // Now, what if we don't know this SO_ID?
                if (m_clientSoftString.IsEmpty()) {
                        if(m_bIsML) {
                                m_clientSoft = SO_MLDONKEY;
                                m_clientSoftString = GetSoftName(m_clientSoft);
                        } else if (m_bIsHybrid) {
                                m_clientSoft = SO_EDONKEYHYBRID;
                                m_clientSoftString = GetSoftName(m_clientSoft);
                        } else if (m_byCompatibleClient != 0) {
                                m_clientSoft = SO_COMPAT_UNK;
#ifdef __DEBUG__
                                if (
                                        // Exceptions:
                                        (m_byCompatibleClient != 0xf0)	// Chinese leech mod
                                        && (1==1) 						// Your ad here
                                ) {
                                        AddLogLineNS(CFormat(wxT("Compatible client found with ET_COMPATIBLECLIENT of %x")) % m_byCompatibleClient);
                                }
#endif
                                m_clientSoftString = CFormat(wxT("%s(%#x)")) % GetSoftName(m_clientSoft) % m_byCompatibleClient;
                        } else {
                                // If we step here, it might mean 2 things:
                                // a eMule
                                // a Compat Client that has sent no MuleInfo packet yet.
                                m_clientSoft = SO_EMULE;
                                m_clientSoftString = wxT("eMule");
                        }
                }

                if (m_byEmuleVersion == 0) {
                        m_nClientVersion = MAKE_CLIENT_VERSION(0,0,0);
                } else if (m_byEmuleVersion != 0x99) {
                        uint32 nClientMinVersion = (m_byEmuleVersion >> 4)*10 + (m_byEmuleVersion & 0x0f);
                        m_nClientVersion = MAKE_CLIENT_VERSION(0,nClientMinVersion,0);
                        switch (m_clientSoft) {
                        case SO_AMULE:
                        case SO_IMULE:
                                m_clientVerString = CFormat(_("1.x (based on eMule v0.%u)")) % nClientMinVersion;
                                break;
                        case SO_LPHANT:
                                m_clientVerString = wxT("< v0.05");
                                break;
                        default:
                                clientModString = GetClientModString();
                                m_clientVerString = CFormat(wxT("v0.%u")) % nClientMinVersion;
                                break;
                        }
                } else {
                        uint32 nClientMajVersion = (m_nClientVersion >> 17) & 0x7f;
                        uint32 nClientMinVersion = (m_nClientVersion >> 10) & 0x7f;
                        uint32 nClientUpVersion  = (m_nClientVersion >>  7) & 0x07;

                        m_nClientVersion = MAKE_CLIENT_VERSION(nClientMajVersion, nClientMinVersion, nClientUpVersion);

                        switch (m_clientSoft) {
                        case SO_AMULE:
                        case SO_IMULE:
                        case SO_LXMULE:
                        case SO_HYDRANODE:
                        case SO_MLDONKEY:
                        case SO_NEW_MLDONKEY:
                        case SO_NEW2_MLDONKEY:
                                // Kry - xMule started sending correct version tags on 1.9.1b.
                                // It only took them 4 months, and being told by me and the
                                // eMule+ developers, so I think they're slowly getting smarter.
                                // They are based on our implementation, so we use the same format
                                // for the version string.
                                m_clientVerString = CFormat(wxT("v%u.%u.%u")) % nClientMajVersion % nClientMinVersion % nClientUpVersion;
                                break;
                        case SO_LPHANT:
                                m_clientVerString = CFormat(wxT(" v%u.%.2u%c")) % (nClientMajVersion-1) % nClientMinVersion % ('a' + nClientUpVersion);
                                break;
                        case SO_EMULEPLUS:
                                m_clientVerString = CFormat(wxT("v%u")) % nClientMajVersion;
                                if(nClientMinVersion != 0) {
                                        m_clientVerString += CFormat(wxT(".%u")) % nClientMinVersion;
                                }
                                if(nClientUpVersion != 0) {
                                        m_clientVerString += CFormat(wxT("%c")) % ('a' + nClientUpVersion - 1);
                                }
                                break;
                        default:
                                clientModString = GetClientModString();
                                m_clientVerString = CFormat(wxT("v%u.%u%c")) % nClientMajVersion % nClientMinVersion % ('a' + nClientUpVersion);
                                break;
                        }
                }
        } else if (m_bIsHybrid) {
                // seen:
                // 105010	50.10
                // 10501	50.1
                // 1051		51.0
                // 501		50.1

                m_clientSoft = SO_EDONKEYHYBRID;
                m_clientSoftString = GetSoftName(m_clientSoft);

                uint32 nClientMajVersion;
                uint32 nClientMinVersion;
                uint32 nClientUpVersion;
                if (m_nClientVersion > 100000) {
                        uint32 uMaj = m_nClientVersion/100000;
                        nClientMajVersion = uMaj - 1;
                        nClientMinVersion = (m_nClientVersion - uMaj*100000) / 100;
                        nClientUpVersion = m_nClientVersion % 100;
                } else if (m_nClientVersion > 10000) {
                        uint32 uMaj = m_nClientVersion/10000;
                        nClientMajVersion = uMaj - 1;
                        nClientMinVersion = (m_nClientVersion - uMaj*10000) / 10;
                        nClientUpVersion = m_nClientVersion % 10;
                } else if (m_nClientVersion > 1000) {
                        uint32 uMaj = m_nClientVersion/1000;
                        nClientMajVersion = uMaj - 1;
                        nClientMinVersion = m_nClientVersion - uMaj*1000;
                        nClientUpVersion = 0;
                } else if (m_nClientVersion > 100) {
                        uint32 uMin = m_nClientVersion/10;
                        nClientMajVersion = 0;
                        nClientMinVersion = uMin;
                        nClientUpVersion = m_nClientVersion - uMin*10;
                } else {
                        nClientMajVersion = 0;
                        nClientMinVersion = m_nClientVersion;
                        nClientUpVersion = 0;
                }
                m_nClientVersion = MAKE_CLIENT_VERSION(nClientMajVersion, nClientMinVersion, nClientUpVersion);
                if (nClientUpVersion) {
                        m_clientVerString = CFormat(wxT("v%u.%u.%u")) % nClientMajVersion % nClientMinVersion % nClientUpVersion;
                } else {
                        m_clientVerString = CFormat(wxT("v%u.%u")) % nClientMajVersion % nClientMinVersion;
                }
        } else if (m_bIsML || (iHashType == SO_MLDONKEY)) {
                m_clientSoft = SO_MLDONKEY;
                m_clientSoftString = GetSoftName(m_clientSoft);
                uint32 nClientMinVersion = m_nClientVersion;
                m_nClientVersion = MAKE_CLIENT_VERSION(0, nClientMinVersion, 0);
                m_clientVerString = CFormat(wxT("v0.%u")) % nClientMinVersion;
        } else if (iHashType == SO_OLDEMULE) {
                m_clientSoft = SO_OLDEMULE;
                m_clientSoftString = GetSoftName(m_clientSoft);
                uint32 nClientMinVersion = m_nClientVersion;
                m_nClientVersion = MAKE_CLIENT_VERSION(0, nClientMinVersion, 0);
                m_clientVerString = CFormat(wxT("v0.%u")) % nClientMinVersion;
        } else {
                m_clientSoft = SO_EDONKEY;
                m_clientSoftString = GetSoftName(m_clientSoft);
                m_nClientVersion *= 10;
                m_clientVerString = CFormat(wxT("v%u.%u")) % (m_nClientVersion / 100000) % ((m_nClientVersion / 1000) % 100);
        }

        m_clientVersionString = m_clientVerString;
        if (!clientModString.IsEmpty()) {
                m_clientVerString += wxT(" - ") + clientModString;
        }
        m_fullClientVerString = m_clientSoftString + wxT(" ") + m_clientVerString;

        UpdateStats();
}

void CUpDownClient::RequestSharedFileList()
{
        if (m_iFileListRequested == 0) {
                AddDebugLogLineN( logClient, wxString( wxT("Requesting shared files from ") ) + GetUserName() );
                m_iFileListRequested = 1;
                TryToConnect(true);
        } else {
                AddDebugLogLineN(logClient, CFormat( wxT("Requesting shared files from user %s (%x) is already in progress")) % GetUserName() % GetUserIDHybrid().hashCode());
        }
}


void CUpDownClient::ProcessSharedFileList(const byte* pachPacket, uint32 nSize, wxString& pszDirectory)
{
        if (m_iFileListRequested > 0) {
                m_iFileListRequested--;
                theApp->searchlist->ProcessSharedFileList(pachPacket, nSize, this, NULL, pszDirectory);
        }
}


void CUpDownClient::ResetFileStatusInfo()
{
        m_nPartCount = 0;

        if ( m_reqfile ) {
                m_reqfile->UpdatePartsFrequency( this, false );
        }
        m_downPartStatus.clear();

        m_clientFilename.Clear();

        m_bCompleteSource = false;
        m_dwLastAskedTime = 0;
        m_iRating = 0;
        m_strComment.Clear();

        if (m_pReqFileAICHHash != NULL) {
                delete m_pReqFileAICHHash;
                m_pReqFileAICHHash = NULL;
        }

}

wxString CUpDownClient::GetUploadFileInfo()
{
        // build info text and display it
        wxString sRet;
        sRet = ( CFormat ( _ ( "NickName: %s ID: %x\n" ) ) % GetUserName() % GetUserIDHybrid().hashCode() );
        if (m_reqfile) {
                sRet += CFormat(_("Requested: %s\n")) % m_reqfile->GetFileName();
                sRet += CFormat(
                                wxPLURAL("Filestats for this session: Accepted %d of %d request, %s transferred\n", "Filestats for this session: Accepted %d of %d requests, %s transferred\n", m_reqfile->statistic.GetRequests())
                        ) % m_reqfile->statistic.GetAccepts() % m_reqfile->statistic.GetRequests() % CastItoXBytes(m_reqfile->statistic.GetTransferred());
                sRet += CFormat(
                                wxPLURAL("Filestats for all sessions: Accepted %d of %d request, %s transferred\n", "Filestats for all sessions: Accepted %d of %d requests, %s transferred\n", m_reqfile->statistic.GetAllTimeRequests())
                        ) % m_reqfile->statistic.GetAllTimeAccepts() % m_reqfile->statistic.GetAllTimeRequests() % CastItoXBytes(m_reqfile->statistic.GetAllTimeTransferred());
        } else {
                sRet += _("Requested unknown file");
        }
        return sRet;
}

// sends a packet, if needed it will establish a connection before
// options used: ignore max connections, control packet, delete packet
// !if the functions returns false it is _possible_ that this clientobject was deleted, because the connectiontry fails
bool CUpDownClient::SafeSendPacket(CPacket* packet)
{
        if (IsConnected()) {
                SendPacket(packet, true);
                return true;
        } else {
                m_WaitingPackets_list.push_back(packet);
                return TryToConnect(true);
        }
}

void CUpDownClient::SendPublicKeyPacket()
{
        // send our public key to the client who requested it
        if (m_socket == NULL || credits == NULL || m_SecureIdentState != IS_KEYANDSIGNEEDED) {
                wxFAIL;
                return;
        }
        if (!theApp->CryptoAvailable())
                return;

        CMemFile data;
        data.WriteUInt8(theApp->clientcredits->GetPubKeyLen());
        data.Write(theApp->clientcredits->GetPublicKey(), theApp->clientcredits->GetPubKeyLen());
        CPacket* packet = new CPacket(data, OP_EMULEPROT, OP_PUBLICKEY);

        theStats::AddUpOverheadOther(packet->GetPacketSize());
        AddDebugLogLineN(logLocalClient, wxT("Local Client: OP_PUBLICKEY to ") + GetIP().humanReadable());
        SendPacket(packet, true, true);
        m_SecureIdentState = IS_SIGNATURENEEDED;
}


void CUpDownClient::SendSignaturePacket()
{
        // signate the public key of this client and send it
        if (m_socket == NULL || credits == NULL || m_SecureIdentState == 0) {
                wxFAIL;
                return;
        }

        if (!theApp->CryptoAvailable()) {
                return;
        }
        if (credits->GetSecIDKeyLen() == 0) {
                return; // We don't have his public key yet, will be back here later
        }
        // do we have a challenge value received (actually we should if we are in this function)
        if (credits->m_dwCryptRndChallengeFrom == 0) {
                AddDebugLogLineN( logClient, wxString(wxT("Want to send signature but challenge value is invalid - User ")) + GetUserName());
                return;
        }
        // v2
        // we will use v1 as default, except if only v2 is supported
        bool bUseV2;
        if ( (m_bySupportSecIdent&1) == 1 )
                bUseV2 = false;
        else
                bUseV2 = true;

        uint8 byChaIPKind = 0;
        uint32 ChallengeIP = 0;
        if ( bUseV2 ) {
                ChallengeIP = theApp->GetED2KID();
                byChaIPKind  = CRYPT_CIP_LOCALCLIENT;
        }
        //end v2
        byte achBuffer[250];

        uint8 siglen = theApp->clientcredits->CreateSignature(credits, achBuffer,  250, ChallengeIP, byChaIPKind );
        if (siglen == 0) {
                wxFAIL;
                return;
        }
        CMemFile data;
        data.WriteUInt8(siglen);
        data.Write(achBuffer, siglen);
        if (bUseV2) {
                data.WriteUInt8(byChaIPKind);
        }

        CPacket* packet = new CPacket(data, OP_EMULEPROT, OP_SIGNATURE);

        theStats::AddUpOverheadOther(packet->GetPacketSize());
        AddDebugLogLineN( logLocalClient, wxT("Local Client: OP_SIGNATURE to ") + GetIP().humanReadable() );
        SendPacket(packet,true,true);
        m_SecureIdentState = IS_ALLREQUESTSSEND;
}


void CUpDownClient::ProcessPublicKeyPacket(const byte* pachPacket, uint32 nSize)
{
        theApp->clientlist->AddTrackClient(this);

        if (m_socket == NULL || credits == NULL || pachPacket[0] != nSize-1
                        || nSize == 0 || nSize > 250) {
                wxFAIL;
                return;
        }
        if (!theApp->CryptoAvailable())
                return;
        // the function will handle everything (mulitple key etc)
        if (credits->SetSecureIdent(pachPacket+1, pachPacket[0])) {
                // if this client wants a signature, now we can send him one
                if (m_SecureIdentState == IS_SIGNATURENEEDED) {
                        SendSignaturePacket();
                } else if (m_SecureIdentState == IS_KEYANDSIGNEEDED) {
                        // something is wrong
                        AddDebugLogLineN( logClient, wxT("Invalid State error: IS_KEYANDSIGNEEDED in ProcessPublicKeyPacket") );
                }
        } else {
                AddDebugLogLineN( logClient, wxT("Failed to use new received public key") );
        }
}


void CUpDownClient::ProcessSignaturePacket(const byte* pachPacket, uint32 nSize)
{
        // here we spread the good guys from the bad ones ;)

        if (m_socket == NULL || credits == NULL || nSize == 0 || nSize > 250) {
                wxFAIL;
                return;
        }

        uint8 byChaIPKind;
        if (pachPacket[0] == nSize-1)
                byChaIPKind = 0;
        else if (pachPacket[0] == nSize-2 && (m_bySupportSecIdent & 2) > 0) //v2
                byChaIPKind = pachPacket[nSize-1];
        else {
                wxFAIL;
                return;
        }

        if (!theApp->CryptoAvailable())
                return;

        // we accept only one signature per IP, to avoid floods which need a lot cpu time for cryptfunctions
        if (m_dwLastSignatureDest == GetIP()) {
                AddDebugLogLineN( logClient, wxT("received multiple signatures from one client") );
                return;
        }
        // also make sure this client has a public key
        if (credits->GetSecIDKeyLen() == 0) {
                AddDebugLogLineN( logClient, wxT("received signature for client without public key") );
                return;
        }
        // and one more check: did we ask for a signature and sent a challange packet?
        if (credits->m_dwCryptRndChallengeFor == 0) {
                AddDebugLogLineN( logClient, wxT("received signature for client with invalid challenge value - User ") + GetUserName() );
                return;
        }

        if (theApp->clientcredits->VerifyIdent(credits, pachPacket+1, pachPacket[0], GetIP(), byChaIPKind ) ) {
                // result is saved in function above
                AddDebugLogLineN( logClient, CFormat( wxT("'%s' has passed the secure identification, V2 State: %i") ) % GetUserName() % byChaIPKind );
        } else {
                AddDebugLogLineN( logClient, CFormat( wxT("'%s' has failed the secure identification, V2 State: %i") ) % GetUserName() % byChaIPKind );
        }

        m_dwLastSignatureDest = GetIP();
}

void CUpDownClient::SendSecIdentStatePacket()
{
        // check if we need public key and signature
        uint8 nValue = 0;
        if (credits) {
                if (theApp->CryptoAvailable()) {
                        if (credits->GetSecIDKeyLen() == 0) {
                                nValue = IS_KEYANDSIGNEEDED;
                        } else if (m_dwLastSignatureDest != GetIP()) {
                                nValue = IS_SIGNATURENEEDED;
                        }
                }
                if (nValue == 0) {
                        AddDebugLogLineN( logClient, wxT("Not sending SecIdentState Packet, because State is Zero") );
                        return;
                }
                // crypt: send random data to sign
                uint32 dwRandom = rand()+1;
                credits->m_dwCryptRndChallengeFor = dwRandom;

                CMemFile data;
                data.WriteUInt8(nValue);
                data.WriteUInt32(dwRandom);
                CPacket* packet = new CPacket(data, OP_EMULEPROT, OP_SECIDENTSTATE);

                theStats::AddUpOverheadOther(packet->GetPacketSize());
                AddDebugLogLineN( logLocalClient, wxT("Local Client: OP_SECIDENTSTATE to ") + GetIP().humanReadable() );
                SendPacket(packet,true,true);
        } else {
                wxFAIL;
        }
}


void CUpDownClient::ProcessSecIdentStatePacket(const byte* pachPacket, uint32 nSize)
{
        if ( nSize != 5 ) {
                return;
        }

        if ( !credits ) {
                wxASSERT( credits );
                return;
        }

        CMemFile data(pachPacket,nSize);

        switch ( data.ReadUInt8() ) {
        case 0:
                m_SecureIdentState = IS_UNAVAILABLE;
                break;
        case 1:
                m_SecureIdentState = IS_SIGNATURENEEDED;
                break;
        case 2:
                m_SecureIdentState = IS_KEYANDSIGNEEDED;
                break;
        default:
                return;
        }

        credits->m_dwCryptRndChallengeFrom = data.ReadUInt32();
}


void CUpDownClient::InfoPacketsReceived()
{
        // indicates that both Information Packets has been received
        // needed for actions, which process data from both packets
        wxASSERT ( m_byInfopacketsReceived == IP_BOTH );
        m_byInfopacketsReceived = IP_NONE;

        if (m_bySupportSecIdent) {
                SendSecIdentStatePacket();
        }
}


bool CUpDownClient::CheckHandshakeFinished() const
{
        if (m_bHelloAnswerPending) {
                // this triggers way too often.. need more time to look at this -> only create a warning
                // The reason for this is that 2 clients are connecting to each other at the same time..
                AddDebugLogLineN( logClient, wxT("Handshake not finished while processing packet.") );
                return false;
        }

        return true;
}


wxString CUpDownClient::GetClientFullInfo()
{
        if (m_clientVerString.IsEmpty()) {
                ReGetClientSoft();
        }

        return CFormat( wxT("Client %s on Dest: %s using %s %s %s" ) )
               % ( m_Username.IsEmpty() ? wxString (_("Unknown")) : m_Username )
               % GetIP().humanReadable()
               % m_clientSoftString
               % m_clientVerString
               % m_strModVersion;
}


wxString CUpDownClient::GetClientShortInfo()
{
        if (m_clientVerString.IsEmpty()) {
                ReGetClientSoft();
        }

        return CFormat( wxT("'%s' (%s %s %s)") )
               % ( m_Username.IsEmpty() ? wxString(_("Unknown")) : m_Username )
               % m_clientSoftString
               % m_clientVerString
               % m_strModVersion;
}


void CUpDownClient::SendPublicIPRequest()
{
        if (IsConnected()) {
                CPacket* packet = new CPacket(OP_PUBLICIP_REQ,0,OP_EMULEPROT);
                theStats::AddUpOverheadOther(packet->GetPacketSize());
                AddDebugLogLineN( logLocalClient, wxT("Local Client: OP_PUBLICIP_REQ to ") + GetIP().humanReadable());
                SendPacket(packet,true);
                m_fNeedOurPublicIP = true;
        }
}

void CUpDownClient::ProcessPublicIPAnswer(const byte* pbyData, uint32 uSize)
{
        if (uSize != 4) {
                throw wxString(wxT("Wrong Packet size on Public IP answer"));
        }

        CI2PAddress dwDest;
        dwDest.readBytes ( pbyData );
        if (m_fNeedOurPublicIP == true) { // did we?
                m_fNeedOurPublicIP = false;
                // Ignore local ip on GetPublicIP (could be wrong)

                if ( !theApp->GetPublicDest(true) && !IsLowID(dwDest) ) {
                        theApp->SetPublicDest(dwDest);
                }
        }
}


bool CUpDownClient::IsConnected() const
{
        return m_socket && m_socket->IsConnected();
}

bool CUpDownClient::SendPacket(CPacket* packet, bool delpacket, bool controlpacket)
{
        if ( m_socket ) {
                m_socket->SendPacket(packet, delpacket, controlpacket );
                return true;
        } else {
                AddLogLineN(wxT("CAUGHT DEAD SOCKET IN SENDPACKET()"));
                return false;
        }
}

float CUpDownClient::SetDownloadLimit(uint32 reducedownload)
{
        // lfroen: in daemon it actually can happen
        wxASSERT( m_socket );

        float kBpsClient = CalculateKBpsDown();

        if ( m_socket ) {

                if (reducedownload) {
                        // (% to reduce * current speed) / 100 and THEN, / (1000 / CORE_TIMER_PERIOD)
                        // which is how often it is called per second.
                        uint32 limit = (uint32)(reducedownload * kBpsClient * 1024.0 / 100000.0 * CORE_TIMER_PERIOD);

                        if(limit<1024 && reducedownload >= 200) {
                                // If we're going up and this download is < 1kB,
                                // we want it to go up fast. Can be reduced later,
                                // and it'll probably be in a more fair way with
                                // other downloads that are faster.
                                limit +=1024;
                        } else if(limit == 0) {
                                // This download is not transferring yet... make it
                                // 1024 so we don't fill the TCP stack and lose the
                                // connection.
                                limit = 1024;
                        }

                        m_socket->SetDownloadLimit(limit);
                } else {
                        m_socket->DisableDownloadLimit();
                }

        } else {
                AddLogLineNS(CFormat(wxT("CAUGHT DEAD SOCKET IN SETDOWNLOADLIMIT() WITH SPEED %f")) % kBpsClient);
        }

        return kBpsClient;
}

void CUpDownClient::SetUserIDHybrid(const CI2PAddress & nUserID)
{
        theApp->clientlist->UpdateClientID( this, nUserID );

        m_nUserIDHybrid = nUserID;
}



void CUpDownClient::SetTCPDest ( const CI2PAddress & val )
{
        theApp->clientlist->UpdateClientIP( this, val );

        m_dwUserIP = val;

        m_nConnectIP = val;
}


void CUpDownClient::SetUserHash(const CMD4Hash& userhash)
{
        theApp->clientlist->UpdateClientHash( this, userhash );

        m_UserHash = userhash;

        ValidateHash();
}

EUtf8Str CUpDownClient::GetUnicodeSupport() const
{
        return m_bUnicodeSupport ? utf8strRaw : utf8strNone;
}

void CUpDownClient::SetSpammer(bool bVal)
{
        if (bVal) {
                Ban();
        } else if (IsBanned() && m_fIsSpammer) {
                UnBan();
        }
        m_fIsSpammer = bVal;
}

uint8 CUpDownClient::GetSecureIdentState()
{
        if (m_SecureIdentState != IS_UNAVAILABLE) {
                if (!SecIdentSupRec) {
                        // This can be caused by a 0.30x based client which sends the old
                        // style Hello packet, and the mule info packet, but between them they
                        // send a secure ident state packet (after a hello but before we have
                        // the SUI capabilities). This is a misbehaving client, and somehow I
                        // feel like it should be dropped. But then again, it won't harm to use
                        // this SUI state if they are reporting no SUI (won't be used) and if
                        // they report using SUI on the mule info packet, it's ok to use it.

                        AddDebugLogLineN(logClient, wxT("A client sent secure ident state before telling us the SUI capabilities"));
                        AddDebugLogLineN(logClient, wxT("Client info: ") + GetClientFullInfo());
                        AddDebugLogLineN(logClient, wxT("This client won't be disconnected, but it should be. :P"));
                }
        }

        return m_SecureIdentState;
}


bool CUpDownClient::SendChatMessage(const wxString& message)
{
        if (GetChatCaptchaState() == CA_CAPTCHARECV) {
                m_nChatCaptchaState = CA_SOLUTIONSENT;
        } else if (GetChatCaptchaState() == CA_SOLUTIONSENT) {
                wxFAIL; // we responsed to a captcha but didn't heard from the client afterwards - hopefully its just lag and this message will get through
        } else {
                m_nChatCaptchaState = CA_ACCEPTING;
        }

        SetSpammer(false);
        IncMessagesSent();
        // Already connecting?
        if (GetChatState() == MS_CONNECTING) {
                // Queue all messages till we're able to send them (or discard them)
                if (!m_pendingMessage.IsEmpty()) {
                        m_pendingMessage += wxT("\n");
                } else {
                        // There must be a message to send
                        // - except if we got disconnected. No need to assert therefore.
                }
                m_pendingMessage += message;
                return false;
        }
        if (IsConnected()) {
                // If we are already connected when we send the first message,
                // we have to update the chat status.
                SetChatState(MS_CHATTING);
                CMemFile data;
                data.WriteString(message, GetUnicodeSupport());
                CPacket* packet = new CPacket(data, OP_EDONKEYPROT, OP_MESSAGE);
                theStats::AddUpOverheadOther(packet->GetPacketSize());
                AddDebugLogLineN( logLocalClient, wxT("Local Client: OP_MESSAGE to ") + GetIP().humanReadable());
                SendPacket(packet, true, true);
                return true;
        } else {
                m_pendingMessage = message;
                SetChatState(MS_CONNECTING);
                // True to ignore "Too Many Connections"
                TryToConnect(true);
                return false;
        }
}


/* Statistics */

void CUpDownClient::UpdateStats()
{
        if (m_lastClientSoft != m_clientSoft || m_lastClientVersion != m_nClientVersion || m_lastOSInfo != m_sClientOSInfo) {
                if (m_lastClientSoft == SO_UNKNOWN) {
                        theStats::RemoveUnknownClient();
                } else if (m_lastClientSoft != (uint32)(-1)) {
                        theStats::RemoveKnownClient(m_lastClientSoft, m_lastClientVersion, m_lastOSInfo);
                }

                m_lastClientSoft = m_clientSoft;
                m_lastClientVersion = m_nClientVersion;
                m_lastOSInfo = m_sClientOSInfo;

                if (m_clientSoft == SO_UNKNOWN) {
                        theStats::AddUnknownClient();
                } else {
                        theStats::AddKnownClient(this);
                }
        }
}

bool CUpDownClient::IsIdentified() const
{
        return (credits && credits->GetCurrentIdentState(GetIP()) == IS_IDENTIFIED);
}

bool CUpDownClient::IsBadGuy() const
{
        return (credits && credits->GetCurrentIdentState(GetIP()) == IS_IDBADGUY);
}

bool CUpDownClient::SUIFailed() const
{
        return (credits && credits->GetCurrentIdentState(GetIP()) == IS_IDFAILED);
}

bool CUpDownClient::SUINeeded() const
{
        return (credits && credits->GetCurrentIdentState(GetIP()) == IS_IDNEEDED);
}

bool CUpDownClient::SUINotSupported() const
{
        return ( credits && credits->GetCurrentIdentState(GetIP()) == IS_NOTAVAILABLE);
}

uint64 CUpDownClient::GetDownloadedTotal() const
{
        return credits ? credits->GetDownloadedTotal() : 0;
}

uint64 CUpDownClient::GetUploadedTotal() const
{
        return credits ? credits->GetUploadedTotal() : 0;
}

double CUpDownClient::GetScoreRatio() const
{
        return credits ? credits->GetScoreRatio(GetIP(), theApp->CryptoAvailable()) : 0;
}

const wxString CUpDownClient::GetServerName() const
{
        wxString ret;
        wxString srvaddr = GetServerIP().toString();
        CServer* cserver = theApp->serverlist->GetServerByAddress(
                                   srvaddr);
        if (cserver) {
                ret = cserver->GetListName();
        } else {
                ret = _("Unknown");
        }

        return ret;
}

bool CUpDownClient::ShouldReceiveCryptUDPPackets() const
{
        return (thePrefs::IsClientCryptLayerSupported() && SupportsCryptLayer() && theApp->GetPublicDest().isValid()
                && HasValidHash() && (thePrefs::IsClientCryptLayerRequested() || RequestsCryptLayer()) );
}

#ifdef AMULE_DAEMON

void CUpDownClient::ProcessCaptchaRequest(CMemFile* WXUNUSED(data)) {}
void CUpDownClient::ProcessCaptchaReqRes(uint8 WXUNUSED(nStatus)) {}
void CUpDownClient::ProcessChatMessage(const wxString WXUNUSED(message)) {}

#else

void CUpDownClient::ProcessCaptchaRequest(CMemFile* data)
{
        CI2PAddress id = GetIP();
        // received a captcha request, check if we actually accept it (only after sending a message ourself to this client)
        if (GetChatCaptchaState() == CA_ACCEPTING && GetChatState() != MS_NONE
                        && theApp->amuledlg->m_chatwnd->IsIdValid(id)) {
                // read tags (for future use)
                uint8 nTagCount = data->ReadUInt8();
                if (nTagCount) {
                        AddDebugLogLineN(logClient, CFormat(wxT("Received captcha request from client (%s) with (%u) tags")) % GetFullIP() % nTagCount);
                        // and ignore them for now
                        for (uint32 i = 0; i < nTagCount; i++) {
                                CTag tag(*data, true);
                        }
                }

                // sanitize checks - we want a small captcha not a wallpaper
                uint32 nSize = (uint32)(data->GetLength() - data->GetPosition());

                if ( nSize > 128 && nSize < 4096 ) {
                        uint64 pos = data->GetPosition();
                        wxMemoryInputStream memstr(data->GetRawBuffer() + pos, nSize);
                        wxImage imgCaptcha(memstr, wxBITMAP_TYPE_BMP);

                        if (imgCaptcha.IsOk() && imgCaptcha.GetHeight() > 10 && imgCaptcha.GetHeight() < 50
                                        && imgCaptcha.GetWidth() > 10 && imgCaptcha.GetWidth() < 150 ) {
                                m_nChatCaptchaState = CA_CAPTCHARECV;
                                CCaptchaDialog * dialog = new CCaptchaDialog(theApp->amuledlg, imgCaptcha, id);
                                dialog->Show();

                        } else {
                                AddDebugLogLineN(logClient, CFormat(wxT("Received captcha request from client, processing image failed or invalid pixel size (%s)")) % GetFullIP());
                        }
                } else {
                        AddDebugLogLineN(logClient, CFormat(wxT("Received captcha request from client, size sanitize check failed (%u) (%s)")) % nSize % GetFullIP());
                }
        } else {
                AddDebugLogLineN(logClient, CFormat(wxT("Received captcha request from client, but don't accepting it at this time (%s)")) % GetFullIP());
        }
}

void CUpDownClient::ProcessCaptchaReqRes(uint8 nStatus)
{
        uint64 id = GUI_ID(GetIP(),GetUserPort());
        if (GetChatCaptchaState() == CA_SOLUTIONSENT && GetChatState() != MS_NONE
                        && theApp->amuledlg->m_chatwnd->IsIdValid(id)) {
                wxASSERT( nStatus < 3 );
                m_nChatCaptchaState = CA_NONE;
                theApp->amuledlg->m_chatwnd->ShowCaptchaResult(id, nStatus == 0);
        } else {
                m_nChatCaptchaState = CA_NONE;
                AddDebugLogLineN(logClient, CFormat(wxT("Received captcha result from client, but not accepting it at this time (%s)")) % GetFullIP());
        }
}

void CUpDownClient::ProcessChatMessage(wxString message)
{
        if (IsMessageFiltered(message)) {
                AddLogLineC(CFormat(_("Message filtered from '%s' (IP:%s)")) % GetUserName() % GetFullIP());
                return;
        }

        // advanced spamfilter check
        if (thePrefs::IsChatCaptchaEnabled() && !IsFriend()) {
                // captcha checks outrank any further checks - if the captcha has been solved, we assume it's not spam
                // first check if we need to send a captcha request to this client
                if (GetMessagesSent() == 0 && GetMessagesReceived() == 0 && GetChatCaptchaState() != CA_CAPTCHASOLVED) {
                        // we have never sent a message to this client, and no message from him has ever passed our filters
                        if (GetChatCaptchaState() != CA_CHALLENGESENT) {
                                // we also aren't currently expecting a captcha response
                                if (m_fSupportsCaptcha) {
                                        // and he supports captcha, so send him one and store the message (without showing for now)
                                        if (m_cCaptchasSent < 3) {	// no more than 3 tries
                                                m_strCaptchaPendingMsg = message;
                                                wxMemoryOutputStream memstr;
                                                memstr.PutC(0); // no tags, for future use
                                                CCaptchaGenerator captcha(4);
                                                if (captcha.WriteCaptchaImage(memstr)) {
                                                        m_strCaptchaChallenge = captcha.GetCaptchaText();
                                                        m_nChatCaptchaState = CA_CHALLENGESENT;
                                                        m_cCaptchasSent++;
                                                        CMemFile fileAnswer((byte*) memstr.GetOutputStreamBuffer()->GetBufferStart(), memstr.GetLength());
                                                        CPacket* packet = new CPacket(fileAnswer, OP_EMULEPROT, OP_CHATCAPTCHAREQ);
                                                        theStats::AddUpOverheadOther(packet->GetPacketSize());
                                                        AddLogLineN(CFormat(wxT("sent Captcha %s (%d)")) % m_strCaptchaChallenge % packet->GetPacketSize());
                                                        SafeSendPacket(packet);
                                                } else {
                                                        wxFAIL;
                                                }
                                        }
                                } else {
                                        // client doesn't support captchas, but we require them, tell him that it's not going to work out
                                        // with an answer message (will not be shown and doesn't count as sent message)
                                        if (m_cCaptchasSent < 1) {	// don't send this notifier more than once
                                                m_cCaptchasSent++;
                                                // always sent in english
                                                SendChatMessage(wxT("In order to avoid spam messages, this user requires you to solve a captcha before you can send a message to him. However your client does not supports captchas, so you will not be able to chat with this user."));
                                                AddDebugLogLineN(logClient, CFormat(wxT("Received message from client not supporting captchas, filtered and sent notifier (%s)")) % GetClientFullInfo());
                                        } else {
                                                AddDebugLogLineN(logClient, CFormat(wxT("Received message from client not supporting captchas, filtered, didn't send notifier (%s)")) % GetClientFullInfo());
                                        }
                                }
                                return;
                        } else { // (GetChatCaptchaState() == CA_CHALLENGESENT)
                                // this message must be the answer to the captcha request we sent him, let's verify
                                wxASSERT( !m_strCaptchaChallenge.IsEmpty() );
                                if (m_strCaptchaChallenge.CmpNoCase(message.Trim().Right(std::min(message.Length(), m_strCaptchaChallenge.Length()))) == 0) {
                                        // allright
                                        AddDebugLogLineN(logClient, CFormat(wxT("Captcha solved, showing withheld message (%s)")) % GetClientFullInfo());
                                        m_nChatCaptchaState = CA_CAPTCHASOLVED; // this state isn't persitent, but the messagecounter will be used to determine later if the captcha has been solved
                                        // replace captchaanswer with withheld message and show it
                                        message = m_strCaptchaPendingMsg;
                                        m_cCaptchasSent = 0;
                                        m_strCaptchaChallenge.Clear();
                                        CPacket* packet = new CPacket(OP_CHATCAPTCHARES, 1, OP_EMULEPROT, false);
                                        byte statusResponse = 0; // status response
                                        packet->CopyToDataBuffer(0, &statusResponse, 1);
                                        theStats::AddUpOverheadOther(packet->GetPacketSize());
                                        SafeSendPacket(packet);
                                } else { // wrong, cleanup and ignore
                                        AddDebugLogLineN(logClient, CFormat(wxT("Captcha answer failed (%s). Key was '%s', answer was '%s'.")) % GetClientFullInfo()
						% m_strCaptchaChallenge
						% message.Trim().Right(std::min(message.Length(), m_strCaptchaChallenge.Length()))
					);
                                        m_nChatCaptchaState = CA_NONE;
                                        m_strCaptchaChallenge.Clear();
                                        m_strCaptchaPendingMsg.Clear();
                                        CPacket* packet = new CPacket(OP_CHATCAPTCHARES, 1, OP_EMULEPROT, false);
                                        byte statusResponse = (m_cCaptchasSent < 3) ? 1 : 2; // status response
                                        packet->CopyToDataBuffer(0, &statusResponse, 1);
                                        theStats::AddUpOverheadOther(packet->GetPacketSize());
                                        SafeSendPacket(packet);
                                        return; // nothing more todo
                                }
                        }
                }
        }

        if (thePrefs::IsAdvancedSpamfilterEnabled() && !IsFriend()) { // friends are never spammer... (but what if two spammers are friends :P )
                bool bIsSpam = false;
                if (m_fIsSpammer) {
                        bIsSpam = true;
                } else {
                        // first fixed criteria: If a client sends me an URL in his first message before I respond to him
                        // there is a 99,9% chance that it is some poor guy advising his leech mod, or selling you .. well you know :P
                        if (GetMessagesSent() == 0) {
                                static wxArrayString urlindicators(wxStringTokenize(wxT("http:|www.|.de |.net |.com |.org |.to |.tk |.cc |.fr |ftp:|ed2k:|https:|ftp.|.info|.biz|.uk|.eu|.es|.tv|.cn|.tw|.ws|.nu|.jp"), wxT("|")));
                                for (size_t pos = urlindicators.GetCount(); pos--;) {
                                        if (message.Find(urlindicators[pos]) != wxNOT_FOUND) {
                                                bIsSpam = true;
                                                break;
                                        }
                                }
                                // second fixed criteria: he sent me 4 or more messages and I didn't answer him once
                                if (GetMessagesReceived() > 3) {
                                        bIsSpam = true;
                                }
                        }
                }
                if (bIsSpam) {
                        AddDebugLogLineN(logClient, CFormat(wxT("'%s' has been marked as spammer")) % GetUserName());
                        SetSpammer(true);
                        theApp->amuledlg->m_chatwnd->EndSession(GUI_ID(GetIP(),GetUserPort()));
                        return;
                }
        }


        wxString logMsg = CFormat(_("New message from '%s' (IP:%s)")) % GetUserName() % GetFullIP();
        if(thePrefs::ShowMessagesInLog()) {
                logMsg += wxT(": ") + message;
        }
        AddLogLineC(logMsg);
        IncMessagesReceived();

        Notify_ChatProcessMsg(GetIP(), GetUserName() + wxT("|") + message);
}

bool CUpDownClient::IsMessageFiltered(const wxString& message)
{
        bool filtered = false;
        // If we're chatting to the guy, we don't want to filter!
        if (GetChatState() != MS_CHATTING) {
                if (thePrefs::MsgOnlyFriends() && !IsFriend()) {
                        filtered = true;
                } else if (thePrefs::MsgOnlySecure() && GetUserName().IsEmpty() ) {
                        filtered = true;
                } else if (thePrefs::MustFilterMessages()) {
                        filtered = thePrefs::IsMessageFiltered(message);
                }
        }
        if (filtered) {
                SetSpammer(true);
        }
        return filtered;
}

#endif

void CUpDownClient::SendSharedDirectories()
{
        // This list will contain all (unique) folders.
        PathList foldersToSend;

        // The shared folders
        const unsigned folderCount = theApp->glob_prefs->shareddir_list.size();
        for (unsigned i = 0; i < folderCount; ++i) {
                foldersToSend.push_back(theApp->glob_prefs->shareddir_list[i]);
        }

        // ... the categories folders ... (category 0 -> incoming)
        for (unsigned i = 0; i < theApp->glob_prefs->GetCatCount(); ++i) {
                foldersToSend.push_back(theApp->glob_prefs->GetCategory(i)->path);
        }

        // Strip duplicates
        foldersToSend.sort();
        foldersToSend.unique();

        // Build packet
        CMemFile tempfile(80);
        tempfile.WriteUInt32(foldersToSend.size());

        PathList::iterator it = foldersToSend.begin();
        for (; it != foldersToSend.end(); ++it) {
                // Note: the public shared name contains the 'raw' path, so we can recognize it again.
                //       the 'raw' path is determined using CPath::GetRaw()
                tempfile.WriteString( theApp->sharedfiles->GetPublicSharedDirName(*it), GetUnicodeSupport() );
        }

        // ... and the Magic thing from the eDonkey Hybrids...
        tempfile.WriteString(OP_INCOMPLETE_SHARED_FILES, GetUnicodeSupport());

        // Send the packet.
        CPacket* replypacket = new CPacket(tempfile, OP_EDONKEYPROT, OP_ASKSHAREDDIRSANS);
        theStats::AddUpOverheadOther(replypacket->GetPacketSize());
        AddDebugLogLineN( logLocalClient, wxT("Local Client: OP_ASKSHAREDDIRSANS to ") + GetFullIP() );
        SendPacket(replypacket, true, true);
}

void CUpDownClient::SendSharedFilesOfDirectory(const wxString& strReqDir)
{
        CKnownFilePtrList list;

        if (strReqDir == OP_INCOMPLETE_SHARED_FILES) {
                // get all shared files from download queue
                int iQueuedFiles = theApp->downloadqueue->GetFileCount();
                for (int i = 0; i < iQueuedFiles; i++) {
                        CPartFile* pFile = theApp->downloadqueue->GetFileByIndex(i);
                        if (pFile == NULL || pFile->GetStatus(true) != PS_READY) {
                                continue;
                        }
                        list.push_back(pFile);
                }
        } else {
                // get all shared files for the requested directory
                const CPath *sharedDir = theApp->sharedfiles->GetDirForPublicSharedDirName(strReqDir);
                if (sharedDir) {
                        theApp->sharedfiles->GetSharedFilesByDirectory(sharedDir->GetRaw(), list);
                } else {
                        AddLogLineC(CFormat(_("User %s (%x) requested sharedfiles-list for not existing directory '%s' -> Ignored")) % GetUserName() % GetUserIDHybrid().hashCode() % strReqDir);
                }
        }

        CMemFile tempfile(80);
        tempfile.WriteString(strReqDir, GetUnicodeSupport());
        tempfile.WriteUInt32(list.size());

        while (!list.empty()) {
                if (!list.front()->IsLargeFile() || SupportsLargeFiles()) {
                        list.front()->CreateOfferedFilePacket(&tempfile, NULL, this);
                }
                list.pop_front();
        }

        CPacket* replypacket = new CPacket(tempfile, OP_EDONKEYPROT, OP_ASKSHAREDFILESDIRANS);
        theStats::AddUpOverheadOther(replypacket->GetPacketSize());
        AddDebugLogLineN( logLocalClient, wxT("Local Client: OP_ASKSHAREDFILESDIRANS to ") + GetFullIP() );
        SendPacket(replypacket, true, true);
}

void CUpDownClient::SetConnectOptions(uint8_t options, bool encryption, bool callback)
{
        SetCryptLayerSupport((options & 0x01) != 0 && encryption);
        SetCryptLayerRequest((options & 0x02) != 0 && encryption);
        SetCryptLayerRequires((options & 0x04) != 0 && encryption);
        SetDirectUDPCallbackSupport((options & 0x08) != 0 && callback);
}


#ifdef DEBUG_ZOMBIE_CLIENTS
void CUpDownClient::Unlink(const wxString& from)
{
        std::multiset<wxString>::iterator it = m_linkedFrom.find(from);
        if (it != m_linkedFrom.end()) {
                m_linkedFrom.erase(it);
        }
        m_linked--;
        if (!m_linked) {
                if (m_linkedDebug) {
                        AddLogLineN(CFormat(wxT("Last reference to client %d %p unlinked, delete it.")) % ECID() % this);
                }
                delete this;
        }
}

#else

void CUpDownClient::Unlink()
{
        m_linked--;
        if (!m_linked) {
                delete this;
        }
}
#endif


// File_checked_for_headers