//this file is part of NeoMule
//Copyright (C)2006 David Xanatos ( Xanatos@Lycos.at / http://neomule.sourceforge.net )
//
//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., 675 Mass Ave, Cambridge, MA 02139, USA.

#include "StdAfx.h"
#include <share.h>
#include "DebugHelpers.h"
#include "emule.h"
#include "Voodoo.h"
#include "Preferences.h"
#include "Log.h"
#include "packets.h"
#include "downloadqueue.h"
#include "PartFile.h"
#include "LanCast.h"
#include "eMuleDlg.h"
#include "downloadlistctrl.h"
#include "sharedfilelist.h"
#include "knownfilelist.h"
#include "transferwnd.h"
#include "functions.h"
#include "OpCodes.h"
#include "NeoOpCodes.h"
#include "UpDownClient.h"
#include "Uploadqueue.h"
#include "ClientFileStatus.h" // NEO: SCFS - [SmartClientFileStatus]
// NEO: VOODOOs - [VoodooSearchForwarding]
#include "ServerConnect.h"
#include "SearchDlg.h"
#include "SearchResultsWnd.h"
#include "SearchParams.h"
#include "Statistics.h"
#include "Searchlist.h"
#include "Exceptions.h"
#include "Server.h"
#include "Kademlia/Kademlia/Kademlia.h"
#include "kademlia/kademlia/SearchManager.h"
#include "kademlia/kademlia/search.h"
// NEO: VOODOOs END
#include "BandwidthControl\DownloadBandwidthThrottler.h"
#include "BandwidthControl\UploadBandwidthThrottler.h"
#include "BandwidthControl\BandwidthControl.h"
#include "clientlist.h"
#ifdef ARGOS // NEO: NA - [NeoArgos] -- Xanatos -->
#include "Neo/Argos.h"
#endif // ARGOS // NEO: NA END <-- Xanatos -
#ifdef NATTUNNELING // NEO: UTCP - [UserModeTCP] -- Xanatos -->
#include "AsyncProxySocketLayer.h"
#include "Neo/AbstractSocket.h"
#endif //NATTUNNELING // NEO: UTCP END <-- Xanatos --

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif


//////////////////////
// CMasterDatas

#ifdef VOODOO // NEO: VOODOO - [UniversalPartfileInterface] -- Xanatos -->

#if !defined(LANCAST) || !defined(NEO_BC)
#error The UniversalPartfileInterface (VOODOO) requires the Neo Lancast Upload system from the Neo bandwidth control
#endif

/* David:
* The hierarchy model is a securinty feature to protect the master and ensure the protocol will be respected.
*/
IMPLEMENT_DYNAMIC(CHierarchyExceptio, CException)

FileID::FileID(CSafeMemFile &packet, bool b64){
	packet.ReadHash16(Hash);
	if(b64)
		Size = packet.ReadUInt64();
	else
		Size = packet.ReadUInt32();
}

bool WriteFileID(CSafeMemFile &packet, CKnownFile* file, bool b64)
{
	if(!b64 && file->IsLargeFile()) // unfortunatly this client does not supports our file size
		return false;

	packet.WriteHash16(file->GetFileHash());
	if(b64)
		packet.WriteUInt64((uint64)file->GetFileSize());
	else
		packet.WriteUInt32((uint32)(uint64)file->GetFileSize());
	return true;
}

void WriteFileID(CSafeMemFile &packet, FileID& file, bool b64)
{
	ASSERT(b64 || file.Size < OLD_MAX_EMULE_FILE_SIZE);
	packet.WriteHash16(file.Hash);
	if(b64)
		packet.WriteUInt64(file.Size);
	else
		packet.WriteUInt32((uint32)file.Size);
}

//////////////////////
// CMasterDatas

CMasterDatas::CMasterDatas()
{
	gaplist = NULL;
}

CMasterDatas::~CMasterDatas()
{
	if(gaplist){
		while (!gaplist->IsEmpty())
			delete gaplist->RemoveHead();
		delete gaplist;
	}
}

bool CMasterDatas::IsComplete(uint64 start, uint64 end) const
{
	ASSERT( start <= end );
	if(gaplist == NULL)
		return true;

	//if (end >= m_nFileSize)
	//	end = m_nFileSize-(uint64)1;
	for (POSITION pos = gaplist->GetHeadPosition();pos != 0;)
	{
		const Gap_Struct* cur_gap = gaplist->GetNext(pos);
		if (   (cur_gap->start >= start          && cur_gap->end   <= end)
			|| (cur_gap->start >= start          && cur_gap->start <= end)
			|| (cur_gap->end   <= end            && cur_gap->end   >= start)
			|| (start          >= cur_gap->start && end            <= cur_gap->end)
		   )
		{
			return false;	
		}
	}
	return true;
}


////////////////////////////////////////////////////////////////////////////////
// CVoodooSocket
//

#define STAT_REFRESH SEC2MS(1)

IMPLEMENT_DYNCREATE(CVoodooSocket, CEMSocket)

CVoodooSocket::CVoodooSocket()
{
#ifdef NATTUNNELING // NEO: UTCP - [UserModeTCP]
	((CTCPSocket*)m_Socket)->m_StreamCryptState = ECS_NONE; // Dissable obfuscation voodoo does not support it
#else
	m_StreamCryptState = ECS_NONE; // Dissable obfuscation voodoo does not support it
#endif //NATTUNNELING // NEO: UTCP END

	//ResetTimeOutTimer();
	theApp.voodoo->AddSocket(this);
	deletethis = false;
	deltimer = 0;
	reconect = false;
	clientIP = 0;
	clientPort = 0;
	socketPort = 0;

	m_uAction = VA_NONE;
	m_bIsMaster = false;
	m_bIsSlave = false;

	m_uVoodooVersion = NULL;
	m_u64FileSizeSupport = 0;
	m_uCorruptionHandling = 0;
	m_uAdvDownloadSync = 0;

	m_uSupportsStatistics = 0;
	m_uNeoSourceExport = 0; // NEO: VOODOOx - [VoodooSourceExchange]
	m_uNeoSourceExchange = 0; // NEO: VOODOOx - [VoodooSourceExchange]
	m_uExtendedStates = 0;
	m_uPriorityInstructions = 0;
	m_uVoodooSearch = 0; // NEO: VOODOOs - [VoodooSearchForwarding]
	m_uA4AFCommands = 0;
	m_uExtendedComands = 0; // NEO: VOODOOn - [VoodooExtensionForNeo]
	m_uNeoFilePreferences = 0; // NEO: VOODOOn - [VoodooExtensionForNeo]


	m_sName.Empty();
	m_uType = CT_UNKNOWN;
	m_nVoodoPort = 0;
	m_nPortED2K = 0;
	m_bUsable = false;
	m_sSpell.Empty();

	m_bHaveMoreResults = false; // NEO: VOODOOs - [VoodooSearchForwarding]

	// stats
	m_uLastStatUpdate = ::GetTickCount();
}

void CVoodooSocket::SetOnLan() 
{ 
	theApp.uploadBandwidthThrottler->RemoveFromAllQueues(this);
	theApp.downloadBandwidthThrottler->RemoveFromAllQueues(this);

	//if(!thePrefs.IsDirectLanUpload())
		theApp.uploadBandwidthThrottler->QueueForLanPacket(this);

	//if(!thePrefs.IsDirectLanDownload()){
		theApp.downloadBandwidthThrottler->QueueForLanPacket(this);
		theApp.downloadqueue->AddToProcessQueue(this);
	//}
}

void CVoodooSocket::ConfigSocket()
{
	if(thePrefs.IsSetLanUploadBuffer()){
		int sendBuff = thePrefs.GetLanUploadBufferSize();
#ifdef NATTUNNELING // NEO: UTCP - [UserModeTCP]
		m_Socket->SetSockOpt(SO_SNDBUF, &sendBuff, sizeof(sendBuff), SOL_SOCKET);
#else
		SetSockOpt(SO_SNDBUF, &sendBuff, sizeof(sendBuff), SOL_SOCKET);
#endif //NATTUNNELING // NEO: UTCP END
	}

	if(thePrefs.IsSetLanDownloadBuffer()){
		int recvBuff = thePrefs.GetLanDownloadBufferSize();
#ifdef NATTUNNELING // NEO: UTCP - [UserModeTCP]
		m_Socket->SetSockOpt(SO_RCVBUF, &recvBuff, sizeof(recvBuff), SOL_SOCKET);
#else
		SetSockOpt(SO_RCVBUF, &recvBuff, sizeof(recvBuff), SOL_SOCKET);
#endif //NATTUNNELING // NEO: UTCP END
	}

	if(thePrefs.IsTCPDisableNagle()){
		BOOL noDelay = true;
#ifdef NATTUNNELING // NEO: UTCP - [UserModeTCP]
		m_Socket->SetSockOpt(TCP_NODELAY, &noDelay, sizeof(noDelay), IPPROTO_TCP);
#else
		SetSockOpt(TCP_NODELAY, &noDelay, sizeof(noDelay), IPPROTO_TCP);
#endif //NATTUNNELING // NEO: UTCP END
	}
}

CVoodooSocket::~CVoodooSocket()
{
	// NEO: NLC - [NeoLanCast]
	theApp.uploadBandwidthThrottler->RemoveFromLanPacketQueue(this);
	theApp.downloadBandwidthThrottler->RemoveFromLanPacketQueue(this);
	theApp.downloadqueue->RemoveFromProcessQueue(this);
	// NEO: NLC END 
	theApp.voodoo->RemoveSocket(this);
}

void CVoodooSocket::SendPacket(CSafeMemFile &data, uint8 opcode, bool /*bControlPacket*/)
{
	if(data.GetLength() > 1536*1024){
		DebugLog(LOG_ERROR,_T("VOODOO: Tryed to send a to large packet packet dropped"));
		ASSERT(0);
		return; // Drop packet to avoid disconnect
	}
	// Note: All voodoo packets are control packets...
	Packet* packet = new Packet(&data,OP_VOODOOPROT);
	packet->opcode = opcode;
	CEMSocket::SendPacket(packet,true);
}

void CVoodooSocket::OnClose(int nErrorCode){
	ASSERT (theApp.voodoo->IsValidSocket(this));
	CEMSocket::OnClose(nErrorCode);

	LPCTSTR pszReason;
	CString* pstrReason = NULL;
	if (nErrorCode == 0)
		pszReason = _T("Close");
	else if (thePrefs.GetVerbose()){
		pstrReason = new CString;
		*pstrReason = GetErrorMessage(nErrorCode, 1);
		pszReason = *pstrReason;
	}
	else
		pszReason = NULL;
	Disconnect(pszReason);
	delete pstrReason;
}

void CVoodooSocket::OnError(int nErrorCode)
{
	CString strTCPError;
	if (thePrefs.GetVerbose())
	{
		if (nErrorCode == ERR_WRONGHEADER)
			strTCPError = _T("Error: Wrong header");
		else if (nErrorCode == ERR_TOOBIG)
			strTCPError = _T("Error: Too much data sent");
		else
			strTCPError = GetErrorMessage(nErrorCode);
		DebugLogWarning(_T("Client TCP socket: %s; %s"), strTCPError, DbgGetClientInfo());
	}
	Disconnect(strTCPError);
}

void CVoodooSocket::Disconnect(LPCTSTR pszReason){
	AsyncSelect(0);
	byConnected = ES_DISCONNECTED;

	AddDebugLogLine(DLP_VERYLOW, true,_T("Disconnecting voodoo client: %s; reason: %s"), DbgGetClientInfo(), pszReason);

	if(m_bIsMaster || m_bIsSlave){
		ModLog(LOG_WARNING,GetResString(IDS_X_VOODOO_CLIENT_CONENCT_LOST), 
			GetResString(IsMaster() ? IDS_X_VOODOO_MASTER : IsSlave() ? IDS_X_VOODOO_SLAVE : IDS_X_VOODOO_UNKNOWN), 
			clientAddress.IsEmpty() ? ipstr(clientIP) : clientAddress, clientPort);

		if(m_bIsMaster){
			CTypedPtrList<CPtrList, CPartFile*>* filelist = theApp.downloadqueue->GetFileList();
			for (POSITION pos = filelist->GetHeadPosition(); pos != 0; ){
				CPartFile* pFile = filelist->GetNext(pos);
				if(pFile->IsVoodooFile() && pFile->GetMasterDatas(this)){
					if(thePrefs.UseVirtualVoodooFiles() == TRUE)
						pFile->DeleteFile(false,false); // voodoo files are now completly virtual // Disable resort
					else
						pFile->RemoveMaster(this);
				}
			}

			CMap<CCKey,const CCKey&,CKnownFile*,CKnownFile*>* filemap = theApp.sharedfiles->GetFileMap();
			CKnownFile* kFile;
			CCKey bufKey;
			for (POSITION pos = filemap->GetStartPosition();pos != 0;){
				filemap->GetNextAssoc(pos,bufKey,kFile);
				if(kFile->IsVoodooFile() && kFile->GetMasterDatas(this))
					kFile->RemoveMaster(this);
			}
		}

		if(thePrefs.IsAutoConnectVoodoo())
			SetReconnect();

		m_bIsMaster = false;
		m_bIsSlave = false;
	}else if(m_uAction != VA_NONE)
		ModLog(LOG_WARNING,GetResString(IDS_X_VOODOO_CLIENT_CONENCT_FAILED), 
			GetClientDesc(), clientAddress.IsEmpty() ? ipstr(clientIP) : clientAddress, clientPort);

	theApp.voodoo->CleanUpKnown(this);

	Safe_Delete();
};

//bool CVoodooSocket::CheckTimeOut()
//{
//	if (::GetTickCount() - timeout_timer > MIN2MS(15)){ // very very long timeout for Voodoo sockets
//		ASSERT(0); // For Voodoo sockets this should not happen...
//		timeout_timer = ::GetTickCount();
//		CString str;
//		str.Format(_T("Timeout"));
//		Disconnect(str);
//		return true;
//	}
//	return false;
//}

void CVoodooSocket::Delete_Timed(){
	// it seems that MFC Sockets call socketfunctions after they are deleted, even if the socket is closed
	// and select(0) is set. So we need to wait some time to make sure this doesn't happens
	if (::GetTickCount() - deltimer > 10000)
		delete this;
}

void CVoodooSocket::Safe_Delete()
{
#ifdef NEO_DBT // NEO: NDBT - [NeoDownloadBandwidthThrottler]
	SetPriorityReceive(false);
#endif // NEO_DBT // NEO: NDBT END
#ifdef NEO_UBT // NEO: NUBT - [NeoUploadBandwidthThrottler]
	SetPrioritySend(false);
#endif // NEO_UBT // NEO: NUBT END
	ASSERT (theApp.voodoo->IsValidSocket(this));
	AsyncSelect(0);
	deltimer = ::GetTickCount();
#ifdef NATTUNNELING // NEO: UTCP - [UserModeTCP] -- Xanatos -->
	if (((CTCPSocket*)m_Socket)->m_SocketData.hSocket != INVALID_SOCKET)
		m_Socket->ShutDown(SD_BOTH);
#else
	if (m_SocketData.hSocket != INVALID_SOCKET) // deadlake PROXYSUPPORT - changed to AsyncSocketEx
		ShutDown(SD_BOTH);
#endif //NATTUNNELING // NEO: UTCP END <-- Xanatos --
	byConnected = ES_DISCONNECTED;
	deletethis = true;
}

void CVoodooSocket::Reconect()
{
	if(::GetTickCount() - deltimer < 3000) // wait 3 secunds bevoure retrying connection
		return;

	reconect = false;

	ModLog(GetResString(IDS_X_VOODOO_CLIENT_RECONNECT), 
			GetClientDesc(), clientAddress.IsEmpty() ? ipstr(clientIP) : clientAddress, clientPort);

	theApp.voodoo->ConnectVoodooClient(clientIP,clientPort,m_uAction,clientAddress);
}


void CVoodooSocket::OnSend(int nErrorCode)
{
	//ResetTimeOutTimer();
	CEMSocket::OnSend(nErrorCode);
}

void CVoodooSocket::OnReceive(int nErrorCode)
{
	//ResetTimeOutTimer();
	CEMSocket::OnReceive(nErrorCode);
}

void CVoodooSocket::OnConnect(int nErrorCode)
{
	CEMSocket::OnConnect(nErrorCode);
	if (nErrorCode)
	{
	    CString strTCPError;
		if (thePrefs.GetVerbose())
		{
		    strTCPError = GetErrorMessage(nErrorCode, 1);
		    if (nErrorCode != WSAECONNREFUSED && nErrorCode != WSAETIMEDOUT)
			    DebugLogError(_T("Client TCP socket (OnConnect): %s; %s"), strTCPError, DbgGetClientInfo());
		}
		Disconnect(strTCPError);
	}
	else
	{
		//This socket may have been delayed by SP2 protection, lets make sure it doesn't time out instantly.
		//ResetTimeOutTimer();

		ConfigSocket();

		SOCKADDR_IN sockAddr = {0};
		int nSockAddrLen = sizeof(sockAddr);
		GetPeerName((SOCKADDR*)&sockAddr, &nSockAddrLen);
		if (sockAddr.sin_addr.S_un.S_addr != 0){
			clientIP = sockAddr.sin_addr.S_un.S_addr;
			socketPort = ntohs(sockAddr.sin_port);
		}
	}
}



////////////////////////
// Connection begin

void CVoodooSocket::SendVoodooHello(uint8 uHello)
{
	CSafeMemFile data(128);
	data.WriteUInt8(VOODOOVERSION);

	data.WriteUInt8(uHello);

	if(uHello != VH_QUERY){
		uint32 uTagCount = 0;
		ULONG uTagCountFilePos = (ULONG)data.GetPosition();
		data.WriteUInt32(uTagCount);
	
		CTag tagName(VT_NAME,thePrefs.GetUserNick());
		tagName.WriteTagToFile(&data);
		uTagCount++;

		CTag tagPort(VT_PORT,thePrefs.IsVoodooAllowed() ?  thePrefs.GetVoodooPort() : 0);
		tagPort.WriteTagToFile(&data);
		uTagCount++;

		CTag tagType(VT_TYPE,CT_ED2K);
		tagType.WriteTagToFile(&data);
		uTagCount++;

		CTag tagED2K(VT_ED2K,thePrefs.GetPort());
		tagED2K.WriteTagToFile(&data);
		uTagCount++;

		CTag tagUsable(VT_USABLE,thePrefs.IsSlaveAllowed());
		tagUsable.WriteTagToFile(&data);
		uTagCount++;

		const UINT u64FileSizeSupport	= (OLD_MAX_EMULE_FILE_SIZE < MAX_EMULE_FILE_SIZE) ? 1 : 0;
		const UINT uCorruptionHandling	= 1;
		const UINT uAdvDownloadSync		= 2;
		UINT uFeatures = 
			//(u						<< 8) |
			(uAdvDownloadSync			<< 5) |
			(uCorruptionHandling		<< 1) |
			(u64FileSizeSupport			<< 0) ;
		CTag tagAdvFeatures(VT_FEATURES,uFeatures);
		tagAdvFeatures.WriteTagToFile(&data);
		uTagCount++;

		const UINT uSupportsStatistics	= 2;
		const UINT uNeoSourceExport		= thePrefs.UseVoodooSourceExchange() != FALSE	? 2 : 0; // NEO: VOODOOx - [VoodooSourceExchange] // V2 bidirectional
		const UINT uNeoSourceExchange	= thePrefs.UseVoodooSourceExchange() == TRUE	? 1 : 0; // NEO: VOODOOx - [VoodooSourceExchange]
		const UINT uExtendedStates		= 1;
		const UINT uPriorityInstructions= 3;
		const UINT uVoodooSearch		= thePrefs.UseVoodooSearch()					? 1 : 0; // NEO: VOODOOs - [VoodooSearchForwarding]
		const UINT uA4AFCommands		= 1;
		const UINT uExtendedComands		= thePrefs.IsVoodooNeoCommands()				? 7 : 0; // NEO: VOODOOn - [VoodooExtensionForNeo]
		const UINT uNeoFilePreferences	= thePrefs.IsVoodooNeoPreferences()				? 1 : 0; // NEO: VOODOOn - [VoodooExtensionForNeo]
		UINT uFeaturesEx = 
			//(u						<< 24) |
			(uNeoFilePreferences		<< 20) | // NEO: VOODOOn - [VoodooExtensionForNeo]
			(uExtendedComands			<< 16) | // NEO: VOODOOn - [VoodooExtensionForNeo]
			(uA4AFCommands				<< 14) |
			(uVoodooSearch				<< 13) | // NEO: VOODOOs - [VoodooSearchForwarding]
			(uPriorityInstructions		<< 11) |
			(uExtendedStates			<< 10) |
			(uNeoSourceExchange			<<  6) | // NEO: VOODOOx - [VoodooSourceExchange]
			(uNeoSourceExport			<<  2) | // NEO: VOODOOx - [VoodooSourceExchange]
			(uSupportsStatistics		<<  0) ;
		CTag tagFeatures(VT_FEATURES_EX,uFeaturesEx);
		tagFeatures.WriteTagToFile(&data);
		uTagCount++;


		data.Seek(uTagCountFilePos, CFile::begin);
		data.WriteUInt32(uTagCount);
		data.Seek(0, CFile::end);
	}

	SendPacket(data,OP_VOODOO_HELLO);
}


void CVoodooSocket::RecivedVoodooHello(CSafeMemFile &packet)
{
	m_uVoodooVersion = packet.ReadUInt8();
	
	uint8 uHello = packet.ReadUInt8();
	if(uHello == VH_QUERY){
		SendVoodooHello(VH_ANSWER);
		return;
	}

	uint32 tagcount = packet.ReadUInt32();
	for (uint32 i = 0;i < tagcount; i++)
	{
		CTag temptag(&packet, true);
		switch (temptag.GetNameID())
		{
			case VT_NAME:
				ASSERT (temptag.IsStr());
				m_sName = temptag.GetStr();
				break;

			case VT_TYPE:
				ASSERT (temptag.IsInt());
				m_uType = (uint8)temptag.GetInt();
				break;

			case VT_PORT:
				ASSERT (temptag.IsInt());
				m_nVoodoPort = (uint16)temptag.GetInt();
				break;

			case VT_ED2K:
				ASSERT (temptag.IsInt());
				m_nPortED2K = (uint16)temptag.GetInt();
				break;

			case VT_USABLE:
				ASSERT (temptag.IsInt());
				m_bUsable = I2B(temptag.GetInt());
				break;

			case VT_FEATURES:{
				ASSERT (temptag.IsInt());
				UINT uFeatures = temptag.GetInt();
				//m_u					= (uFeatures >> 8);
				m_uAdvDownloadSync		= (uFeatures >> 5);
				m_uCorruptionHandling	= (uFeatures >> 1);
				m_u64FileSizeSupport	= (uFeatures >> 0);
				break;
			}

			case VT_FEATURES_EX:{
				ASSERT (temptag.IsInt());
				UINT uFeaturesEx = temptag.GetInt();
				//m_u					= (uFeaturesEx >> 24);
				m_uNeoFilePreferences	= (uFeaturesEx >> 20); // NEO: VOODOOn - [VoodooExtensionForNeo]
				m_uExtendedComands		= (uFeaturesEx >> 16); // NEO: VOODOOn - [VoodooExtensionForNeo]
				m_uA4AFCommands			= (uFeaturesEx >> 14);
				m_uVoodooSearch			= (uFeaturesEx >> 13); // NEO: VOODOOs - [VoodooSearchForwarding]
				m_uPriorityInstructions	= (uFeaturesEx >> 11);
				m_uExtendedStates		= (uFeaturesEx >> 10);
				m_uNeoSourceExchange	= (uFeaturesEx >> 6); // NEO: VOODOOx - [VoodooSourceExchange]
				m_uNeoSourceExport		= (uFeaturesEx >> 2); // NEO: VOODOOx - [VoodooSourceExchange]
				m_uSupportsStatistics	= (uFeaturesEx >> 0);
				break;
			}

			default:
				DebugLogWarning(_T("Unknown Tag Voodoo Hello Packet: %s"), temptag.GetFullInfo());
		}
	}

	if(uHello == VH_HELLO)
		SendVoodooHello(VH_ANSWER);

	ConnectionEstablished();
}

void CVoodooSocket::ConnectionEstablished()
{
	SetPort(m_nVoodoPort);
	ModLog(GetResString(IDS_X_VOODOO_CLIENT_CONNECTED), clientAddress.IsEmpty() ? ipstr(clientIP) : clientAddress, clientPort);

	//if(m_uVoodooVersion <= VOODOOVERSION_OLD){
	//	ModLog(LOG_WARNING,GetResString(IDS_X_VOODOO_TO_OLD),DbgGetClientInfo());
	//	Disconnect(_T("To old version"));
	//	return;
	//}

	if(!theApp.voodoo->AddKnown(this)){
		SendGoodBy();
		Disconnect(_T("Blocked clinet"));
	}else if(m_uAction == VA_QUERY)
		Disconnect(_T("Query finished"));
	else if(!IsUsable() && (m_uAction & VA_SLAVE))
		ModLog(GetResString(IDS_X_VOODOO_NOT_SLAVE),DbgGetClientInfo());
	else if(m_uAction & VA_PARTNER) // true for VA_SLAVE and VA_MASTER
		SendSpell();
}

/////////////////////////
// Spell

void CVoodooSocket::SendSpell()
{
	CSafeMemFile data(128);
	uint8 uAction = m_uAction;
	if(m_uVoodooVersion < 0x02){
		if(uAction == VA_QUERY)
			uAction = VA_PARTNER;
		else if(uAction == VA_PARTNER)
			uAction = VA_SLAVE;
	}
	data.WriteUInt8(uAction);
	data.WriteString(m_sSpell.IsEmpty() ? thePrefs.GetVoodooSpell() : m_sSpell,utf8strNone/*utf8strRaw*/);
	SendPacket(data,OP_SPELL);
}

void CVoodooSocket::SpellRecived(CSafeMemFile &packet)
{
	uint8 uAction = packet.ReadUInt8();
	CString sSpell = packet.ReadString(false/*true*/);
	bool bResult = (sSpell.Compare(thePrefs.GetVoodooSpell()) == 0);

	if(bResult){
		if(m_uVoodooVersion < 0x02){
			if(uAction == VA_PARTNER)
				uAction = VA_QUERY;
		}
		uint8 uResult = VA_NONE;
		if((uAction & VA_SLAVE) && thePrefs.IsSlaveAllowed()){
			m_bIsMaster = true;
			ModLog(GetResString(IDS_X_ACCEPT_SPELL),GetResString(IDS_X_VOODOO_MASTER),DbgGetClientInfo());
			uResult |= VA_SLAVE;
		}
		if((uAction & VA_MASTER) && thePrefs.IsSlaveHosting()){
			m_bIsSlave = true;
			ModLog(GetResString(IDS_X_ACCEPT_SPELL),GetResString(IDS_X_VOODOO_SLAVE),DbgGetClientInfo());
			uResult |= VA_MASTER;
		}
		SendSpellResult(uResult);
		if(IsSlave()){
			if(m_uVoodooVersion >= 0x02){
				SendDownloadQueueV2();
				SendSharedFilesListV2();
			}else{
				SendDownloadQueue();
				SendSharedFilesList();			
			}
		}
	}else
		SendSpellResult(VA_NONE);
}

void CVoodooSocket::SendSpellResult(uint8 uResult)
{
	CSafeMemFile data(128);
	data.WriteUInt8(uResult);
	SendPacket(data,OP_SPELL_RESULT);
}

void CVoodooSocket::SpellResultRecived(CSafeMemFile &packet)
{
	uint8 uResult = packet.ReadUInt8();
	if(uResult){
		ModLog(GetResString(IDS_X_SPELL_ACCEPTED), 
			(uResult == VA_PARTNER) ? GetResString(IDS_X_VOODOO_PARTNER) : 
			(uResult == VA_SLAVE) ? GetResString(IDS_X_VOODOO_SLAVE) : 
			(uResult == VA_MASTER) ? GetResString(IDS_X_VOODOO_MASTER) : 
			GetResString(IDS_X_VOODOO_UNKNOWN), DbgGetClientInfo());
		if(uResult & VA_SLAVE){
			m_bIsSlave = true;
			// the new sync metod assumes that the files are virtual (only sensefull with the same software)
			if(m_uVoodooVersion >= 0x02 && IsED2K()){ 
				SendDownloadQueueV2();
				SendSharedFilesListV2();
			}else{
				SendDownloadQueue();
				SendSharedFilesList();			
			}
		}
		if(uResult & VA_MASTER){
			m_bIsMaster = true;
		}
	}else{
		ModLog(LOG_ERROR,GetResString(IDS_X_SPELL_REJECTED),DbgGetClientInfo());
		if(m_uAction & VA_SLAVE)
			m_bIsSlave = false;
		if(m_uAction & VA_MASTER)
			m_bIsMaster = false;
	}
}

////////////////////////////////////////
// Disconnect

void CVoodooSocket::SendGoodBy()
{
	if(m_uVoodooVersion >= 0x02){
		CEMSocket::SendPacket(new Packet(OP_VOODOO_GOODBY,0,OP_VOODOOPROT));
		// we need to flash the buffered packet:
		if (!thePrefs.IsDirectSendingTCP() && !thePrefs.IsDirectLanUpload())
			CEMSocket::Send(0x7fffffff,MAXFRAGSIZE);
	}
}

///////////////////////////////////////////
// Snychronizing download queue

void CVoodooSocket::SendDownloadQueue()
{
	CSafeMemFile list(128);

	uint32 RecordsNumber = 0;
	ULONG RecordsNumberFilePos = (ULONG)list.GetPosition();
	list.WriteUInt32(RecordsNumber);

	CTypedPtrList<CPtrList, CPartFile*>* filelist = theApp.downloadqueue->GetFileList();
	for (POSITION pos = filelist->GetHeadPosition(); pos != 0; ){
		CPartFile* pFile = filelist->GetNext(pos);
		if(pFile->GetMasterDatas(this) || !pFile->KnownPrefs.UseVoodoo())
			continue;
		if(!WriteFileID(list,pFile,Use64Size()))
			continue;

		uint8 state;
		if(pFile->IsStopped())
			state = INST_STOP;
		else if(pFile->IsPaused())
			state = INST_PAUSE;
		else
			state = INST_RESUME;
		
		list.WriteUInt8(state);
		RecordsNumber++;
	}

	list.Seek(RecordsNumberFilePos, CFile::begin);
	list.WriteUInt32(RecordsNumber);
	list.Seek(0, CFile::end);

	SendPacket(list,OP_DOWNLOAD_QUEUE);
}

void CVoodooSocket::SynchronizeDownloadQueue(CSafeMemFile &packet)
{
	uint32 RecordsNumber = packet.ReadUInt32();
	for (uint32 i = 0; i < RecordsNumber; i++)
	{
		CPartFile* pFile = GetDownloadingFile(packet, true, false);
		uint8 state = packet.ReadUInt8();
		if(!pFile)
			continue;

		//pFile->AddMaster(this); // is already done by GetDownloadingFile
		RequestGapList(pFile);
		ExecuteDownloadInstruction(pFile, state);
	}
}

void CVoodooSocket::SendDownloadQueueV2()
{
	CTypedPtrList<CPtrList, CPartFile*>* filelist = theApp.downloadqueue->GetFileList();
	for (POSITION pos = filelist->GetHeadPosition(); pos != 0; ){
		CPartFile* pFile = filelist->GetNext(pos);
		if(pFile->GetMasterDatas(this) || !pFile->KnownPrefs.UseVoodoo())
			continue;

		uint8 state;
		if(pFile->IsStopped())
			state = INST_STOP;
		else if(pFile->IsPaused())
			state = INST_PAUSE;
		else
			state = INST_RESUME;

		SendDownloadInstruction(pFile,state);
	}
}

////////////////////////////////////
// Synchronize Files

void CVoodooSocket::SendFileUnavalible(FileID &fileID, uint8 uErr)
{
	CSafeMemFile data(128);
	WriteFileID(data,fileID,Use64Size());
	data.WriteUInt8(uErr);
	SendPacket(data,OP_FILE_UNAVALIBLE);
}

void CVoodooSocket::FileUnavalibleRecived(CSafeMemFile &packet)
{
	FileID fileID(packet,Use64Size());
	CPartFile* pFile = theApp.downloadqueue->GetFileByID(fileID.Hash);
	CKnownFile* kFile = pFile ? pFile : theApp.knownfiles->FindKnownFileByID(fileID.Hash);

	if(!kFile)
		throw StrLine(GetResString(IDS_X_VOODOO_FILE_FAILED),md4str(fileID.Hash),DbgGetClientInfo());
	else if(kFile->GetFileSize() != fileID.Size)
		throw StrLine(GetResString(IDS_X_VOODOO_FILE_SIZE_FAILED),kFile->GetFileName(),fileID.Size,kFile->GetFileSize(),DbgGetClientInfo());

	uint8 uErr = packet.ReadUInt8();
	switch(uErr){
		case RET_NONE:
		case RET_UNKNOWN:
			if(!IsSlave()) throw new CHierarchyExceptio(OP_FILE_UNAVALIBLE,RET_NONE);

			if(m_uType == CT_ED2K){
				//ModLog(GetResString(IDS_X_VOODOO_FILE_ORDER),DbgGetClientInfo(),kFile->GetFileName());
				if(pFile)
					SendDownloadOrder(pFile); 
				else
					SendShareOrder(kFile); 
				if(thePrefs.IsVoodooNeoPreferences()) // NEO: VOODOOn - [VoodooExtensionForNeo]
					SendFilePreferences(kFile);
			}else
				ModLog(GetResString(IDS_X_VOODOO_UNKNOWN_FILE),DbgGetClientInfo(),kFile->GetFileName());
			break;
		case RET_UNAVALIBLY:
			if(!IsMaster()) throw new CHierarchyExceptio(OP_FILE_UNAVALIBLE,RET_UNAVALIBLY);

			ModLog(GetResString(IDS_X_VOODOO_FILE_MISSING),kFile->GetFileName());
			if(kFile->IsVoodooFile()){
				if(pFile)
					pFile->DeleteFile(); // CleanUp, delete file
				else
					kFile->RemoveMaster(this); // Unshare file
			}else
				throw StrLine(GetResString(IDS_X_NOT_VOODOO_FILE),DbgGetClientInfo(),kFile->GetFileName()); // Shouldn't happen
			break;
		case RET_REAL:
			ModLog(GetResString(IDS_X_VOODOO_FILE_REAL_ERROR), kFile->GetFileName(),DbgGetClientInfo());
			break;
		case RET_NEW:
			ModLog(GetResString(IDS_X_VOODOO_FILE_NEW_ERROR), kFile->GetFileName(),DbgGetClientInfo());
			break;
		case RET_BADSIZE:
			ModLog(LOG_ERROR,GetResString(IDS_X_VOODOO_FILE_SIZE_REMOTE_FAILED), kFile->GetFileName(),DbgGetClientInfo());
			break;
		case RET_EXIST:
			ModLog(LOG_WARNING,GetResString(IDS_X_VOODOO_FILE_EXIST_FAILED), kFile->GetFileName(),DbgGetClientInfo());
			break;
		default:
			ModLog(GetResString(IDS_X_VOODOO_FILE_UNAVALIBLE_ERROR), kFile->GetFileName(),
				GetResString(IsMaster() ? IDS_X_VOODOO_MASTER : IsSlave() ? IDS_X_VOODOO_SLAVE : IDS_X_VOODOO_UNKNOWN), 
				DbgGetClientInfo());
	}
}

///////////////////////////////////////////
// Snychronizing shared file

void CVoodooSocket::SendSharedFilesList()
{
	CSafeMemFile list(128);

	uint32 RecordsNumber = 0;
	ULONG RecordsNumberFilePos = (ULONG)list.GetPosition();
	list.WriteUInt32(RecordsNumber);

	CMap<CCKey,const CCKey&,CKnownFile*,CKnownFile*>* filemap = theApp.sharedfiles->GetFileMap();
	CKnownFile* kFile;
	CCKey bufKey;
	for (POSITION pos = filemap->GetStartPosition();pos != 0;){
		filemap->GetNextAssoc(pos,bufKey,kFile);
		if(kFile->GetMasterDatas(this))
			continue;
		if(kFile->IsPartFile() || kFile->IsUnshared()) // NEO: USC - [UnShareCommand]
			continue;
		if(!WriteFileID(list,kFile,Use64Size()))
			continue;
		RecordsNumber++;
	}

	list.Seek(RecordsNumberFilePos, CFile::begin);
	list.WriteUInt32(RecordsNumber);
	list.Seek(0, CFile::end);

	SendPacket(list,OP_SHARED_FILE_LIST);
}

void CVoodooSocket::SynchronizeSharedFileList(CSafeMemFile &packet)
{
	uint32 RecordsNumber = packet.ReadUInt32();
	for (uint32 i = 0; i < RecordsNumber; i++)
	{
		CKnownFile* kFile = GetSharedFile(packet);
		if(!kFile)
			continue;
		kFile->AddMaster(this);
	}
}

void CVoodooSocket::SendSharedFilesListV2()
{

	CMap<CCKey,const CCKey&,CKnownFile*,CKnownFile*>* filemap = theApp.sharedfiles->GetFileMap();
	CKnownFile* kFile;
	CCKey bufKey;
	for (POSITION pos = filemap->GetStartPosition();pos != 0;){
		filemap->GetNextAssoc(pos,bufKey,kFile);
		if(kFile->GetMasterDatas(this))
			continue;
		if(kFile->IsPartFile() || kFile->IsUnshared()) // NEO: USC - [UnShareCommand]
			continue;
		
		SendShareInstruction(kFile,INST_SHARE);
	}
}

/////////////////////////
// New Download Order

void CVoodooSocket::SendDownloadOrder(CPartFile* pFile)
{
	ASSERT(m_uType == CT_ED2K); // Only ED2K compatible clients support this

	CSafeMemFile data(128);
	if(!WriteFileID(data,pFile,Use64Size()))
		return;
	pFile->WriteToTempFile(&data);
	SendPacket(data,OP_DOWNLOAD_ORDER);
}

void CVoodooSocket::DownloadOrderRecived(CSafeMemFile &packet)
{
	FileID fileID(packet,Use64Size());

	CPartFile* pFile = theApp.downloadqueue->GetFileByID(fileID.Hash);
	if(pFile){
		if(pFile->GetFileSize() != fileID.Size){
			SendFileUnavalible(fileID,RET_BADSIZE);
			ModLog(LOG_ERROR,GetResString(IDS_X_VOODOO_FILE_SIZE_FAILED),pFile->GetFileName(),fileID.Size,pFile->GetFileSize(),DbgGetClientInfo());
		}else if(pFile->IsVoodooFile()){
			if(pFile->GetMasterDatas(this) == NULL)
				pFile->AddMaster(this);
		}else{
			SendFileUnavalible(fileID, RET_REAL);
			ModLog(LOG_WARNING,GetResString(IDS_X_NOT_VOODOO_FILE),DbgGetClientInfo(),pFile->GetFileName());
		}
		return;
	}

	pFile = new CPartFile;

	pFile->SetVoodoo();

	if(thePrefs.UseVirtualVoodooFiles() == FALSE)
		pFile->CreatePartFile();

	try{
		if (pFile->LoadFromTempFile(&packet)){
			pFile->SetStatus(PS_PAUSED);

			theApp.downloadqueue->GetFileList()->AddTail(pFile);
			theApp.emuledlg->transferwnd->downloadlistctrl.AddFile(pFile); // show in downloadwindow

			pFile->AddMaster(this);

			RequestGapList(pFile); // I know we got the list in the file, but anyway

			// NEO: VOODOOx - [VoodooSourceExchange]
			if(thePrefs.UseVoodooSourceExchange() && GetNeoSourceExportVersion())
				RequestSourceList(pFile);
			// NEO: VOODOOx END
		}else{
			delete pFile;
			SendFileUnavalible(fileID, RET_NEW);
			m_LastErrFile = fileID;
			return;
		}
	}catch(CFileException* error){
		delete pFile;
		SendFileUnavalible(fileID, RET_NEW);
		m_LastErrFile = fileID;
		throw error;
	}
}

//////////////////////
// New Share Order

void CVoodooSocket::SendShareOrder(CKnownFile* kFile)
{
	ASSERT(m_uType == CT_ED2K); // Only ED2K compatible clients support this

	CSafeMemFile data(128);
	if(!WriteFileID(data,kFile,Use64Size()))
		return;
	kFile->WriteToFile(&data);
	SendPacket(data,OP_SHARE_ORDER);
}

void CVoodooSocket::ShareOrderRecived(CSafeMemFile &packet)
{
	FileID fileID(packet,Use64Size());

	CKnownFile* kFile = theApp.knownfiles->FindKnownFileByID(fileID.Hash);
	if(!kFile)
		kFile = theApp.downloadqueue->GetFileByID(fileID.Hash);

	if(kFile){
		if(kFile->GetFileSize() != fileID.Size){
			SendFileUnavalible(fileID,RET_BADSIZE);
			ModLog(LOG_ERROR,GetResString(IDS_X_VOODOO_FILE_SIZE_FAILED),kFile->GetFileName(),fileID.Size,kFile->GetFileSize(),DbgGetClientInfo());
		}else if(!kFile->IsRealFile()){
			// We can add complete masters to an partfile becouse we sendt to masters only parts thay are missing, so no conflict will happen
			if(kFile->GetMasterDatas(this) == NULL)
				kFile->AddMaster(this);
		}else{
			if(kFile->IsPartFile())
				SendFileUnavalible(fileID, RET_REAL);
			ModLog(GetResString(IDS_X_NOT_VOODOO_FILE),DbgGetClientInfo(),kFile->GetFileName());
		}
		return;
	}

	kFile = new CKnownFile();
	try{
		if (kFile->LoadFromFile(&packet)){
			theApp.knownfiles->SafeAddKFile(kFile);
			kFile->AddMaster(this);
		}else{
			delete kFile;
			SendFileUnavalible(fileID, RET_NEW);
			m_LastErrFile = fileID;
			return;
		}
	}catch(CFileException* error){
		delete kFile;
		SendFileUnavalible(fileID, RET_NEW);
		m_LastErrFile = fileID;
		throw error;
	}
}

////////////////////////////////////
// Updating gap List

void CVoodooSocket::RequestGapList(CPartFile* pFile)
{
	CSafeMemFile data(128);
	if(!WriteFileID(data,pFile,Use64Size()))
		return;
	SendPacket(data,OP_GAP_LIST_REQUEST);
}

void CVoodooSocket::GapListRequestRecived(CSafeMemFile &packet)
{
	CPartFile* pFile = GetDownloadingFile(packet,false);
	if(!pFile)
		return;
	SendGapList(pFile);
}

void CVoodooSocket::SendGapList(CPartFile* pFile)
{
	CSafeMemFile data(128);
	if(!WriteFileID(data,pFile,Use64Size()))
		return;

	CTypedPtrList<CPtrList, Gap_Struct*>& gaplist = pFile->GetGapList();
	data.WriteUInt32(gaplist.GetCount());
	for (POSITION pos = gaplist.GetHeadPosition();pos != NULL;)
	{
		Gap_Struct* gap = gaplist.GetNext(pos);
		if(Use64Size()){
			data.WriteUInt64(gap->start);
			data.WriteUInt64(gap->end);
		}else{
			data.WriteUInt32((uint32)gap->start);
			data.WriteUInt32((uint32)gap->end);
		}
	}

	SendPacket(data,OP_GAP_LIST);
}

void CVoodooSocket::GapListRecived(CSafeMemFile &packet)
{
	CPartFile* pFile = GetDownloadingFile(packet);
	if(!pFile)
		return;

	CMasterDatas* Datas = pFile->GetMasterDatas(this);
	ASSERT(Datas);

	if(!Datas->gaplist)
		Datas->gaplist = new CTypedPtrList<CPtrList, Gap_Struct*>; // Individual Master gap list
	
	while (!Datas->gaplist->IsEmpty())
		delete Datas->gaplist->RemoveHead();

	UINT tagcount = packet.ReadUInt32();
	for (UINT j = 0; j < tagcount; j++)
	{
		Gap_Struct* gap = new Gap_Struct;
		if(Use64Size()){
			gap->start = packet.ReadUInt64();
			gap->end = packet.ReadUInt64();
		}else{
			gap->start = packet.ReadUInt32();
			gap->end = packet.ReadUInt32();
		}
		Datas->gaplist->AddTail(gap);
	}

	pFile->ReCombinateGapList();
}

/////////////////////////////
// Download Instructions

void CVoodooSocket::SendDownloadInstruction(CPartFile* pFile, uint8 uInstruction, uint32 Flag1, uint32 Flag2)
{
	CSafeMemFile data(128);
	if(!WriteFileID(data,pFile,Use64Size()))
		return;
	data.WriteUInt8(uInstruction);
	if(m_uVoodooVersion >= 0x02){
		data.WriteUInt32(Flag1);
		data.WriteUInt32(Flag2);
	}
	SendPacket(data,OP_DOWNLOAD_INSTRUCTION);
}

CPartFile* CVoodooSocket::GetDownloadingFile(CSafeMemFile &packet, bool bRequest, bool bLogUnknown)
{
	FileID fileID(packet,Use64Size());
	CPartFile* pFile = theApp.downloadqueue->GetFileByID(fileID.Hash);
	if(!pFile){
		if(fileID != m_LastErrFile)
			SendFileUnavalible(fileID, bRequest ? RET_UNKNOWN : RET_UNAVALIBLY);
		else if (thePrefs.GetVerbose())
			DebugLog(_T("Droped operation for Erroneous file %s, from client %s "),md4str(fileID.Hash),DbgGetClientInfo());
			
		if(bRequest && bLogUnknown)
			ModLog(LOG_WARNING,GetResString(IDS_X_VOODOO_FILE_FAILED_SYNC),md4str(fileID.Hash),DbgGetClientInfo());
		return NULL;
	}
	else if(pFile->GetFileSize() != fileID.Size){
		ModLog(LOG_ERROR,GetResString(IDS_X_VOODOO_FILE_SIZE_FAILED),pFile->GetFileName(),fileID.Size,pFile->GetFileSize(),DbgGetClientInfo());
		SendFileUnavalible(fileID,RET_BADSIZE);
		return NULL;
	}
	else if(IsMaster() && !pFile->IsVoodooFile()){
		ModLog(LOG_WARNING,GetResString(IDS_X_NOT_VOODOO_FILE),DbgGetClientInfo(),pFile->GetFileName());
		SendFileUnavalible(fileID, RET_REAL);
		return NULL;
	}

	// update master on the fly
	if(IsMaster()){
		if(pFile->GetMasterDatas(this) == NULL)
			pFile->AddMaster(this);
	}

	return pFile;
}

void CVoodooSocket::DownloadInstructionRecived(CSafeMemFile &packet)
{
	CPartFile* pFile = GetDownloadingFile(packet,true,false);
	if(!pFile)
		return;

	uint8 uInstruction = packet.ReadUInt8();
	uint32 Flag1 = NULL;
	uint32 Flag2 = NULL;
	if(m_uVoodooVersion >= 0x02){
		Flag1 = packet.ReadUInt32();
		Flag2 = packet.ReadUInt32();
	}
	ExecuteDownloadInstruction(pFile, uInstruction, Flag1, Flag2);
}

void CVoodooSocket::ExecuteDownloadInstruction(CPartFile* pFile, uint8 uInstruction, uint32 Flag1, uint32 Flag2)
{
	switch(uInstruction){
		case INST_RESUME:{
			RequestGapList(pFile);
			// NEO: VOODOOx - [VoodooSourceExchange]
			if(thePrefs.UseVoodooSourceExchange() && GetNeoSourceExportVersion() && pFile->IsStopped())
				RequestSourceList(pFile);
			// NEO: VOODOOx END
			UINT uState = pFile->GetStatus();
			if(pFile->IsPaused() || pFile->IsStopped() 
			 || (uState != PS_READY && uState != PS_EMPTY))
				pFile->ResumeFile();
			break;
			}
		case INST_PAUSE:
			if(!pFile->IsPaused() && !pFile->IsStopped())
				pFile->PauseFile();
			break;
		case INST_STOP:
			/*if(thePrefs.UseVirtualVoodooFiles() == TRUE) // X-ToDo: yes/no?
				pFile->DeleteFile(); // voodoo files are now completly virtual 
			else*/ if(!pFile->IsStopped())
				pFile->StopFile();
			break;
		case INST_DELETE:
			pFile->DeleteFile();
			break;
		case INST_COMPLETE:
			pFile->VoodooComplete(true);
			break;

		case INST_SUSPEND:
			pFile->SetSuspend(I2B(Flag1));
			break;
		case INST_STANDBY:
			pFile->SetStandBy(I2B(Flag1));
			break;
		case INST_FORCE:
			pFile->SetForced(I2B(Flag1));
			break;

		case INST_DL_PRIO:
			if(Flag1 == PR_AUTO){
				pFile->SetAutoDownPriority(true);
				pFile->SetDownPriority(PR_HIGH);
			}else{
				pFile->SetAutoDownPriority(false);
				if (Flag1 != PR_LOW && Flag1 != PR_NORMAL && Flag1 != PR_HIGH)
					pFile->SetDownPriority(PR_NORMAL);
				else
					pFile->SetDownPriority((uint8)Flag1);
			}
			break;

#ifdef A4AF_CATS // NEO: MAC - [MorphA4AFCategories]
		case INST_FORCE_ALL_A4AF:
			if(Flag1)
				theApp.downloadqueue->forcea4af_file = pFile;
			else
				theApp.downloadqueue->forcea4af_file = NULL;
			break;
#endif // A4AF_CATS // NEO: MAC END
		case INST_GET_ALL_A4AF:
			pFile->CollectAllA4AF();
			break;
		case INST_DROP_ALL_A4AF:
			pFile->ReleaseAllA4AF();
			break;

		default:
			DebugLogError(_T("Unknown Download Instruction Recived: %u; %u/%u"),(uint32)uInstruction,Flag1,Flag2);
	}
}

/////////////////////
// Share instructions

void CVoodooSocket::SendShareInstruction(CKnownFile* kFile, uint8 uInstruction, uint32 Flag1, uint32 Flag2)
{
	CSafeMemFile data(128);
	if(!WriteFileID(data,kFile,Use64Size()))
		return;
	data.WriteUInt8(uInstruction);
	if(m_uVoodooVersion >= 0x02){
		data.WriteUInt32(Flag1);
		data.WriteUInt32(Flag2);
	}
	SendPacket(data,OP_SHARE_INSTRUCTION);
}

CKnownFile*	CVoodooSocket::GetSharedFile(CSafeMemFile &packet)
{
	FileID fileID(packet,Use64Size());
	CKnownFile* kFile = theApp.knownfiles->FindKnownFileByID(fileID.Hash);
	if(!kFile)
		kFile = theApp.downloadqueue->GetFileByID(fileID.Hash);
	if(!kFile){
		if(fileID != m_LastErrFile)
			SendFileUnavalible(fileID,RET_UNKNOWN);
		else if (thePrefs.GetVerbose())
			DebugLog(_T("Droped operation for Erroneous file %s, from client %s "),md4str(fileID.Hash),DbgGetClientInfo());

		//if(bLogUnknown)
		//	ModLog(LOG_WARNING, GetResString(IDS_X_VOODOO_FILE_FAILED_ERR),md4str(fileID.Hash),DbgGetClientInfo());
		return NULL;
	}
	else if(kFile->GetFileSize() != fileID.Size){
		SendFileUnavalible(fileID,RET_BADSIZE);
		ModLog(LOG_ERROR,GetResString(IDS_X_VOODOO_FILE_SIZE_FAILED),kFile->GetFileName(),fileID.Size,kFile->GetFileSize(),DbgGetClientInfo());
		return NULL;
	}
	else if(kFile->IsRealFile()){
		ModLog(LOG_WARNING,GetResString(IDS_X_NOT_VOODOO_FILE),DbgGetClientInfo(),kFile->GetFileName());
		//SendFileUnavalible(fileID, RET_REAL); // for shared files it is no problem
		return NULL;
	}
	/*else if(kFile->IsPartFile()){ // For now allowed for ul priority setting
		ModLog(LOG_WARNING,GetResString(IDS_X_NOT_VOODOO_KNOWN_FILE),DbgGetClientInfo(),kFile->GetFileName());
		return NULL;
	}*/
	return kFile;
}

void CVoodooSocket::ShareInstructionRecived(CSafeMemFile &packet)
{
	CKnownFile* kFile = GetSharedFile(packet);
	if(!kFile)
		return;

	uint8 uInstruction = packet.ReadUInt8();
	uint32 Flag1 = NULL;
	uint32 Flag2 = NULL;
	if(m_uVoodooVersion >= 0x02){
		Flag1 = packet.ReadUInt32();
		Flag2 = packet.ReadUInt32();
	}
	switch(uInstruction){
		case INST_SHARE:
			if(!kFile->IsPartFile()){
				if(kFile->GetMasterDatas(this) == NULL)
					kFile->AddMaster(this);
			}else
				ModLog(LOG_WARNING,GetResString(IDS_X_NOT_VOODOO_KNOWN_FILE),DbgGetClientInfo(),kFile->GetFileName());
			break;
		case INST_UNSHARE:
			if(!kFile->IsPartFile())
				kFile->RemoveMaster(this);
			else
				ModLog(LOG_WARNING,GetResString(IDS_X_NOT_VOODOO_KNOWN_FILE),DbgGetClientInfo(),kFile->GetFileName());
			break;
		case INST_UL_PRIO:
			if(Flag1 == PR_RELEASE){
				kFile->SetReleasePriority(I2B(Flag2));
			}else if(Flag1 == PR_AUTO){
				kFile->SetAutoUpPriority(true);
				kFile->UpdateAutoUpPriority();
			}else{
				kFile->SetAutoUpPriority(false);
				if (Flag1 != PR_VERYLOW && Flag1 != PR_LOW && Flag1 != PR_NORMAL && Flag1 != PR_HIGH && Flag1 != PR_VERYHIGH)
					kFile->SetUpPriority(PR_NORMAL);
				else
					kFile->SetUpPriority((uint8)Flag1);
			}
			break;
		default:
			DebugLogError(_T("Unknown Share Instruction Recived %u"),uInstruction);
	}
}

///////////////////////////
// Packet Reciver

bool CVoodooSocket::PacketReceived(Packet* packet)
{
	bool bResult = true;
	switch (packet->prot){
		case OP_VOODOOPROT:
			bResult = ProcessVoodooPacket((const BYTE*)packet->pBuffer, packet->size, packet->opcode);
			break;
		default:{
			if (thePrefs.GetVerbose())
				DebugLogError(_T("Failed to decompress client TCP packet; %s; %s"), DbgGetClientTCPPacket(packet->prot, packet->opcode, packet->size), DbgGetClientInfo());
			//if(!m_bIsMaster && !m_bIsSlave){ // Don't disconnect a connected vooodo socket on error
				Disconnect(_T("Unknown protocol"));
				bResult = false;
			//}
		}
	}
	return bResult;
}


bool CVoodooSocket::ProcessVoodooPacket(const BYTE* packet, uint32 size, UINT opcode)
{
	try
	{
		try
		{
			if ((!IsMaster() && (
			   opcode == OP_DOWNLOAD_QUEUE
			|| opcode == OP_SHARED_FILE_LIST
			|| opcode == OP_DOWNLOAD_ORDER 
			|| opcode == OP_SHARE_ORDER 
			|| opcode == OP_GAP_LIST 
			|| opcode == OP_DOWNLOAD_INSTRUCTION 
			|| opcode == OP_SHARE_INSTRUCTION 
			// NEO: VOODOOn - [VoodooExtensionForNeo]
			|| opcode == OP_DOWNLOAD_COMMAND
			|| opcode == OP_FILE_PREFERENCES
			// NEO: VOODOOn END
			|| opcode == OP_CORRUPT_SENDER
			|| opcode == OP_UPLOAD_DATA
			// NEO: VOODOOs - [VoodooSearchForwarding]
			|| opcode == OP_SEARCH_REQUEST
			|| opcode == OP_SEARCH_COMMAND
			// NEO: VOODOOs END
			)) ||
				(!IsSlave() && (
			   opcode == OP_GAP_LIST_REQUEST 
			|| opcode == OP_DOWNLOAD_DATA 
			|| opcode == OP_DATA_REQUEST
			|| opcode == OP_STATISTICS
			|| opcode == OP_FILE_STATISTICS
			// NEO: VOODOOs - [VoodooSearchForwarding]
			|| opcode == OP_SEARCH_STATE
			|| opcode == OP_SEARCH_RESULT
			// NEO: VOODOOs END
			)) ||
				(!IsMaster() && !IsSlave() && (
			   opcode == OP_FILE_UNAVALIBLE
			|| opcode == OP_THROTTLE_CHUNK
			//|| opcode == OP_DOWNLOAD_UNAVALIBLE // old
			|| opcode == OP_SOURCE_LIST_REQUEST
			|| opcode == OP_SOURCE_LIST
			|| opcode == OP_SINGLE_SOURCE
			))
			)
				throw new CHierarchyExceptio((uint8)opcode);

			CSafeMemFile data(packet, size);
			switch(opcode)
			{
				case OP_VOODOO_HELLO:{
					RecivedVoodooHello(data);
					break;
				}
				case OP_SPELL:{
					SpellRecived(data);
					break;
				}
				case OP_SPELL_RESULT:{
					SpellResultRecived(data);
					break;
				}
				case OP_VOODOO_GOODBY:{
					ModLog(GetResString(IDS_X_VOODOO_GOODBY),DbgGetClientInfo());
					SetAction(VA_NONE);
					break;
				}
				case OP_DOWNLOAD_QUEUE:{
					SynchronizeDownloadQueue(data);
					break;
				}
				//case OP_DOWNLOAD_UNAVALIBLE:
				case OP_FILE_UNAVALIBLE:{
					FileUnavalibleRecived(data);
					break;
				}
				case OP_NEW_DOWNLOAD_ORDER:{
					ModLog(LOG_WARNING,GetResString(IDS_X_VOODOO_OLD_OPCODE),DbgGetClientInfo(),_T("OP_NEW_DOWNLOAD_ORDER"));
					break;
				}
				case OP_DOWNLOAD_ORDER:{
					DownloadOrderRecived(data);
					break;
				}
				case OP_SHARE_ORDER:{
					ShareOrderRecived(data);
					break;
				}
				case OP_SHARED_FILE_LIST:{
					SynchronizeSharedFileList(data);
					break;
				}
				case OP_GAP_LIST_REQUEST:{
					GapListRequestRecived(data);
					break;
				}
				case OP_GAP_LIST:{
					GapListRecived(data);
					break;
				}
				case OP_DOWNLOAD_INSTRUCTION:{
					DownloadInstructionRecived(data);
					break;
				}
				case OP_SHARE_INSTRUCTION:{
					ShareInstructionRecived(data);
					break;
				}
				// NEO: VOODOOn - [VoodooExtensionForNeo]
				case OP_DOWNLOAD_COMMAND:{
					DownloadCommandRecived(data);
					break;
				}
				case OP_FILE_PREFERENCES:{
					FilePreferencesRecived(data);
					break;
				}
				// NEO: VOODOOn END
				// Data DownLink
				case OP_DOWNLOAD_DATA:{
					RecivedFileData(data);
					break;
				}
				case OP_THROTTLE_CHUNK:{
					RecivedThrottleChunk(data);
					break;
				}

				// corruption handling
				case OP_CORRUPT_SENDER:{
					RecivedCorruptedSenderWarning(data);
					break;
				}

				// Data UpLink
				case OP_DATA_REQUEST:{
					FileDataRequestRecived(data);
					break;
				}
				case OP_UPLOAD_DATA:{
					FileDataAnswerRecived(data);
					break;
				}
				// stats
				case OP_STATISTICS:{
					StatisticsRecived(data);
					break;
				}
				case OP_FILE_STATISTICS:{
					FileStatisticsRecived(data);
					break;
				}
				// Source exchange
				case OP_SOURCE_LIST_REQUEST:{
					SourceListRequestRecived(data);
					break;
				}
				case OP_SOURCE_LIST:{
					SourceListRecived(data);
					break;
				}
				case OP_SINGLE_SOURCE:{
					SingleSourceRecived(data);
					break;
				}
				// NEO: VOODOOs - [VoodooSearchForwarding]
				// voodoo search forwarding
				case OP_SEARCH_REQUEST:{
					NewSearchRecived(data);
					break;
				}
				case OP_SEARCH_STATE:{
					SearchStateRecived(data);
					break;
				}
				case OP_SEARCH_COMMAND:{
					SearchCommandRecived(data);
					break;
				}
				case OP_SEARCH_RESULT:{
					SearchResultRecived(data);
					break;
				}
				// NEO: VOODOOs END

				default:
					PacketToDebugLogLine(_T("Voodoo"), packet, size, opcode);
			}
		}
		catch(CHierarchyExceptio* error)
		{
			uint8 uOpCode = error->m_uOpCode;
			uint8 uTagCode = error->m_uTagCode;
			error->Delete();
			ASSERT(0);
			throw StrLine(GetResString(IDS_X_VOODOO_HIERARCHY_VIOLATION),DbgGetClientInfo(), uOpCode, uTagCode, 
				GetResString(IsMaster() ? IDS_X_VOODOO_MASTER : IsSlave() ? IDS_X_VOODOO_SLAVE : IDS_X_VOODOO_UNKNOWN));
		}
		catch(CFileException* error)
		{
			error->Delete();
			throw GetResString(IDS_ERR_INVALIDPACKAGE);
		}
		catch(CMemoryException* error)
		{
			error->Delete();
			throw CString(_T("Memory exception"));
		}
	}
	catch(CString error)
	{
		if (thePrefs.GetVerbose() && !error.IsEmpty())
			DebugLogWarning(_T("VOODOO: %s - while processing voodoo packet: opcode=%s  size=%u; %s"), error, DbgGetMuleClientTCPOpcode(opcode), size, DbgGetClientInfo());
		if(!m_bIsMaster && !m_bIsSlave){ // Don't disconnect a connected vooodo socket on error
			Disconnect(_T("ProcessVoodooPacket error. ") + error);
			return false;
		}
	}
	return true;
}

//////////////////////////////////
// Data DownLink

void CVoodooSocket::SplitPart(CPartFile* pFile, PartFileBufferedData *item)
{
	uint64 lenData = (item->end - item->start + 1);
	uint64 start = item->start;
	uint32 lenPart;
	uint64 offSet = 0;
	while(lenData){
		PartFileBufferedData* newItem = new PartFileBufferedData;
		newItem->start = start;
		lenPart = (uint32)min(lenData,KB2B(512));
		newItem->end = lenPart + newItem->start - 1;
		start = newItem->end + 1;
		
		newItem->data = new BYTE[lenPart];
		memcpy(newItem->data, item->data + offSet, lenPart);
		lenData -= lenPart;
		offSet += lenPart;

		TransferFileData(pFile,newItem, 0); // packets to split arrives not from real sources but from part import *only*
	}
}

void CVoodooSocket::TransferFileData(CPartFile* pFile, PartFileBufferedData *item, DWORD dwIP)
{
	CSafeMemFile data(128);
	if(!WriteFileID(data,pFile,Use64Size()))
		return;

	if(GetCorruptionHandlingVersion())
		data.WriteUInt32(dwIP);

	if(Use64Size()){
		data.WriteUInt64(item->start);
		data.WriteUInt64(item->end);
	}else{
		data.WriteUInt32((uint32)item->start);
		data.WriteUInt32((uint32)item->end);
	}

	uint32 lenData = (uint32)(item->end - item->start + 1);
	data.Write(item->data, lenData);

	SendPacket(data,OP_DOWNLOAD_DATA,false);

	delete[] item->data;
	delete item;
}

void CVoodooSocket::RecivedFileData(CSafeMemFile &packet)
{
	CPartFile* pFile = GetDownloadingFile(packet,false);
	if(!pFile)
		return;
	
	DWORD dwIP = 0;
	if(GetCorruptionHandlingVersion())
		dwIP = packet.ReadUInt32();

	PartFileBufferedData item;
	if(Use64Size()){
		item.start = packet.ReadUInt64();
		item.end = packet.ReadUInt64();
	}else{
		item.start = packet.ReadUInt32();
		item.end = packet.ReadUInt32();
	}

	if (pFile->IsComplete(item.start, item.end, false))
	{
		DebugLog(LOG_ERROR,GetResString(IDS_X_VOODOO_GOT_COMPLETED_DATA),DbgGetClientInfo(), pFile->GetFileName());
		SendGapList(pFile);
		return;
	}

	uint32 lenData = (uint32)(item.end - item.start + 1);
	item.data = new BYTE[lenData];
	packet.Read(item.data, lenData);

	pFile->WriteToBuffer(lenData, item.data, item.start, item.end, NULL, dwIP);
	delete [] item.data;
}

// Download synchronisation
void CVoodooSocket::SendThrottleChunk(CPartFile* pFile, uint16 partNumber, bool bRelease)
{
	CSafeMemFile data(128);
	if(!WriteFileID(data,pFile,Use64Size()))
		return;
	data.WriteUInt16(partNumber);
	if(GetAdvDownloadSyncVersion() > 1)
		data.WriteUInt8(bRelease);
	SendPacket(data,OP_THROTTLE_CHUNK,true);
}

void CVoodooSocket::RecivedThrottleChunk(CSafeMemFile &packet)
{
	CPartFile* pFile = GetDownloadingFile(packet,IsMaster());
	if(!pFile)
		return;

	uint16 partNumber = packet.ReadUInt16();
	bool bRelease = false;
	if(GetAdvDownloadSyncVersion() > 1)
		bRelease = packet.ReadUInt8() != 0;

	if(bRelease)
		pFile->UnThrottleChunk(partNumber);
	else
		pFile->ThrottleChunk(partNumber);

	theApp.voodoo->ManifestThrottleChunk(pFile, partNumber, this, bRelease); // Manifest Throttle instruction to remainding slaves
}

// Corruption handling
void CVoodooSocket::SendCorruptedSenderWarning(DWORD dwIP)
{
	CSafeMemFile data(128);
	data.WriteUInt32(dwIP);
	SendPacket(data,OP_CORRUPT_SENDER,true);
}

void CVoodooSocket::RecivedCorruptedSenderWarning(CSafeMemFile &packet)
{
	DWORD dwIP = packet.ReadUInt32();

	CUpDownClient* pEvilClient = theApp.clientlist->FindClientByIP(dwIP);
	if (pEvilClient != NULL){
		AddDebugLogLine(DLP_HIGH, false, _T("CorruptionBlackBox: Banning: Found client which send corrupted data, %s"), pEvilClient->DbgGetClientInfo());
		theApp.clientlist->AddTrackClient(pEvilClient);
#ifdef ARGOS // NEO: NA - [NeoArgos] -- Xanatos -->
		if(thePrefs.IsBadSenderDetection())
			if(thePrefs.UseArgosSystem())
				theApp.argos->DoArgos(pEvilClient->GetConnectIP(),AR_CORRUPTEDSENDER);
			else
#endif // ARGOS // NEO: NA END <-- Xanatos --
		pEvilClient->Ban(_T("Identified as sender of corrupt data"));
	}
	else{
		AddDebugLogLine(DLP_HIGH, false, _T("CorruptionBlackBox: Banning: Found client which send corrupted data, %s"), ipstr(dwIP));
#ifdef ARGOS // NEO: NA - [NeoArgos] -- Xanatos -->
		if(thePrefs.IsBadSenderDetection())
			if(thePrefs.UseArgosSystem())
				theApp.argos->DoArgos(dwIP,AR_CORRUPTEDSENDER);
			else
#endif // ARGOS // NEO: NA END <-- Xanatos --
		theApp.clientlist->AddBannedClient(dwIP);
	}
}


/////////////////////////
// Data UpLink

void CVoodooSocket::RequestFileData(CKnownFile* kFile, sDataRequest &DataRequest)
{
	CSafeMemFile data(128);
	if(!WriteFileID(data,kFile,Use64Size()))
		return;

	data.WriteUInt32(DataRequest.IdKey);
	if(Use64Size()){
		data.WriteUInt64(DataRequest.StartOffset);
		data.WriteUInt64(DataRequest.EndOffset);
	}else{
		data.WriteUInt32((uint32)DataRequest.StartOffset);
		data.WriteUInt32((uint32)DataRequest.EndOffset);
	}
	data.WriteUInt32(DataRequest.Client);

	SendPacket(data,OP_DATA_REQUEST,false);
}

void CVoodooSocket::FileDataRequestRecived(CSafeMemFile &packet)
{
	FileID fileID(packet,Use64Size());

	CKnownFile* kFile = theApp.sharedfiles->GetFileByID(fileID.Hash);
	if(!kFile){
		DebugLog(LOG_ERROR,GetResString(IDS_X_VOODOO_FILE_FAILED_NO),md4str(fileID.Hash),DbgGetClientInfo());
		SendFileUnavalible(fileID,RET_UNAVALIBLY);
		return;
	}else if(kFile->GetFileSize() != fileID.Size){
		ModLog(LOG_ERROR,GetResString(IDS_X_VOODOO_FILE_SIZE_FAILED),kFile->GetFileName(),fileID.Size,kFile->GetFileSize(),DbgGetClientInfo());
		SendFileUnavalible(fileID,RET_BADSIZE);
		return;
	}

	sDataRequest* DataRequest = new sDataRequest;
	DataRequest->IdKey = packet.ReadUInt32();
	if(Use64Size()){
		DataRequest->StartOffset = packet.ReadUInt64();
		DataRequest->EndOffset = packet.ReadUInt64();
	}else{
		DataRequest->StartOffset = packet.ReadUInt32();
		DataRequest->EndOffset = packet.ReadUInt32();
	}
	DataRequest->ID = fileID;
	if(packet.GetLength() - packet.GetPosition() >= sizeof(uint32))
		DataRequest->Client = packet.ReadUInt32();
	else
		DataRequest->Client = NULL;
	
	if(kFile->IsVoodooFile()){
		delete DataRequest;
		SendFileUnavalible(fileID,RET_UNAVALIBLY); // X-ToDo: Voodoo Caskade, reas upload proxying buffer
	}else
		GetFileData(kFile, DataRequest);
}

void CVoodooSocket::GetFileData(CKnownFile* kFile, sDataRequest* DataRequest)
{
	CString fullname;
	//CFile file; // NEO: RBT - [ReadBlockThread]
	try{
		uint64 i64uTogo = 0;
		if (DataRequest->StartOffset > DataRequest->EndOffset){
			i64uTogo = DataRequest->EndOffset + (kFile->GetFileSize() - DataRequest->StartOffset);
		}
		else{
			i64uTogo = DataRequest->EndOffset - DataRequest->StartOffset;
			if (kFile->IsPartFile() && !((CPartFile*)kFile)->IsComplete(DataRequest->StartOffset,DataRequest->EndOffset-1, true))
			//if (kFile->IsPartFile() && !((CPartFile*)kFile)->IsRangeShareable(DataRequest->StartOffset,DataRequest->EndOffset-1))	// SLUGFILLER: SafeHash - final safety precaution // NEO: SSH - [SlugFillerSafeHash]
				throw GetResString(IDS_ERR_INCOMPLETEBLOCK);
		}

		DataRequest->togo = (uint32)i64uTogo;

		// file statistic
		kFile->statistic.AddTransferred(DataRequest->StartOffset, DataRequest->togo);

		kFile->SetReadBlockFromFile(DataRequest->StartOffset, DataRequest->togo, this, DataRequest, true); // NEO: RBT - [ReadBlockThread]
	}
	catch(CString error)
	{
		delete DataRequest;
		if (thePrefs.GetVerbose())
			DebugLogWarning(_T("Failed to create voodoo upload package for %s (%s) - %s"), DbgGetClientInfo(), kFile->GetFileName(), error);
	}
}

// NEO: RBT - [ReadBlockThread]
void CVoodooSocket::SendFileData(sDataRequest* DataRequest, byte* Data)
{
	if(Data == RBT_ERROR && thePrefs.GetVerbose()){ //An error occured
		CKnownFile* kFile = theApp.sharedfiles->GetFileByID(DataRequest->ID.Hash);
		DebugLogWarning(_T("Failed to create voodoo upload package for %s - %s"), DbgGetClientInfo(), kFile ? kFile->GetFileName() : _T("n/a"), GetResString(IDS_ERR_OPEN));
	}

	CSafeMemFile data(128);
	WriteFileID(data,DataRequest->ID,Use64Size());

	data.WriteUInt32(DataRequest->IdKey);
	if(Data != RBT_ERROR && Data != RBT_ACTIVE && Data != NULL){
		data.WriteUInt32(DataRequest->togo);
		data.Write(Data,DataRequest->togo);
		delete [] Data;
		Data = NULL;
	}else{
		data.WriteUInt32(0);
		data.WriteUInt32((uint32)Data);
	}
	data.WriteUInt32(DataRequest->Client);

	SendPacket(data,OP_UPLOAD_DATA,false);

	delete DataRequest;
}
// NEO: RBT END

void CVoodooSocket::FileDataAnswerRecived(CSafeMemFile &packet)
{
	FileID fileID(packet,Use64Size());

	CKnownFile* kFile = theApp.sharedfiles->GetFileByID(fileID.Hash);
	if(!kFile)
		throw StrLine(GetResString(IDS_X_VOODOO_FILE_FAILED),md4str(fileID.Hash),DbgGetClientInfo()); // Shouldn't happen
	else if(kFile->GetFileSize() != fileID.Size)
		throw StrLine(GetResString(IDS_X_VOODOO_FILE_SIZE_FAILED),kFile->GetFileName(),fileID.Size,kFile->GetFileSize(),DbgGetClientInfo());
	
	Requested_Block_Struct* request = (Requested_Block_Struct*)packet.ReadUInt32();
	uint32 size = packet.ReadUInt32();
	byte* Data = NULL;
	
	if(size != 0){
		Data = new byte[size+500];
		packet.Read(Data,size);
	}else
		Data = (byte*)packet.ReadUInt32();

	CUpDownClient* clinet = NULL;
	if(packet.GetLength() - packet.GetPosition() >= sizeof(uint32))
		clinet = (CUpDownClient*)packet.ReadUInt32();
	else
	{
		// for compatybility to older masters, it isn't pritty but nessesery
		CUpDownClient* cur_client;
		for (POSITION pos = theApp.uploadqueue->GetFirstFromUploadList();
			pos != 0;theApp.uploadqueue->GetNextFromUploadList(pos))
		{
			cur_client = theApp.uploadqueue->GetQueueClientAt(pos);
			if(cur_client->m_BlockRequests_queue.Find(request)){
				clinet = cur_client;
				break;
			}
		}
	}
	
	if (theApp.uploadqueue->IsDownloading(clinet) // could have been canceld
	 && clinet->m_BlockRequests_queue.Find(request)) // check is this block pointer still valid
		request->filedata = Data;
	else if (Data != RBT_ERROR && Data != RBT_ACTIVE && Data != NULL)
		delete [] Data;
}

/////////////////////////
// Statistics

void CVoodooSocket::SendStatistics()
{
	CSafeMemFile data(128);

	SVoodooStats Stats;
	CMap<CKnownFile*,CKnownFile*,SFileVoodooStats,SFileVoodooStats> FileStats; // geathet file specyfic stats

	CTypedPtrList<CPtrList, CPartFile*>* filelist = theApp.downloadqueue->GetFileList();
	POSITION pos1 = filelist->GetHeadPosition();
	while(pos1 != NULL){
		CPartFile* pFile = filelist->GetNext(pos1);
		if(pFile->IsVoodooFile() && pFile->GetMasterDatas(this)){
			if(uint32 uDownDatarate = pFile->GetDatarate())	{
				Stats.uDownDatarate += uDownDatarate;
				FileStats[pFile].uDownDatarate += uDownDatarate;
			}
		}
	}

	POSITION pos2 = theApp.uploadqueue->uploadinglist.GetHeadPosition();
	while(pos2 != NULL){
		CUpDownClient* cur_client = theApp.uploadqueue->uploadinglist.GetNext(pos2);
		CKnownFile* kFile = cur_client->GetUploadingFile();
		if(theApp.sharedfiles->IsFilePtrInList(kFile) && kFile->IsVoodooFile() && kFile->GetMasterDatas(this)){
			if(uint32 uUpDatarate = cur_client->GetDatarate()){
				Stats.uUpDatarate += uUpDatarate;
				FileStats[kFile].uUpDatarate += uUpDatarate;
			}
		}
	}

	uint32 uTagCount = 0;
	ULONG uTagCountFilePos = (ULONG)data.GetPosition();
	data.WriteUInt32(uTagCount);

	CTag tagDown(ST_DOWN_DATARATE,Stats.uDownDatarate);
	tagDown.WriteTagToFile(&data);
	uTagCount++;

	CTag tagUp(ST_UP_DATARATE,Stats.uUpDatarate);
	tagUp.WriteTagToFile(&data);
	uTagCount++;

	// X-ToDo: add connection amount, queue size, tota source count, hmm...

	data.Seek(uTagCountFilePos, CFile::begin);
	data.WriteUInt32(uTagCount);
	data.Seek(0, CFile::end);

	SendPacket(data,OP_STATISTICS);

	// send stats per file
	if(m_uSupportsStatistics > 1)
		SendFileStatistics(FileStats);
}

void CVoodooSocket::StatisticsRecived(CSafeMemFile &packet)
{
	m_Stats.uLastStatUpdate = ::GetTickCount();

	uint32 tagcount = packet.ReadUInt32();
	for (uint32 i = 0;i < tagcount; i++)
	{
		CTag temptag(&packet, true);
		switch (temptag.GetNameID())
		{
			case ST_DOWN_DATARATE:
				ASSERT (temptag.IsInt());
				m_Stats.uDownDatarate = temptag.GetInt();
				break;

			case ST_UP_DATARATE:
				ASSERT (temptag.IsInt());
				m_Stats.uUpDatarate = temptag.GetInt();
				break;

			default:
				DebugLogWarning(_T("Unknown Tag Voodoo Statistics Packet: %s"), temptag.GetFullInfo());
		}
	}
}

void CVoodooSocket::SendFileStatistics(CMap<CKnownFile*,CKnownFile*,SFileVoodooStats,SFileVoodooStats> &FileStats)
{
	SFileVoodooStats Stats;
	CKnownFile* File;
	for (POSITION pos = FileStats.GetStartPosition();pos != 0;){
		FileStats.GetNextAssoc(pos,File,Stats);

		CSafeMemFile data(128);

		if(!WriteFileID(data,File,Use64Size()))
			continue;

		uint32 uTagCount = 0;
		ULONG uTagCountFilePos = (ULONG)data.GetPosition();
		data.WriteUInt32(uTagCount);

		CTag tagDown(ST_DOWN_DATARATE,Stats.uDownDatarate);
		tagDown.WriteTagToFile(&data);
		uTagCount++;

		// not needed
		//CTag tagUp(ST_UP_DATARATE,Stats.uUpDatarate);
		//tagUp.WriteTagToFile(&data);
		//uTagCount++;

		// X-ToDo: add source count, hmm...

		data.Seek(uTagCountFilePos, CFile::begin);
		data.WriteUInt32(uTagCount);
		data.Seek(0, CFile::end);

		SendPacket(data,OP_FILE_STATISTICS);
	}
}

void CVoodooSocket::FileStatisticsRecived(CSafeMemFile &packet)
{
	FileID fileID(packet,Use64Size());

	CPartFile* pFile = theApp.downloadqueue->GetFileByID(fileID.Hash);
	CKnownFile* kFile = pFile ? pFile : theApp.knownfiles->FindKnownFileByID(fileID.Hash);

	if(!kFile){
		if(fileID != m_LastErrFile)
			SendFileUnavalible(fileID, RET_UNAVALIBLY);
		else if (thePrefs.GetVerbose())
			DebugLog(_T("Droped operation for Erroneous file %s, from client %s "),md4str(fileID.Hash),DbgGetClientInfo());
		return;
	}else if(kFile->GetFileSize() != fileID.Size){
		SendFileUnavalible(fileID,RET_BADSIZE);
		ModLog(LOG_ERROR,GetResString(IDS_X_VOODOO_FILE_SIZE_FAILED),kFile->GetFileName(),fileID.Size,kFile->GetFileSize(),DbgGetClientInfo());
		return;
	}

	SFileVoodooStats &Stats = m_FileStats[kFile];

	// reset the values
	Stats.uDownDatarate = 0;
	Stats.uUpDatarate = 0;
	Stats.uLastStatUpdate = ::GetTickCount();

	uint32 tagcount = packet.ReadUInt32();
	for (uint32 i = 0;i < tagcount; i++)
	{
		CTag temptag(&packet, true);
		switch (temptag.GetNameID())
		{
			case ST_DOWN_DATARATE:
				ASSERT (temptag.IsInt());
				Stats.uDownDatarate = temptag.GetInt();
				break;

			case ST_UP_DATARATE:
			//	ASSERT (temptag.IsInt());
			//	Stats.uUpDatarate = temptag.GetInt();
				break;

			default:
				DebugLog(_T("Unknown Tag Voodoo File Statistics Packet: %s"), temptag.GetFullInfo());
		}
	}
}

//NEO: VOODOOx - [VoodooSourceExchange]
/////////////////////////
// Source Exchange

void CVoodooSocket::RequestSourceList(CPartFile* pFile)
{
	CSafeMemFile data(128);
	if(!WriteFileID(data,pFile,Use64Size()))
		return;
	SendPacket(data,OP_SOURCE_LIST_REQUEST);
}

void CVoodooSocket::SourceListRequestRecived(CSafeMemFile &packet)
{
	if(!thePrefs.UseVoodooSourceExchange())
		return;

	CPartFile* pFile = GetDownloadingFile(packet,IsMaster());
	if(!pFile)
		return;
	SendSourceList(pFile);
}

void CVoodooSocket::SendSourceList(CPartFile* pFile)
{
	CSafeMemFile data(128);
	if(!WriteFileID(data,pFile,Use64Size()))
		return;

	data.WriteUInt8(SRCFILE_VERSION);
	pFile->SaveSources(&data,true,MB2B(1));

	SendPacket(data,OP_SOURCE_LIST);
}

void CVoodooSocket::SourceListRecived(CSafeMemFile &packet)
{
	if(!thePrefs.UseVoodooSourceExchange())
		return;

	CPartFile* pFile = GetDownloadingFile(packet,IsMaster());
	if(!pFile)
		return;

	if(packet.ReadUInt8() != SRCFILE_VERSION){
		ModLog(LOG_WARNING,GetResString(IDS_X_VOODOO_BAD_SOURCE_VERSION),DbgGetClientInfo());
	}else{
		pFile->LoadSources(&packet,true);

		if(pFile->PartPrefs.StoreSourcesFileStatus() == TRUE){
			pFile->UpdateSourceCount(); // NEO: FIX - [SourceCount]
			pFile->UpdatePartsInfo(true);
			pFile->UpdatePartsInfoEx(CFS_Incomplete); // NEO: ICS - [InteligentChunkSelection]
			pFile->UpdatePartsInfoEx(CFS_Hiden); // NEO: RPS - [RealPartStatus]
			pFile->UpdatePartsInfoEx(CFS_Blocked); // NEO: RPS - [RealPartStatus]
			pFile->UpdateAvailablePartsCount();
		}
	}
}

// single source exchange
void CVoodooSocket::SendSingleSource(CPartFile* pFile, CUpDownClient* sClient)
{
	CSafeMemFile data(128);
	if(!WriteFileID(data,pFile,Use64Size()))
		return;

	data.WriteUInt8(SRCFILE_VERSION);
	sClient->StoreToFile(&data);

	SendPacket(data,OP_SINGLE_SOURCE);
}

void CVoodooSocket::SingleSourceRecived(CSafeMemFile &packet)
{
	if(thePrefs.UseVoodooSourceExchange() != TRUE)
		return;

	CPartFile* pFile = GetDownloadingFile(packet,IsMaster());
	if(!pFile)
		return;

	if(packet.ReadUInt8() != SRCFILE_VERSION){
		ModLog(LOG_WARNING,GetResString(IDS_X_VOODOO_BAD_SOURCE_VERSION),DbgGetClientInfo());
	}else{
		CUpDownClient* sClient = new CUpDownClient();
		sClient->CreateFromFile(&packet);
		sClient->SetRequestFile(pFile);
		sClient->SetSourceFrom(SF_VOODOO);

		// filter own ip the source packet may comm from
		if(sClient->GetConnectIP() == theApp.GetPublicIP() 
		&& sClient->GetUserPort() == thePrefs.GetPort()){
			delete sClient;
			return;
		}

		theApp.downloadqueue->CheckAndAddSource(pFile,sClient);
	}
}
// NEO: VOODOOx END


// NEO: VOODOOs - [VoodooSearchForwarding]
//////////////////////////
// Search forwarding

void CVoodooSocket::SendNewSearch(SSearchParams* pParams)
{
	CSafeMemFile data(128);
	data.WriteUInt8((uint8)pParams->eType);
	data.WriteUInt32(pParams->dwSearchID);
	data.WriteString(pParams->strExpression);
	data.WriteString(pParams->strFileType);
	data.WriteString(pParams->strMinSize);
	if(Use64Size())
		data.WriteUInt64(pParams->ullMinSize);
	else
		data.WriteUInt32((uint32)pParams->ullMinSize);
	data.WriteString(pParams->strMaxSize);
	if(Use64Size())
		data.WriteUInt64(pParams->ullMaxSize);
	else
		data.WriteUInt32((uint32)pParams->ullMaxSize);
	data.WriteUInt32(pParams->uAvailability);
	data.WriteString(pParams->strExtension);
	data.WriteUInt32(pParams->uComplete);
	data.WriteString(pParams->strCodec);
	data.WriteUInt32(pParams->ulMinBitrate);
	data.WriteUInt32(pParams->ulMinLength);
	data.WriteString(pParams->strTitle);
	data.WriteString(pParams->strAlbum);
	data.WriteString(pParams->strArtist);
	data.WriteUInt8(pParams->bUnicode);
	SendPacket(data,OP_SEARCH_REQUEST);
}

extern LPCTSTR _aszInvKadKeywordChars;
void CVoodooSocket::NewSearchRecived(CSafeMemFile &packet)
{
	SSearchParams pParams;
	pParams.eType = (ESearchType)packet.ReadUInt8();
	pParams.dwSearchID = packet.ReadUInt32();
	pParams.strExpression = packet.ReadString(true);
	pParams.strFileType = packet.ReadString(true);
	pParams.strMinSize = packet.ReadString(true);
	if(Use64Size())
		pParams.ullMinSize = packet.ReadUInt64();
	else
		pParams.ullMinSize = packet.ReadUInt32();	
	pParams.strMaxSize = packet.ReadString(true);
	if(Use64Size())
		pParams.ullMaxSize = packet.ReadUInt64();
	else
		pParams.ullMaxSize = packet.ReadUInt32();
	pParams.uAvailability = packet.ReadUInt32();
	pParams.strExtension = packet.ReadString(true);
	pParams.uComplete = packet.ReadUInt32();
	pParams.strCodec = packet.ReadString(true);
	pParams.ulMinBitrate = packet.ReadUInt32();
	pParams.ulMinLength = packet.ReadUInt32();
	pParams.strTitle = packet.ReadString(true);
	pParams.strAlbum = packet.ReadString(true);
	pParams.strArtist = packet.ReadString(true);
	pParams.bUnicode = I2B(packet.ReadUInt8());

	if (pParams.eType == SearchTypeEd2kServer || pParams.eType == SearchTypeEd2kGlobal)
	{
		if (!theApp.IsWorkingAllowed(WRK_SEARCHING)){ // NEO: EGS - [ExtendetGlobalSearch]
			SendSearchState(pParams.dwSearchID, SS_OFFLINE);
			return;
		}

		bool bServerSupports64Bit = theApp.serverconnect->GetCurrentServer() != NULL
								&& (theApp.serverconnect->GetCurrentServer()->GetTCPFlags() & SRV_TCPFLG_LARGEFILES);
		bool bPacketUsing64Bit = false;
		CSafeMemFile data(100);
		if (!GetSearchPacket(&data, &pParams, bServerSupports64Bit, &bPacketUsing64Bit) || data.GetLength() == 0){
			SendSearchState(pParams.dwSearchID, SS_ERROR, SE_GENERIC);
			return;
		}

		m_bHaveMoreResults = false;
		theApp.emuledlg->searchwnd->m_pwndResults->CancelEd2kSearch();

		theApp.searchlist->SetCurED2KSearchID(NULL);

		Packet* packet = new Packet(&data);
		packet->opcode = OP_SEARCHREQUEST;
		// We Need a connection for TCP Ask
		{
			if (thePrefs.GetDebugServerTCPLevel() > 0)
				Debug(_T(">>> Sending OP__SearchRequest\n"));
			theStats.AddUpDataOverheadServer(packet->size);
			theApp.serverconnect->SendPacket(packet,false);
		}

		if (pParams.eType == SearchTypeEd2kGlobal && theApp.serverconnect->IsUDPSocketAvailable())
			theApp.emuledlg->searchwnd->m_pwndResults->StartGlobalSearch(packet,bPacketUsing64Bit);
		else
			delete packet;

		SSearchMaster Master;
		Master.MasterSocket = this;
		Master.MasterID = pParams.dwSearchID;
		Master.KillTimer = NULL;
		theApp.voodoo->m_SearchMasterMap.SetAt(NULL,Master);
	}
	else if(pParams.eType == SearchTypeKademlia)
	{
		if (!Kademlia::CKademlia::IsConnected()){ // NEO: EGS - [ExtendetGlobalSearch]
			SendSearchState(pParams.dwSearchID, SS_OFFLINE);
			return;
		}

		int iPos = 0;
		pParams.strKeyword = pParams.strExpression.Tokenize(_T(" "), iPos);
		pParams.strKeyword.Trim();
		if (pParams.strKeyword.IsEmpty() || pParams.strKeyword.FindOneOf(_aszInvKadKeywordChars) != -1){
			SendSearchState(pParams.dwSearchID, SS_ERROR, SE_INVALID);
			return;
		}

		CSafeMemFile data(100);
		if (!GetSearchPacket(&data, &pParams, true, NULL)/* || (!pParams.strBooleanExpr.IsEmpty() && data.GetLength() == 0)*/){
			SendSearchState(pParams.dwSearchID, SS_ERROR, SE_GENERIC);
			return;
		}

		LPBYTE pSearchTermsData = NULL;
		UINT uSearchTermsSize = (UINT)data.GetLength();
		if (uSearchTermsSize){
			pSearchTermsData = new BYTE[uSearchTermsSize];
			data.SeekToBegin();
			data.Read(pSearchTermsData, uSearchTermsSize);
		}

		Kademlia::CSearch* pSearch = NULL;
		try{
			pSearch = Kademlia::CSearchManager::PrepareFindKeywords(pParams.bUnicode, pParams.strKeyword, uSearchTermsSize, pSearchTermsData);
			delete pSearchTermsData;
			if (!pSearch){
				ASSERT(0);
				return;
			}
		}catch (CString strException){
			SendSearchState(pParams.dwSearchID, SS_ERROR, SE_KAD);
			return;
		}
		DWORD dwSlaveSearchID = pSearch->GetSearchID();

		SSearchMaster Master;
		Master.MasterSocket = this;
		Master.MasterID = pParams.dwSearchID;
		Master.KillTimer = NULL;
		theApp.voodoo->m_SearchMasterMap.SetAt(dwSlaveSearchID,Master);
	}
	else
		return; // unsupported search type
}

void CVoodooSocket::SendSearchState(DWORD dwSearchID, uint8 uState, uint32 uValue)
{
	CSafeMemFile data(128);
	data.WriteUInt32(dwSearchID);
	data.WriteUInt8(uState);
	data.WriteUInt32(uValue);
	SendPacket(data,OP_SEARCH_STATE);
}

void CVoodooSocket::SearchStateRecived(CSafeMemFile &packet)
{
	DWORD dwSearchID = packet.ReadUInt32();
	uint8 uState = packet.ReadUInt8();
	uint32 uValue = packet.ReadUInt32();

	switch(uState){
		case SS_ERROR:
			switch(uValue)
			{
				case SE_GENERIC:
					ModLog(LOG_ERROR | LOG_STATUSBAR,GetResString(IDS_X_GENERIC_VOODOO_SEARCH_ERROR),DbgGetClientInfo());
					break;
				case SE_INVALID:
					ModLog(LOG_WARNING | LOG_STATUSBAR,GetResString(IDS_X_VOODOO_PREFIX),StrLine(GetResString(IDS_KAD_SEARCH_KEYWORD_INVALID),_aszInvKadKeywordChars));
					break;
				case SE_KAD:
					ModLog(LOG_WARNING | LOG_STATUSBAR,GetResString(IDS_X_VOODOO_PREFIX),GetResString(IDS_KAD_SEARCH_KEYWORD_ALREADY_SEARCHING));
					break;
				default:
					DebugLogWarning(_T("Unknown State Voodoo Search State Packet: %u;%u"), (uint32)uState, uValue);
			}
			theApp.emuledlg->searchwnd->m_pwndResults->FinishVoodooSearch(this,dwSearchID,SF_ERROR);
			break;
		case SS_OFFLINE:
			ModLog(LOG_WARNING | LOG_STATUSBAR,GetResString(IDS_X_VOODOO_ERR_NOTCONNECTED),DbgGetClientInfo());
			theApp.emuledlg->searchwnd->m_pwndResults->FinishVoodooSearch(this,dwSearchID,SF_ERROR);
			break;
		case SS_FINISH:
			ModLog(LOG_STATUSBAR,GetResString(IDS_X_VOODOO_SEARCH_DONE),DbgGetClientInfo());
			theApp.emuledlg->searchwnd->m_pwndResults->FinishVoodooSearch(this,dwSearchID,uValue);
			break;
		default:
			DebugLogWarning(_T("Unknown State Voodoo Search State Packet: %u;%u"), (uint32)uState, uValue);
	}
}

void CVoodooSocket::SendSearchCommand(DWORD dwSearchID, uint8 uCommand)
{
	CSafeMemFile data(128);
	data.WriteUInt8(uCommand);
	data.WriteUInt32(dwSearchID);
	SendPacket(data,OP_SEARCH_COMMAND);
}

void CVoodooSocket::SearchCommandRecived(CSafeMemFile &packet)
{
	uint8 uCommand = packet.ReadUInt8();
	DWORD dwSearchID = packet.ReadUInt32();
	switch(uCommand){
		case SC_HOLD:
			theApp.emuledlg->searchwnd->m_pwndResults->HoldGlobalSearch();
			break;
		case SC_CANCEL:
			if(dwSearchID){ // we synchronise only KAD searches becouse only thay can be more than one at once
				SSearchMaster Master;
				DWORD SlaveID;
				for (POSITION pos = theApp.voodoo->m_SearchMasterMap.GetStartPosition();pos != 0;){
					theApp.voodoo->m_SearchMasterMap.GetNextAssoc(pos,SlaveID,Master);
					if(Master.MasterID == dwSearchID){
						Kademlia::CSearchManager::StopSearch(SlaveID, false);
						theApp.voodoo->m_SearchMasterMap.RemoveKey(SlaveID);
						break;
					}
				}
			}else
				theApp.emuledlg->searchwnd->m_pwndResults->CancelEd2kSearch();
			break;
		case SC_MORE:
			theApp.emuledlg->searchwnd->m_pwndResults->SearchMore();
			break;
		default:
			DebugLogWarning(_T("Unknown Command Voodoo Search Command Packet: %u"), uCommand);
	}
}

void CVoodooSocket::SendSearchResult(DWORD dwSearchID,ESearchType eType,CSafeMemFile &packet, uint32 nServerIP, uint16 nServerPort)
{
	CSafeMemFile data(128);
	data.WriteUInt32(dwSearchID);
	data.WriteUInt8((uint8)eType);
	data.WriteUInt32(nServerIP);
	data.WriteUInt16(nServerPort);
	data.Write(packet.GetBuffer(),(UINT)packet.GetLength());
	SendPacket(data,OP_SEARCH_RESULT);
}

void CVoodooSocket::SearchResultRecived(CSafeMemFile &packet)
{
	DWORD dwSearchID = packet.ReadUInt32();
	ESearchType eType = (ESearchType)packet.ReadUInt8();
	uint32 nServerIP = packet.ReadUInt32();
	uint16 nServerPort = packet.ReadUInt16();

	CSafeMemFile data(128);
	data.Write(packet.GetBuffer()+4+1+4+2,(UINT)packet.GetLength()-4-1-4-2);
	data.SeekToBegin();
	switch(eType)
	{
		case SearchTypeEd2kServer:
			theApp.searchlist->ProcessSearchAnswer(data.GetBuffer(),(UINT)data.GetLength(),true,nServerIP,nServerPort,&m_bHaveMoreResults);
			break;
		case SearchTypeEd2kGlobal:{
			UINT count = theApp.searchlist->ProcessUDPSearchAnswer(data,true,nServerIP,nServerPort);
			theApp.emuledlg->searchwnd->m_pwndResults->CheckGlobalSearch(this,count);
			break;
		}
		case SearchTypeKademlia:{
			CSearchFile* tempFile = new CSearchFile(&data, true, dwSearchID, 0, 0, 0, true);
			theApp.searchlist->AddToList(tempFile);
			break;
		}
		default:
			return;
	}
}
// NEO: VOODOOs END

// NEO: VOODOOn - [VoodooExtensionForNeo]
//////////////////////////
// Download commands
bool CVoodooSocket::IsCommandSupported(uint8 uCommand){
	if(uCommand == INST_COLLECT && (m_uExtendedComands & 0x01))
		return true;
	else if(uCommand == INST_DROP && (m_uExtendedComands & 0x02))
		return true;
	else if(uCommand == INST_REASK && (m_uExtendedComands & 0x04))
		return true;
	//else if(uCommand == INST_RESERVED && (m_uExtendedComands & 0x08))
	//	return true;
	return false;
}

void CVoodooSocket::SendDownloadCommand(CTypedPtrList<CPtrList, CPartFile*>& FileQueue, uint8 uCommand, uint32 Flag1, uint32 Flag2)
{
	CSafeMemFile data(128);
	data.WriteUInt8(uCommand);
	data.WriteUInt32(Flag1);
	data.WriteUInt32(Flag2);

	uint32 RecordsNumber = 0;
	ULONG RecordsNumberFilePos = (ULONG)data.GetPosition();
	data.WriteUInt32(RecordsNumber);

	for (POSITION pos = FileQueue.GetHeadPosition(); pos != 0; ){
		CPartFile* pFile = FileQueue.GetNext(pos);
		if(!pFile->KnownPrefs.UseVoodoo())
			continue;
		if(pFile->GetMasterDatas(this))
			continue;
		if(!WriteFileID(data,pFile,Use64Size()))
			continue;
		RecordsNumber++;
	}

	data.Seek(RecordsNumberFilePos, CFile::begin);
	data.WriteUInt32(RecordsNumber);
	data.Seek(0, CFile::end);

	SendPacket(data,OP_DOWNLOAD_COMMAND);
}

void CVoodooSocket::DownloadCommandRecived(CSafeMemFile &packet)
{
	uint8 uCommand = packet.ReadUInt8();
	uint32 Flag1= packet.ReadUInt32();
	uint32 Flag2 = packet.ReadUInt32();

	CTypedPtrList<CPtrList, CPartFile*> FileQueue;

	uint32 RecordsNumber = packet.ReadUInt32();
	for (uint32 i = 0; i < RecordsNumber; i++)
	{
		CPartFile* pFile = GetDownloadingFile(packet,true);
		if(!pFile)
			continue;
		FileQueue.AddTail(pFile);
	}

	theApp.downloadqueue->ExecuteNeoCommand(FileQueue,uCommand,(uint8)Flag1); 
	UNREFERENCED_PARAMETER(Flag2); // flag 2 is not used yet
}

/////////////////////////////
// File Properties

void CVoodooSocket::SendFilePreferences(CKnownFile* kFile)
{
	CPartFile* pFile = kFile->IsPartFile() ? (CPartFile*)kFile : NULL;

	CSafeMemFile data(128);
	if(!WriteFileID(data,kFile,Use64Size()))
		return;

	uint32 RecordsNumber = 0;
	ULONG RecordsNumberFilePos = (ULONG)data.GetPosition();
	data.WriteUInt32(RecordsNumber);

	if(pFile){
		data.WriteUInt8(NEOFILE_VERSION);
		if(pFile->PartPrefs.SavePartPrefs(&data))
			RecordsNumber++;
	}

	data.WriteUInt8(KNOWNPREFSFILE_VERSION);
	if(kFile->KnownPrefs.SaveKnownPrefs(&data))
		RecordsNumber++;

	data.Seek(RecordsNumberFilePos, CFile::begin);
	data.WriteUInt32(RecordsNumber);
	data.Seek(0, CFile::end);

	SendPacket(data,OP_FILE_PREFERENCES);
}

void CVoodooSocket::FilePreferencesRecived(CSafeMemFile &packet)
{
	FileID fileID(packet,Use64Size());

	CPartFile* pFile = theApp.downloadqueue->GetFileByID(fileID.Hash);
	CKnownFile* kFile = pFile ? pFile : theApp.knownfiles->FindKnownFileByID(fileID.Hash);

	if(!kFile){
		if(fileID != m_LastErrFile)
			SendFileUnavalible(fileID,RET_UNKNOWN);
		else if (thePrefs.GetVerbose())
			DebugLog(_T("Droped operation for Erroneous file %s, from client %s "),md4str(fileID.Hash),DbgGetClientInfo());
		//if(m_uType != CT_ED2K)
		//	ModLog(LOG_WARNING, GetResString(IDS_X_VOODOO_FILE_FAILED_ERR),md4str(fileID.Hash),DbgGetClientInfo());
		return;
	}else if(kFile->GetFileSize() != fileID.Size){
		SendFileUnavalible(fileID,RET_BADSIZE);
		ModLog(LOG_ERROR,GetResString(IDS_X_VOODOO_FILE_SIZE_FAILED),kFile->GetFileName(),fileID.Size,kFile->GetFileSize(),DbgGetClientInfo());
		return;
	}else if(pFile){
		if(!pFile->IsVoodooFile()){
			ModLog(LOG_WARNING,GetResString(IDS_X_NOT_VOODOO_FILE),DbgGetClientInfo(),pFile->GetFileName());
			SendFileUnavalible(fileID, RET_REAL);
			return;
		}
	}else if(kFile){
		if(kFile->IsRealFile()){
			ModLog(GetResString(IDS_X_NOT_VOODOO_FILE),DbgGetClientInfo(),kFile->GetFileName());
			//SendFileUnavalible(fileID, RET_REAL);
			return;
		}
	}

	uint32 RecordsNumber = packet.ReadUInt32();
	for (uint32 i = 0; i < RecordsNumber; i++)
	{
		switch(packet.ReadUInt8()){
		case NEOFILE_VERSION:
			if(pFile){
				pFile->PartPrefs.ResetTweaks();
				pFile->PartPrefs.LoadPartPrefs(&packet);
			}
			break;
		case KNOWNPREFSFILE_VERSION:
			kFile->KnownPrefs.ResetTweaks();
			kFile->KnownPrefs.LoadKnownPrefs(&packet);
			break;
		}
	}

}
// NEO: VOODOOn END

//////////////////////////
// Process

void CVoodooSocket::Process()
{
	// Send stats
	if(m_uSupportsStatistics > 0){
		if(IsMaster()){
			if(::GetTickCount() - m_uLastStatUpdate > STAT_REFRESH){
				SendStatistics();
				m_uLastStatUpdate = ::GetTickCount();
			}
		}
		if(IsSlave()){
			if(::GetTickCount() - m_Stats.uLastStatUpdate > STAT_REFRESH * 10){ // to long delay, zero stats
				m_Stats.uUpDatarate = 0;
				m_Stats.uDownDatarate = 0;
			}
		}
	}
}

///////////////////////////////
// Debug helpers

void CVoodooSocket::PacketToDebugLogLine(LPCTSTR protocol, const uchar* packet, uint32 size, UINT opcode)
{
	if (thePrefs.GetVerbose())
	{
		CString buffer; 
	    buffer.Format(_T("Unknown %s Protocol Opcode: 0x%02x, Size=%u, Data=["), protocol, opcode, size);
		for (uint32 i = 0; i < size && i < 50; i++){
			if (i > 0)
				buffer += _T(' ');
			TCHAR temp[3];
		    _stprintf(temp, _T("%02x"), packet[i]);
			buffer += temp;
		}
		buffer += (i == size) ? _T("]") : _T("..]");
		buffer += _T("; ");
		buffer += DbgGetClientInfo();
		DebugLogWarning(_T("%s"), buffer);
	}
}

CString CVoodooSocket::DbgGetClientInfo()
{
	CString str;
	SOCKADDR_IN sockAddr = {0};
	int nSockAddrLen = sizeof(sockAddr);
	GetPeerName((SOCKADDR*)&sockAddr, &nSockAddrLen);
	str.Format(m_sName.IsEmpty() ? _T("???") : m_sName);
	if (sockAddr.sin_addr.S_un.S_addr != 0)
		str.AppendFormat(_T(" IP=%s"), ipstr(sockAddr.sin_addr));
	return str;
}

CString CVoodooSocket::GetClientDesc()
{
	return GetResString((m_uAction == VA_PARTNER) ? IDS_X_VOODOO_PARTNER : (m_uAction == VA_MASTER) ? IDS_X_VOODOO_MASTER : (m_uAction == VA_SLAVE) ? IDS_X_VOODOO_SLAVE : IDS_X_VOODOO_UNKNOWN);
}

////////////////////////////////////////////////////////////////////////////////
// CVoodoo
//

CVoodoo::CVoodoo()
{
	bListening = false;
	bStarted = false;
	uLastSearch= ::GetTickCount();
	m_port = 0;
}

CVoodoo::~CVoodoo()
{
}

bool CVoodoo::Rebind()
{
	Close();
	bListening = false;
	//KillAllSockets();

	if(thePrefs.IsVoodooAllowed()){
		if(!StartListening()){
			LogError(LOG_STATUSBAR, GetResString(IDS_MAIN_SOCKETERROR),thePrefs.GetVoodooPort());
			return false;
		}
	}
	return true;
}

bool CVoodoo::Start()
{
	if(bStarted)
		return true;

	theApp.UpdateSplash(GetResString(IDS_X_SS_PREP_VD)); // NEO: SS - [SplashScreen]
	bStarted = true;

	LoadKnownFile();

	if(thePrefs.IsVoodooAllowed())
		if(!StartListening()){
			LogError(LOG_STATUSBAR, GetResString(IDS_MAIN_SOCKETERROR),thePrefs.GetVoodooPort());
			return false;
		}

	if(thePrefs.IsAutoConnectVoodoo() == TRUE)
		TryToConnect();

	if(thePrefs.IsVoodooCastEnabled()){
		if(thePrefs.SearchForSlaves() && thePrefs.SearchForMaster())
			theApp.lancast->SendVoodoo(VA_PARTNER);
		else if(thePrefs.SearchForSlaves())
			theApp.lancast->SendVoodoo(VA_SLAVE);
		else if(thePrefs.SearchForMaster())
			theApp.lancast->SendVoodoo(VA_MASTER);
		else
			theApp.lancast->SendVoodoo(VA_QUERY);
	}

	return true;
}

void CVoodoo::Stop()
{
	bStarted = false;

	Close();
	bListening = false;
	KillAllSockets();

	ClearKnown();
}

bool CVoodoo::StartListening()
{
	if(bListening)
		return true;

	if (!Create(thePrefs.GetVoodooPort(), SOCK_STREAM, FD_ACCEPT, NULL/*thePrefs.GetVoodooAdapter()*/, FALSE/*bReuseAddr*/))
		return false;

	if (!Listen())
		return false;

	bListening = true;
	m_port = thePrefs.GetVoodooPort();
	return true;
}

void CVoodoo::OnAccept(int nErrorCode)
{

	ASSERT(bListening);
	if (!nErrorCode)
	{
		CVoodooSocket* newclient;
		SOCKADDR_IN SockAddr = {0};
		int iSockAddrLen = sizeof SockAddr;
		newclient = new CVoodooSocket();
#ifdef NATTUNNELING // NEO: UTCP - [UserModeTCP]
		if (!Accept(*((CTCPSocket*)newclient->m_Socket), (SOCKADDR*)&SockAddr, &iSockAddrLen)){
#else
		if (!Accept(*newclient, (SOCKADDR*)&SockAddr, &iSockAddrLen)){
#endif //NATTUNNELING // NEO: UTCP END
			newclient->Safe_Delete();
			DWORD nError = GetLastError();
			if (nError == WSAEWOULDBLOCK)
				DebugLogError(LOG_STATUSBAR, _T("%hs Accept() says WSAEWOULDBLOCK - setting counter to zero!"), __FUNCTION__);
			else
				DebugLogError(LOG_STATUSBAR, _T("%hs: Accept() says %s - setting counter to zero!"), __FUNCTION__, GetErrorMessage(nError, 1));
			return;
		}

		if (SockAddr.sin_addr.S_un.S_addr == 0) // for safety..
		{
			iSockAddrLen = sizeof SockAddr;
			newclient->GetPeerName((SOCKADDR*)&SockAddr, &iSockAddrLen);
			DebugLogWarning(_T("SockAddr.sin_addr.S_un.S_addr == 0;  GetPeerName returned %s"), ipstr(SockAddr.sin_addr.S_un.S_addr));
		}

		ASSERT( SockAddr.sin_addr.S_un.S_addr != 0 && SockAddr.sin_addr.S_un.S_addr != INADDR_NONE );

		if (!theApp.lancast->IsLanIP(SockAddr.sin_addr.S_un.S_addr)){
			if (thePrefs.GetLogFilteredIPs())
				AddDebugLogLine(false, _T("Rejecting connection attempt (IP=%s) - Not LAN Member"), ipstr(SockAddr.sin_addr.S_un.S_addr));
			newclient->Safe_Delete();
			return;
		}

		newclient->AsyncSelect(FD_WRITE | FD_READ | FD_CLOSE);

		newclient->AsyncSelect(FD_WRITE | FD_READ | FD_CLOSE);
		if (SockAddr.sin_addr.S_un.S_addr != 0){
			newclient->SetIP(SockAddr.sin_addr.S_un.S_addr);
			newclient->SetSocketPort(ntohs(SockAddr.sin_port));
		}

		newclient->ConfigSocket();

		newclient->SetOnLan();
	}
}

void CVoodoo::Process()
{
	for (POSITION pos = socket_list.GetHeadPosition(); pos != NULL; )
	{
		
		CVoodooSocket* cur_sock = socket_list.GetNext(pos);
		if (cur_sock->deletethis)
		{
			if(cur_sock->reconect){
				cur_sock->Reconect();
			}

#ifdef NATTUNNELING // NEO: UTCP - [UserModeTCP]
			if (((CTCPSocket*)cur_sock->m_Socket)->m_SocketData.hSocket != INVALID_SOCKET){
#else
			if (cur_sock->m_SocketData.hSocket != INVALID_SOCKET){
#endif //NATTUNNELING // NEO: UTCP END
				cur_sock->Close();			// calls 'closesocket'
			}
			else{
				cur_sock->Delete_Timed();	// may delete 'cur_sock'
			}
		}
		else{
			//cur_sock->CheckTimeOut();		// may call 'shutdown'

			cur_sock->Process();

			// NEO: NLC - [NeoLanCast]
			if(thePrefs.IsDirectLanUpload()){
				if(cur_sock->HasQueues()){
					cur_sock->Send(0x7fffffff,MAXFRAGSIZE);
				}
			}
			// NEO: NLC END
		}
	}

	// search for clients
	if(thePrefs.IsVoodooCastEnabled() &&  ::GetTickCount() - uLastSearch > thePrefs.VoodooSearchIntervalsMs()){
		uLastSearch = ::GetTickCount();

		if(thePrefs.SearchForSlaves() && thePrefs.SearchForMaster())
			theApp.lancast->SendVoodoo(VA_PARTNER);
		else if(thePrefs.SearchForSlaves())
			theApp.lancast->SendVoodoo(VA_SLAVE);
		else if(thePrefs.SearchForMaster())
			theApp.lancast->SendVoodoo(VA_MASTER);
		else if(thePrefs.IsVoodooCastEnabled() == TRUE)
			theApp.lancast->SendVoodoo(VA_QUERY);
	}

	// NEO: VOODOOs - [VoodooSearchForwarding]
	// cleanup search forwarding
	if(thePrefs.UseVoodooSearch(true)){
		SSearchMaster Master;
		DWORD MasterID;
		for (POSITION pos = m_SearchMasterMap.GetStartPosition();pos != 0;){
			m_SearchMasterMap.GetNextAssoc(pos,MasterID,Master);
			if(Master.KillTimer && ::GetTickCount() - Master.KillTimer > SEC2MS(15+5)) // the normal 15 sec kad and 5 just in case
				m_SearchMasterMap.RemoveKey(MasterID);
		}
	}
	// NEO: VOODOOs END
}

void CVoodoo::AddSocket(CVoodooSocket* toadd)
{
	socket_list.AddTail(toadd);
}

void CVoodoo::RemoveSocket(CVoodooSocket* todel)
{
	POSITION pos = socket_list.Find(todel);
	if(pos)
		socket_list.RemoveAt(pos);
}

void CVoodoo::KillAllSockets()
{
	for (POSITION pos = socket_list.GetHeadPosition(); pos != 0; pos = socket_list.GetHeadPosition())
	{
		CVoodooSocket* cur_socket = socket_list.GetAt(pos);
		cur_socket->SendGoodBy();
		cur_socket->SetAction(VA_NONE);
		cur_socket->Disconnect(_T("stop voodoo"));
		delete cur_socket;
	}
}

bool CVoodoo::IsValidSocket(CVoodooSocket* totest)
{
	return socket_list.Find(totest) != NULL;
}

////////////////////////////////
// New Connection

void CVoodoo::ConnectVoodooClient(LPCTSTR lpszHostAddress, uint16 nHostPort, uint8 uAction)
{
	LPHOSTENT pHost;
	in_addr iaDest;
	// Lookup destination
	// Use inet_addr() to determine if we're dealing with a name or an address
	iaDest.s_addr = inet_addr(CT2CA(lpszHostAddress));
	if (iaDest.s_addr == INADDR_NONE){
		pHost = gethostbyname(CT2CA(lpszHostAddress));
	}else{
		pHost = gethostbyaddr((const char*)&iaDest, sizeof(struct in_addr), AF_INET);
		lpszHostAddress = NULL;
	}

	if (pHost == NULL) {
		ModLog(GetResString(IDS_X_VOODOO_CLIENT_NOT_FOUNT), lpszHostAddress);
		return;
	}

	DWORD nHostAddress = *(DWORD*)(*pHost->h_addr_list);
	ConnectVoodooClient(nHostAddress, nHostPort, uAction, lpszHostAddress);

}

void CVoodoo::ConnectVoodooClient(DWORD nHostAddress, uint16 nHostPort, uint8 uAction, LPCTSTR lpszHostAddress)
{
	if((uAction & VA_SLAVE) && !thePrefs.UseVoodooTransfer()){
		ModLog(GetResString(IDS_X_VOODOO_SLAVE_CON_UNAVALIBLY));
		return;
	}

	if((uAction & VA_MASTER) && !thePrefs.IsSlaveAllowed()){
		ModLog(GetResString(IDS_X_VOODOO_MASTER_CON_UNAVALIBLY));
		return;
	}

	CVoodooSocket* cur_socket = GetVoodooSocket(nHostAddress,nHostPort);
	if(!cur_socket){
		SOCKADDR_IN sockAddr = {0};
		sockAddr.sin_family = AF_INET;
		sockAddr.sin_port = htons((u_short)nHostPort);
		sockAddr.sin_addr.S_un.S_addr = nHostAddress;

		ModLog(GetResString(IDS_X_VOODOO_CLIENT_CONENCT)
			, GetResString((uAction == VA_MASTER) ? IDS_X_VOODOO_MASTER : (uAction == VA_SLAVE) ? IDS_X_VOODOO_SLAVE : IDS_X_VOODOO_UNKNOWN)
			, lpszHostAddress ? lpszHostAddress : ipstr(nHostAddress), nHostPort);	

		CVoodooSocket* newclient;
		newclient = new CVoodooSocket();
#ifdef NATTUNNELING // NEO: UTCP - [UserModeTCP]
		if (!((CTCPSocket*)newclient->m_Socket)->Create()){
#else
		if (!newclient->Create()){
#endif //NATTUNNELING // NEO: UTCP END
			newclient->Safe_Delete();
			return;
		}

		newclient->SetOnLan();
		newclient->SetAddress(lpszHostAddress);
		newclient->SetPort(nHostPort);
		newclient->SetSocketPort(nHostPort);
		newclient->SetIP(nHostAddress);
		newclient->Connect((SOCKADDR*)&sockAddr, sizeof sockAddr);

		cur_socket = newclient;
	}
	else if(I2B(uAction & VA_SLAVE) == cur_socket->IsSlave() && I2B(uAction & VA_MASTER) == cur_socket->IsMaster()){
		ModLog(GetResString(IDS_X_VOODOO_CLIENT_ALREADY_CONENCTED), 
			cur_socket->GetClientDesc(), lpszHostAddress ? lpszHostAddress : ipstr(nHostAddress), nHostPort);
		return;
	}

	cur_socket->SetAction(uAction);
	cur_socket->SendVoodooHello((uAction == VA_QUERY) ? VH_QUERY : VH_HELLO);
}

////////////////////////////
// Identyfy voodoo clients

bool CVoodoo::IsVoodooClient(DWORD dwIP, uint16 uPort)
{
	for (POSITION pos = socket_list.GetHeadPosition(); pos != 0;)
	{
		CVoodooSocket* cur_socket = socket_list.GetNext(pos);
		if(cur_socket->GetIP() == dwIP && cur_socket->GetED2KPort() == uPort
		&& (cur_socket->IsSlave() || cur_socket->IsMaster()))
			return true;
	}

	return false;
}

CVoodooSocket* CVoodoo::GetVoodooSocket(DWORD dwIP, uint16 uPort, uint16 uSockPort, bool bOld)
{
	for (POSITION pos = socket_list.GetHeadPosition(); pos != 0;)
	{
		CVoodooSocket* cur_socket = socket_list.GetNext(pos);
		if((cur_socket->deletethis == false || bOld) && cur_socket->GetIP() == dwIP 
		 && (!uPort || cur_socket->GetPort() == uPort)
		 && (!uSockPort || cur_socket->GetSocketPort() == uSockPort)
		 )
			return cur_socket;
	}

	return NULL;
}

CVoodooSocket* CVoodoo::GetAnySlave(bool bED2K)
{
	for (POSITION pos = socket_list.GetHeadPosition(); pos != 0;)
	{
		CVoodooSocket* cur_socket = socket_list.GetNext(pos);
		if(cur_socket->deletethis == false && cur_socket->IsSlave() && 
		(!bED2K || cur_socket->IsED2K() && cur_socket->GetED2KPort()))
			return cur_socket;
	}

	return NULL;
}

void CVoodoo::AddVoodooClient(DWORD dwIP, uint16 uPort, uint8 uAction)
{
	if(IsVoodooClient(dwIP,uPort))
		return;

	if((uAction == VA_QUERY)
	|| (thePrefs.UseVoodooTransfer() && (uAction & VA_SLAVE) && thePrefs.SearchForSlaves())
	|| (thePrefs.IsSlaveAllowed() && (uAction & VA_MASTER) && thePrefs.SearchForMaster()))
		ConnectVoodooClient(dwIP, uPort, uAction);
	
}
/////////////////////////////
// Command Manifesting

void CVoodoo::ManifestGapList(CPartFile* pFile)
{
	for (POSITION pos = socket_list.GetHeadPosition(); pos != 0;)
	{
		CVoodooSocket* cur_socket = socket_list.GetNext(pos);
		if(cur_socket->IsSlave() && pFile->GetMasterDatas(cur_socket) == NULL)
			cur_socket->SendGapList(pFile);
	}
}

void CVoodoo::ManifestDownloadOrder(CPartFile* pFile)
{
	for (POSITION pos = socket_list.GetHeadPosition(); pos != 0;)
	{
		CVoodooSocket* cur_socket = socket_list.GetNext(pos);
		if(cur_socket->IsSlave() && cur_socket->IsED2K() && pFile->GetMasterDatas(cur_socket) == NULL)
			cur_socket->SendDownloadOrder(pFile);
	}
}

void CVoodoo::ManifestDownloadInstruction(CPartFile* pFile, uint8 uInstruction, uint32 Flag1, uint32 Flag2)
{
	for (POSITION pos = socket_list.GetHeadPosition(); pos != 0;)
	{
		CVoodooSocket* cur_socket = socket_list.GetNext(pos);
		if(cur_socket->IsSlave() && pFile->GetMasterDatas(cur_socket) == NULL && cur_socket->IsInstructionSupported(uInstruction, Flag1))
			cur_socket->SendDownloadInstruction(pFile, uInstruction, Flag1, Flag2);
	}
}

void CVoodoo::ManifestShareInstruction(CKnownFile* kFile, uint8 uInstruction, uint32 Flag1, uint32 Flag2)
{
	for (POSITION pos = socket_list.GetHeadPosition(); pos != 0;)
	{
		CVoodooSocket* cur_socket = socket_list.GetNext(pos);
		if(cur_socket->IsSlave() && kFile->GetMasterDatas(cur_socket) == NULL && cur_socket->IsInstructionSupported(uInstruction, Flag1))
			cur_socket->SendShareInstruction(kFile, uInstruction, Flag1, Flag2);
	}
}

// NEO: VOODOOx - [VoodooSourceExchange]
// Source exchange
void CVoodoo::ManifestSourceListRequest(CPartFile* pFile)
{
	for (POSITION pos = socket_list.GetHeadPosition(); pos != 0;)
	{
		CVoodooSocket* cur_socket = socket_list.GetNext(pos);
		if(cur_socket->IsSlave() && cur_socket->GetNeoSourceExportVersion() > 1 || pFile->GetMasterDatas(cur_socket) != NULL) // don't request from version 1 it will cause an hierarchy exception!
			cur_socket->RequestSourceList(pFile);
	}
}

void CVoodoo::ManifestSingleSource(CPartFile* pFile, CUpDownClient* sClient)
{
	for (POSITION pos = socket_list.GetHeadPosition(); pos != 0;)
	{
		CVoodooSocket* cur_socket = socket_list.GetNext(pos);
		if((cur_socket->IsSlave() || pFile->GetMasterDatas(cur_socket)) && cur_socket->GetNeoSourceExchangeVersion())
			cur_socket->SendSingleSource(pFile, sClient);
	}
}
// NEO: VOODOOx END

// NEO: VOODOOs - [VoodooSearchForwarding]
// Search forwarding
void CVoodoo::ManifestNewSearch(SSearchParams* pParams)
{
	for (POSITION pos = socket_list.GetHeadPosition(); pos != 0;)
	{
		CVoodooSocket* cur_socket = socket_list.GetNext(pos);
		if(cur_socket->IsSlave() && cur_socket->IsVoodooSearchSupported())
			cur_socket->SendNewSearch(pParams);
	}
}

void CVoodoo::ManifestSearchCommand(DWORD dwSearchID, uint8 uCommand)
{
	for (POSITION pos = socket_list.GetHeadPosition(); pos != 0;)
	{
		CVoodooSocket* cur_socket = socket_list.GetNext(pos);
		if(cur_socket->IsSlave() && cur_socket->IsVoodooSearchSupported() && (uCommand != SC_MORE || cur_socket->m_bHaveMoreResults))
			cur_socket->SendSearchCommand(dwSearchID,uCommand);
	}
}
// NEO: VOODOOs END

void CVoodoo::ManifestThrottleChunk(CPartFile* pFile, uint16 partNumbert, CVoodooSocket* source, bool bRelease)
{
	for (POSITION pos = socket_list.GetHeadPosition(); pos != 0;)
	{
		CVoodooSocket* cur_socket = socket_list.GetNext(pos);
		if(source == cur_socket)
			continue;
		if(source != NULL && pFile->IsVoodooFile()){ // voodooo files in the midle (voodoo cascade) only forward this request on one direction
			if(source->IsSlave() && cur_socket->IsSlave())
				continue;
			if(pFile->GetMasterDatas(source) && pFile->GetMasterDatas(cur_socket))
				continue;
		}
		if(cur_socket->GetAdvDownloadSyncVersion() > (bRelease ? 1 : 0) && (cur_socket->IsSlave() || pFile->GetMasterDatas(cur_socket)))
			cur_socket->SendThrottleChunk(pFile, partNumbert, bRelease);
	}
}

void CVoodoo::ManifestCorruptedSenderWarning(DWORD dwIP)
{
	for (POSITION pos = socket_list.GetHeadPosition(); pos != 0;)
	{
		CVoodooSocket* cur_socket = socket_list.GetNext(pos);
		if(cur_socket->IsSlave() && cur_socket->GetCorruptionHandlingVersion())
			cur_socket->SendCorruptedSenderWarning(dwIP);
	}
}

// NEO: VOODOOn - [VoodooExtensionForNeo]

void CVoodoo::ManifestFilePreferences(CKnownFile* kFile)
{
	for (POSITION pos = socket_list.GetHeadPosition(); pos != 0;)
	{
		CVoodooSocket* cur_socket = socket_list.GetNext(pos);
		if(cur_socket->IsSlave() && kFile->GetMasterDatas(cur_socket) == NULL && cur_socket->GetNeoFilePreferencesVersion() )
			cur_socket->SendFilePreferences(kFile);
	}
}

void CVoodoo::ManifestDownloadCommand(CTypedPtrList<CPtrList, CPartFile*>& FileQueue, uint8 uCommand, uint32 Flag1, uint32 Flag2)
{
	for (POSITION pos = socket_list.GetHeadPosition(); pos != 0;)
	{
		CVoodooSocket* cur_socket = socket_list.GetNext(pos);
		if(cur_socket->IsSlave() && cur_socket->IsCommandSupported(uCommand))
			cur_socket->SendDownloadCommand(FileQueue, uCommand, Flag1, Flag2);
	}
}

// NEO: VOODOOn END

///////////////////////////////
// Voodoo List

void CVoodoo::ClearKnown()
{
	while(!known_list.IsEmpty())
		delete known_list.RemoveHead();
}

SVoodooClient* CVoodoo::GetClientByAddress(LPCTSTR address, uint16 port)
{
	for (POSITION pos = known_list.GetHeadPosition();pos != 0;){
        SVoodooClient* cur_client = known_list.GetNext(pos);
        if ((port == cur_client->clientPort || port == 0) && !_tcscmp(cur_client->clientAddress, address))
			return cur_client; 
	}
	return NULL;
}

SVoodooClient* CVoodoo::GetClientByIP(uint32 nIP, uint16 nPort, uint16 uSockPort)
{
	if(nIP == 0)
		return NULL;

	for (POSITION pos = known_list.GetHeadPosition();pos != 0;){
        SVoodooClient* cur_client = known_list.GetNext(pos);
		if (cur_client->clientIP == nIP 
		 && (nPort == cur_client->clientPort || !nPort)
		 && (!cur_client->socketPort || uSockPort == cur_client->socketPort || !uSockPort)
		 )
			return cur_client;
	}
	return NULL;
}

bool CVoodoo::AddKnown(CVoodooSocket* VoodooClient)
{
	SVoodooClient* cur_client = GetClientByIP(VoodooClient->GetIP(),VoodooClient->GetPort(),VoodooClient->GetSocketPort());
	if(!cur_client && !VoodooClient->GetAddress().IsEmpty())
		cur_client = GetClientByAddress(VoodooClient->GetAddress(),VoodooClient->GetPort());
	
	if(!cur_client){
		cur_client = new SVoodooClient;
		if(!VoodooClient->GetAddress().IsEmpty())
			cur_client->clientAddress = VoodooClient->GetAddress();
		else
			cur_client->clientAddress = ipstr(VoodooClient->GetIP());
		known_list.AddTail(cur_client);
	}else if(cur_client->m_uAction == VA_BLOCK)
		return false;

	cur_client->clientIP = VoodooClient->GetIP();
	cur_client->clientPort = VoodooClient->GetPort();
	cur_client->socketPort = VoodooClient->GetSocketPort();
	cur_client->m_bUsable = VoodooClient->IsUsable();
	cur_client->m_sSpell = VoodooClient->GetSpell();
	cur_client->m_sName = VoodooClient->GetName();
	cur_client->m_uAction = VoodooClient->GetAction();
	cur_client->m_uType = VoodooClient->GetType();

	if(VoodooClient->GetPort() != 0)
		SaveKnownToFile();

	return true;
}

void CVoodoo::CleanUpKnown(CVoodooSocket* VoodooClient)
{
	for (POSITION pos = known_list.GetHeadPosition();pos != 0;){
		POSITION toRemove = pos;
        SVoodooClient* cur_client = known_list.GetNext(pos);
		if(VoodooClient->GetIP() == cur_client->clientIP
		&& VoodooClient->GetSocketPort() == cur_client->socketPort
		){
			if(cur_client->clientPort){
				cur_client->socketPort = 0;
			}else{
				known_list.RemoveAt(toRemove);
				delete cur_client;
			}
			break;
		}
	}
}

bool CVoodoo::IsValudKnownClinet(SVoodooClient* VoodooClient)
{
	return (known_list.Find(VoodooClient) != NULL);
}

void CVoodoo::SaveKnownToFile()
{
	CStdioFile voodoolist;
	if (!voodoolist.Open(thePrefs.GetConfigDir() + _T("voodoolist.dat"), CFile::modeCreate | CFile::modeWrite | CFile::typeText | CFile::shareDenyWrite))
		return;

	CString strLine;
	for (POSITION pos = known_list.GetHeadPosition();pos != 0;){
        SVoodooClient* cur_client = known_list.GetNext(pos);
		if(cur_client->clientPort == 0)
			continue;
		strLine.Empty();
		if(!cur_client->m_sSpell.IsEmpty())
			strLine.AppendFormat(_T("!%s,"),cur_client->m_sSpell);

		strLine.AppendFormat(_T("%u,%s:%u,%u,%u"), 
												cur_client->m_uAction, 
												cur_client->clientAddress, 
												cur_client->clientPort, 
												cur_client->m_uType, 
												cur_client->m_bUsable);
		strLine.AppendFormat(_T(";%s"),cur_client->m_sName);
		voodoolist.WriteString(strLine + _T("\n"));
	}

	voodoolist.Close();
}

void CVoodoo::LoadKnownFile()
{
	CStdioFile voodoolist;
	if (!voodoolist.Open(thePrefs.GetConfigDir() + _T("voodoolist.dat"), CFile::modeRead | CFile::typeText | CFile::shareDenyWrite))
		return;

	UINT uAction;
	TCHAR Address[100];
	DWORD clientIP;
	CString clientAddress;
	UINT clientPort;
	UINT uType;
	UINT bUsable;
	CString sSpell;

	CString strLine;
	while (voodoolist.ReadString(strLine))
	{
		int pos = strLine.Find(':');
		if(pos == -1)
			continue;
		strLine.SetAt(pos, ' ');

		if(strLine.Left(1) == _T("!")){
			int pos = strLine.Find(',');
			if(pos == -1)
				continue;
			sSpell = strLine.Mid(1,pos-1);
			strLine = strLine.Mid(pos+1);
		}else
			sSpell.Empty();

		int test = _stscanf(strLine,_T("%u,%s %u,%u,%u,%s;"),&uAction, Address, &clientPort, &uType, &bUsable, sSpell);
		if(test < 5){
			continue;
		}

		clientIP = inet_addr(CT2CA(Address));
		if (clientIP == INADDR_NONE){
			clientIP = 0;
			clientAddress = Address;
		}else{
			clientAddress = ipstr(clientIP);
		}

		if(GetClientByAddress(clientAddress, (uint16)clientPort) != NULL)
			continue;

		SVoodooClient* new_client = new SVoodooClient;
		new_client->clientIP = clientIP;
		new_client->clientAddress = clientAddress;
		new_client->m_uAction = (uint8)uAction;
		new_client->clientPort = (uint16)clientPort;
		new_client->socketPort = 0;
		new_client->m_uType = (uint8)uType;
		new_client->m_bUsable = I2B(bUsable);
		new_client->m_sSpell = sSpell;

		pos = strLine.Find(';');
		if(pos != -1)
			new_client->m_sName = strLine.Mid(pos + 1);

		known_list.AddTail(new_client);
	}

	voodoolist.Close();
}

void CVoodoo::TryToConnect()
{
	for (POSITION pos = known_list.GetHeadPosition();pos != 0;){
        SVoodooClient* cur_client = known_list.GetNext(pos);
		if(thePrefs.UseVoodooTransfer() && thePrefs.IsSlaveAllowed() && cur_client->m_uAction == VA_PARTNER)
			ConnectVoodooClient(cur_client->clientAddress,cur_client->clientPort,VA_PARTNER);
		else if(thePrefs.UseVoodooTransfer() && cur_client->m_uAction == VA_SLAVE)
			ConnectVoodooClient(cur_client->clientAddress,cur_client->clientPort,VA_SLAVE);
		else if(thePrefs.IsSlaveAllowed() && cur_client->m_uAction == VA_MASTER)
			ConnectVoodooClient(cur_client->clientAddress,cur_client->clientPort,VA_MASTER);
	}
}

// stats
uint32 CVoodoo::GetUpDatarate()
{
	uint32 uUpDatarate = 0;
	for (POSITION pos = socket_list.GetHeadPosition(); pos != 0;)
	{
		CVoodooSocket* cur_socket = socket_list.GetNext(pos);
		if(cur_socket->IsSlave())
			uUpDatarate += cur_socket->m_Stats.uUpDatarate;
	}
	return uUpDatarate;
}

uint32 CVoodoo::GetDownDatarate()
{
	uint32 uDownDatarate = 0;
	for (POSITION pos = socket_list.GetHeadPosition(); pos != 0;)
	{
		CVoodooSocket* cur_socket = socket_list.GetNext(pos);
		if(cur_socket->IsSlave())
			uDownDatarate += cur_socket->m_Stats.uDownDatarate;
	}
	return uDownDatarate;
}

// file stats
//uint32 CVoodoo::GetUpDatarate(CKnownFile* File)
//{
//	uint32 uUpDatarate = 0;
//	for (POSITION pos = socket_list.GetHeadPosition(); pos != 0;)
//	{
//		CVoodooSocket* cur_socket = socket_list.GetNext(pos);
//		SFileVoodooStats Stats;
//		if(cur_socket->IsSlave() && cur_socket->m_FileStats.Lookup(File,Stats))
//			if(::GetTickCount() - Stats.uLastStatUpdate > STAT_REFRESH * 10) // to long delay, zero stats
//				cur_socket->m_FileStats.RemoveKey(File);
//			else
//				uUpDatarate += Stats.uUpDatarate;
//	}
//	return uUpDatarate;
//}

uint32 CVoodoo::GetDownDatarate(CKnownFile* File)
{
	uint32 uDownDatarate = 0;
	for (POSITION pos = socket_list.GetHeadPosition(); pos != 0;)
	{
		CVoodooSocket* cur_socket = socket_list.GetNext(pos);
		SFileVoodooStats Stats;
		if(cur_socket->IsSlave() && cur_socket->m_FileStats.Lookup(File,Stats)){
			if(::GetTickCount() - Stats.uLastStatUpdate > STAT_REFRESH * 10) // to long delay, zero stats
				cur_socket->m_FileStats.RemoveKey(File);
			else
				uDownDatarate += Stats.uDownDatarate;
		}
	}
	return uDownDatarate;
}
#endif // VOODOO // NEO: VOODOO END <-- Xanatos --
