//this file is part of Silversurfer
//Copyright (C)2008 dlarge
//
//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"
#ifdef _DEBUG
#include "DebugHelpers.h"
#endif

#include <share.h>
#include <io.h>
#include "emule.h"
#include "Argos.h"
#include "otherfunctions.h"
#include "preferences.h"
#include "updownclient.h"
#include "clientlist.h"
#include "clientcredits.h"
#include "uploadqueue.h"
#include "Version.h"
#include "EmuleDlg.h"
#include "Log.h"
#include "StringConversion.h"
#include "OpCodes.h"
#pragma warning(disable:4516) // access-declarations are deprecated; member using-declarations provide a better alternative
#include <crypto51/rsa.h>
#include <crypto51/sha.h>
#include <crypto51/md5.h>
#include <crypto51/files.h>
#pragma warning(default:4516)
#include "HttpDownloadDlg.h"
#include "ZipFile.h"
#include "SafeFile.h"
#include "ini2.h"
#include "EMF/ModFunctionsFiles/Functions.h"
#include "EMF/ModName/Modname.h" //==> dlarge Modname

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

#define TRUST_SIZE 3072000

CArgos::CArgos(){
	m_ArgosList.InitHashTable(331);
	m_TrustList.InitHashTable(2011);

	m_dwLastArgosCleanUp = ::GetTickCount();
	m_dwLastTrustCleanUp = ::GetTickCount();

	CreateAntiNickThiefTag();

	m_dlpInstance = NULL;
	m_dlpCheckClient = NULL;

	threadEndedEvent = new CEvent(0, 1);
	doRun = true;

	memset(&Statistic,0,sizeof(TLeecherStatistic));

	if(thePrefs.UseArgosDetectionEngine())
		LoadFromFile();

	if(thePrefs.IsDLPDetection())
		LoadDLPlibrary();

	AfxBeginThread(RunProc, (LPVOID)this, THREAD_PRIORITY_BELOW_NORMAL); // THREAD_PRIORITY_LOWEST
}

CArgos::~CArgos(){
	RemoveAllArgosClients();
	ClearTrustList();

	//EndThread();
	RemoveAllKnownLeechers();
	UnLoadDLPlibrary();
	delete threadEndedEvent;
}

void CArgos::ClearTrustList(){
	POSITION pos = m_TrustList.GetStartPosition();
	uint32 nKey;
	TArgosTrust* pResult;
	while (pos != NULL){
		m_TrustList.GetNextAssoc(pos, nKey, pResult);
		m_TrustList.RemoveKey(nKey);
		delete pResult;
	}
}

void CArgos::RemoveAllArgosClients(){
	POSITION pos = m_ArgosList.GetStartPosition();
	uint32 nKey;
	TArgosClient* pResult;
	while (pos != NULL){
		m_ArgosList.GetNextAssoc(pos, nKey, pResult);
		//theApp.clientlist->RemoveBannedClient(dwIP);
		m_ArgosList.RemoveKey(nKey);
		if(pResult->Comment)
			delete pResult->Comment;
		delete pResult;
	}
}

TArgosClient* CArgos::AddArgosClient(uint32 dwIP)
{
	if(dwIP == 0)
		return NULL;
	TArgosClient* pClient = 0;
	if (m_ArgosList.Lookup(dwIP, pClient))
		return pClient;
	else {
		pClient = new TArgosClient;
		memset(pClient,0,sizeof(TArgosClient));
		m_ArgosList.SetAt(dwIP, pClient);
		return pClient;
	}
}

void CArgos::RemoveArgosClient(uint32 dwIP, bool bTrust)
{
	TArgosClient* pClient = 0;
	if (m_ArgosList.Lookup(dwIP, pClient)){
		theApp.clientlist->RemoveBannedClient(dwIP);
		m_ArgosList.RemoveKey(dwIP);
		if(pClient->Comment)
			delete pClient->Comment;
		delete pClient;
		if(!bTrust) // dont reset trust if trust was the reason for the unban
			ResetTrusted(dwIP);
	}
}

TArgosClient* CArgos::GetArgosClient(uint32 dwIP)
{
	if(dwIP == 0)
		return NULL;
	TArgosClient* pResult;
	if (m_ArgosList.Lookup(dwIP, pResult))
		return pResult;
	return NULL;
}

uint32 CArgos::GetTrust(uint32 dwIP)
{
	TArgosTrust* pResult;
	if (m_TrustList.Lookup(dwIP, pResult))
		return pResult->m_nTrust;
	return 0;
}

void CArgos::AddTrustedTransfer(uint32 dwIP, uint32 nTransferred){
	TArgosTrust* pTrust = 0;
	if (!m_TrustList.Lookup(dwIP, pTrust)){
		pTrust = new TArgosTrust;
		pTrust->m_nTrust = 2;
		pTrust->m_nTrustedTransfer = 0;
		m_TrustList.SetAt(dwIP, pTrust);
	}

	pTrust->m_nTrustedTransfer = ::GetTickCount();
	pTrust->m_nTrustedTransfer += nTransferred;

	if (pTrust->m_nTrustedTransfer > TRUST_SIZE){
		pTrust->m_nTrustedTransfer = 0;
		if (pTrust->m_nTrust < 8)
			pTrust->m_nTrust++;
		if (thePrefs.IsArgosTrustTest()){
			if (pTrust->m_nTrust > 2)
				ArgosTrust(dwIP);
		}
	}
}

void CArgos::ResetTrusted(uint32 dwIP){
	TArgosTrust* pTrust = 0;
	if (m_TrustList.Lookup(dwIP, pTrust)){
		pTrust->m_nTrust = 2;
		pTrust->m_nTrustedTransfer = 2;
	}
}

void CArgos::ArgosTrust(uint32 dwIP)
{
	TArgosClient* aClient = GetArgosClient(dwIP);
	if(aClient == NULL)
		return;

	if(aClient->m_bArgosIsHashThief && thePrefs.TrustUnArgosHashThief())
		aClient->m_bArgosIsHashThief = 0;
		
	if(aClient->m_bArgosIsHashChanged && thePrefs.TrustUnArgosHashChanged())
		aClient->m_bArgosIsHashChanged = 0;

	if(aClient->m_bArgosIsGhostMode && thePrefs.TrustUnArgosGhostMode())
		aClient->m_bArgosIsGhostMode = 0;

	if(aClient->m_bArgosIsAgressiv && thePrefs.TrustUnArgosAgressiv())
		aClient->m_bArgosIsAgressiv = 0;

	if(!IsArgos(aClient))
		RemoveArgosClient(dwIP, true);
	else
		CalcPunishment(aClient,dwIP);
}

void CArgos::Process()
{
	const uint32 cur_tick = ::GetTickCount();

	///////////////////////////////////////////////////////////////////////////
	// Process and cleanup Argos Clients
	//
	if (m_dwLastArgosCleanUp + thePrefs.GetArgosProcessTimeMs(true) < cur_tick)
	{
		m_dwLastArgosCleanUp = cur_tick;

		POSITION pos = m_ArgosList.GetStartPosition();
		uint32 nKey;
		TArgosClient* pResult;
		while (pos != NULL) {
			m_ArgosList.GetNextAssoc(pos, nKey, pResult);
			bool bChange = false;
			bool bKeep = false;
			if(pResult->m_bArgosIsCorruptedSender){
				if(pResult->m_uLastDetectionTime + thePrefs.GetArgosCorruptedSenderTimeMs() < cur_tick){
					pResult->m_bArgosIsCorruptedSender = 0;
					bChange = true;
				}else
					bKeep = true;
			}
			if(pResult->m_bArgosIsFileScanner){
				if(pResult->m_uLastDetectionTime + thePrefs.GetArgosFileScannerTimeMs() < cur_tick){
					pResult->m_bArgosIsFileScanner = 0;
					bChange = true;
				}else
					bKeep = true;
			}
			if(pResult->m_bArgosIsFileFaker){
				if(pResult->m_uLastDetectionTime + thePrefs.GetArgosFileFakerTimeMs() < cur_tick){
					pResult->m_bArgosIsFileFaker = 0;
					bChange = true;
				}else
					bKeep = true;
			}
			if(pResult->m_bArgosIsAgressiv){
				if(pResult->m_uLastDetectionTime + thePrefs.GetArgosAgressionBanTimeMs() < cur_tick){
					pResult->m_bArgosIsAgressiv = 0;
					bChange = true;
				}else
					bKeep = true;
			}
			if(pResult->m_bArgosIsLeecherOpCode){
				if(pResult->m_uLastDetectionTime + thePrefs.GetArgosOpCodeTimeMs() < cur_tick){
					pResult->m_bArgosIsLeecherOpCode = 0;
					bChange = true;
				}else
					bKeep = true;
			}
			if(pResult->m_bArgosIsHashChanged){
				if(pResult->m_uLastDetectionTime + thePrefs.GetArgosHashChangedTimeMs() < cur_tick){
					pResult->m_bArgosIsHashChanged = 0;
					bChange = true;
				}else
					bKeep = true;
			}
			if(pResult->m_bArgosIsRankFlooder){
				if(pResult->m_uLastDetectionTime + thePrefs.GetArgosRankFlooderTimeMs() < cur_tick){
					pResult->m_bArgosIsRankFlooder = 0;
					bChange = true;
				}else
					bKeep = true;
			}
			if(pResult->m_bArgosIsGhostMode){
				if(pResult->m_uLastDetectionTime + thePrefs.GetArgosGhostModeTimeMs() < cur_tick){
					pResult->m_bArgosIsGhostMode = 0;
					bChange = true;
				}else
					bKeep = true;
			}
			if(pResult->m_bArgosIsFailed){
				if(pResult->m_uLastDetectionTime + thePrefs.GetArgosFailedTimeMs() < cur_tick){
					pResult->m_bArgosIsFailed = 0;
					bChange = true;
				}else
					bKeep = true;
			}

			if(pResult->m_bArgosIsXSExploit){
				if(pResult->m_uLastDetectionTime + thePrefs.GetArgosXSExploitTimeMs() < cur_tick){
					pResult->m_bArgosIsXSExploit = 0;
					bChange = true;
				}else
					bKeep = true;
			}

			if(pResult->m_bArgosIsNickChanger){
				if(pResult->m_uLastDetectionTime + thePrefs.GetArgosNickChangerTimeMs() < cur_tick){
					pResult->m_bArgosIsNickChanger = 0;
					bChange = true;
				}else
					bKeep = true;
			}
			if(pResult->m_bArgosIsModChanger){
				if(pResult->m_uLastDetectionTime + thePrefs.GetArgosModChangerTimeMs() < cur_tick){
					pResult->m_bArgosIsModChanger = 0;
					bChange = true;
				}else
					bKeep = true;
			}

			if(pResult->m_bArgosIsSpam){
				if(pResult->m_uLastDetectionTime + thePrefs.GetArgosSpamTimeMs() < cur_tick){
					pResult->m_bArgosIsSpam = 0;
					bChange = true;
				}else
					bKeep = true;
			}

			if(pResult->m_bArgosIsBadHello){
				if(pResult->m_uLastDetectionTime + thePrefs.GetArgosBadHelloTimeMs() < cur_tick){
					pResult->m_bArgosIsBadHello = 0;
					bChange = true;
				}else
					bKeep = true;
			}

			if(pResult->m_bArgosIsBadProt){
				if(pResult->m_uLastDetectionTime + thePrefs.GetArgosBadProtTimeMs() < cur_tick){
					pResult->m_bArgosIsBadProt = 0;
					bChange = true;
				}else
					bKeep = true;
			}

			if(!bKeep && (!IsArgos(pResult) || pResult->m_uLastSeenTime + thePrefs.GetArgosProcessTimeMs() < cur_tick))
				RemoveArgosClient(nKey);
			else if(bChange)
				CalcPunishment(pResult,nKey);
			/*else if(bKeep)
				if(theApp.clientlist->IsBannedClient(nKey))
					theApp.clientlist->AddBannedClient(nKey) // refresch the ban
			*/
		}

		// This function should cleanup officlai bans ordered bevoure argos was enabled.
		CMap<uint32, uint32, uint32, uint32>& bannedList = theApp.clientlist->GetBannedList();
		/*POSITION*/pos = bannedList.GetStartPosition();
		//uint32 nKey;
		uint32 dwBantime;
		while (pos != NULL)
		{
			bannedList.GetNextAssoc( pos, nKey, dwBantime );
			if(!IsArgos(nKey)){ // only when no argos reason is activ
				if (dwBantime + thePrefs.GetArgosGenericBanTimeMs() < cur_tick )
					theApp.clientlist->RemoveBannedClient(nKey);
			}
		}
	}

	///////////////////////////////////////////////////////////////////////////
	// Cleanup trust list list
	//
	if (m_dwLastTrustCleanUp + thePrefs.GetArgosTrustTimeMs(true) < cur_tick)
	{
		m_dwLastTrustCleanUp = cur_tick;

		POSITION pos = m_TrustList.GetStartPosition();
		uint32 nKey;
		TArgosTrust* pResult;
		while (pos != NULL){
			m_TrustList.GetNextAssoc(pos, nKey, pResult);
			if(pResult->m_uLastTransferTime + thePrefs.GetArgosTrustTimeMs() < cur_tick){
				m_TrustList.RemoveKey(nKey);
				delete pResult;
			}
		}
	}
}

void CArgos::DoArgos(uint32 dwIP, EArgosReason Reason, ELeecherType LeecherType, CString Comment, EOpcodeProc uOpSource, uint8 uOpCode)
{
	TArgosClient* aClient = AddArgosClient(dwIP);
	if(aClient == NULL)
		return;

	ASSERT(LeecherType != LT_NO);
	if(aClient->m_eLeecherType != LT_NO)
 		aClient->m_eLeecherType = min(LeecherType,aClient->m_eLeecherType); // Take the extremst identyfication
	else
		aClient->m_eLeecherType = LeecherType;
	if(!Comment.IsEmpty()){
		if(!aClient->m_bArgosIsLeecherOpCode || Reason == AR_LEECHEROPCODE){ // Leecher Opcode Comments have priority
			if(aClient->Comment)
				delete aClient->Comment;
			aClient->Comment = new CString(Comment);
		}
	}

	switch(Reason){
	case AR_NULL:
		break;
	case AR_LEECHERMOD:		
		aClient->m_bArgosIsLeecherMod = 1;	
		break;
	case AR_LEECHERNICK:	
		aClient->m_bArgosIsLeecherNick = 1;											
		break;
	case AR_LEECHEROPCODE:
		aClient->m_bArgosIsLeecherOpCode = (uint8)uOpSource; // Source
		aClient->m_uLeecherOpCode = uOpCode; // OpCode
		aClient->m_uLastDetectionTime = ::GetTickCount();
		break;
	case AR_LEECHERHASH:
		aClient->m_bArgosIsLeecherHash = 1;
		break;

	case AR_FAKECLIENT:		
		aClient->m_bArgosIsFakeClient |= 2; //1<<Flag1;
		if (thePrefs.IsArgosLogLeechers())
			ModLog(GetResString(IDS_ARGOS_FAKE_CLIENT), GetClientNickSoft(dwIP));
		break;

	default:
		ASSERT(0);
	}

	aClient->m_uLastSeenTime = ::GetTickCount();

	CalcPunishment(aClient,dwIP);
}

void CArgos::DoArgos(uint32 dwIP, EArgosReason Reason, UINT Flag1)
{
	TArgosClient* aClient = AddArgosClient(dwIP);
	if(aClient == NULL)
		return;


	switch(Reason){
	case AR_NULL:
		break;

	//case AR_LEECHERMOD:		
	//	aClient->m_bArgosIsLeecherMod = 1;	
	//	break;
	//case AR_LEECHERNICK:	
	//	aClient->m_bArgosIsLeecherNick = 1;	
	//	break;
	//case AR_LEECHEROPCODE:
	//	aClient->m_bArgosIsLeecherOpCode = Flag2; // Source
	//	aClient->m_uLeecherOpCode = uOpCode; // OpCode
	//	aClient->m_uLastDetectionTime = ::GetTickCount();
	//	break;
	//case AR_LEECHERHASH:
	//	aClient->m_bArgosIsLeecherHash = 1;
	//	break;

	case AR_NICKTHIEF:	
		aClient->m_bArgosIsNickThief = 1;	
		if (thePrefs.IsArgosLogLeechers())
			ModLog(GetResString(IDS_ARGOS_NICK_THIEF), GetClientNickSoft(dwIP));
		break;
	case AR_MODTHIEF:		
		aClient->m_bArgosIsModThief = 1;	
		if (thePrefs.IsArgosLogLeechers())
			ModLog(GetResString(IDS_ARGOS_MOD_THIEF), GetClientNickSoft(dwIP));
		break;
	case AR_HASHTHIEF:		
		aClient->m_bArgosIsHashThief |= 1<< Flag1;	
		if (thePrefs.IsArgosLogLeechers()) 
			ModLog(GetResString(IDS_ARGOS_HASH_THIEF), GetClientNickSoft(dwIP));
		break;

	case AR_CREDITHACK:	
		aClient->m_bArgosIsCreditHack = 1;	
		if (thePrefs.IsArgosLogLeechers())
			ModLog(GetResString(IDS_ARGOS_CREDITHACK), GetClientNickSoft(dwIP));
		break;

	case AR_HASHCHANGED:	
		aClient->m_bArgosIsHashChanged |= 1<<Flag1;
		aClient->m_uLastDetectionTime = ::GetTickCount();
		if (thePrefs.IsArgosLogLeechers())
			ModLog(GetResString(IDS_ARGOS_HASH_CHANGED), GetClientNickSoft(dwIP));
		break;

	case AR_GHOSTMODE:		
		aClient->m_bArgosIsGhostMode = 1;
		aClient->m_uLastDetectionTime = ::GetTickCount();
		if (thePrefs.IsArgosLogLeechers())
			ModLog(GetResString(IDS_ARGOS_GHOSTMOD), GetClientNickSoft(dwIP));
		break;
	case AR_FAKECLIENT:		
		aClient->m_bArgosIsFakeClient |= 1; //1<<Flag1;
		if (thePrefs.IsArgosLogLeechers())
			ModLog(GetResString(IDS_ARGOS_FAKE_CLIENT), GetClientNickSoft(dwIP));
		break;

	case AR_AGRESSIV:
		aClient->m_bArgosIsAgressiv |= 1<<Flag1;
		aClient->m_uLastDetectionTime = ::GetTickCount();
		if (thePrefs.IsArgosLogLeechers())
			ModLog(GetResString(IDS_ARGOS_AGGRESSIVE), GetClientNickSoft(dwIP));
		break;
	case AR_FILESCANNER:
		aClient->m_bArgosIsFileScanner = 1;
		aClient->m_uLastDetectionTime = ::GetTickCount();
		if (thePrefs.IsArgosLogLeechers())
			ModLog(GetResString(IDS_ARGOS_FILESCANNER), GetClientNickSoft(dwIP));
		break;
	case AR_FILEFAKER:
		aClient->m_bArgosIsFileFaker = 1;
		aClient->m_uLastDetectionTime = ::GetTickCount();
		if (thePrefs.IsArgosLogLeechers())
			ModLog(GetResString(IDS_ARGOS_FILEFAKER), GetClientNickSoft(dwIP));
		break;
	case AR_CORRUPTEDSENDER:
		aClient->m_bArgosIsCorruptedSender = 1;
		aClient->m_uLastDetectionTime = ::GetTickCount();
		if (thePrefs.IsArgosLogLeechers())
			ModLog(GetResString(IDS_ARGOS_CORRUPTEDSENDER), GetClientNickSoft(dwIP));
		break;

	case AR_RANKFLOODER:	
		aClient->m_bArgosIsRankFlooder = 1;
		aClient->m_uLastDetectionTime = ::GetTickCount();
		if (thePrefs.IsArgosLogLeechers())
			ModLog(GetResString(IDS_ARGOS_RANKFLOODER), GetClientNickSoft(dwIP));
		break;

	case AR_FAILED:
		aClient->m_bArgosIsFailed = 1;	
		aClient->m_uLastDetectionTime = ::GetTickCount();
		if (thePrefs.IsArgosLogLeechers())
			ModLog(GetResString(IDS_ARGOS_FAILED), GetClientNickSoft(dwIP));
		break;

	case AR_XSEXPLOIT:
		aClient->m_bArgosIsXSExploit |= 1<<Flag1;
		aClient->m_uLastDetectionTime = ::GetTickCount();
		if (thePrefs.IsArgosLogLeechers())
			ModLog(GetResString(IDS_ARGOS_XSEXPLOIT), GetClientNickSoft(dwIP));
		break;

	case AR_NICKCHANGER:
		aClient->m_bArgosIsNickChanger = 1;
		aClient->m_uLastDetectionTime = ::GetTickCount();
		if (thePrefs.IsArgosLogLeechers())
			ModLog(GetResString(IDS_ARGOS_NICKCHANGER), GetClientNickSoft(dwIP));
		break;
	case AR_MODCHANGER:
		aClient->m_bArgosIsModChanger = 1;
		aClient->m_uLastDetectionTime = ::GetTickCount();
		if (thePrefs.IsArgosLogLeechers())
			ModLog(GetResString(IDS_ARGOS_MODCHANGER), GetClientNickSoft(dwIP));
		break;

	case AR_NULLNICK:
		aClient->m_bArgosIsNullNick = 1;
		aClient->m_uLastDetectionTime = ::GetTickCount();
		if (thePrefs.IsArgosLogLeechers())
			ModLog(GetResString(IDS_ARGOS_NULLNICK), GetClientNickSoft(dwIP));
		break;

	case AR_SPAM:
		aClient->m_bArgosIsSpam |= 1<<Flag1;
		aClient->m_uLastDetectionTime = ::GetTickCount();
		if (thePrefs.IsArgosLogLeechers())
			ModLog(GetResString(IDS_ARGOS_SPAMER), GetClientNickSoft(dwIP));
		break;

	case AR_BADHELLO:
		aClient->m_bArgosIsBadHello |= Flag1;
		aClient->m_uLastDetectionTime = ::GetTickCount();
		if (thePrefs.IsArgosLogLeechers())
			ModLog(GetResString(IDS_ARGOS_SUSPECTHELLO), GetClientNickSoft(dwIP));
		break;

	case AR_BADPROT:
		aClient->m_bArgosIsBadProt |= Flag1;
		aClient->m_uLastDetectionTime = ::GetTickCount();
		if (thePrefs.IsArgosLogLeechers())
			ModLog(GetResString(IDS_ARGOS_SUSPECTPROT), GetClientNickSoft(dwIP));
		break;

	default:
		ASSERT(0);
	}

	aClient->m_uLastSeenTime = ::GetTickCount();

	CalcPunishment(aClient,dwIP);
}

void CArgos::UnArgos(uint32 dwIP, EArgosReason Reason, UINT Flag1)
{
	TArgosClient* aClient = GetArgosClient(dwIP);
	if(aClient == NULL)
		return;

	switch(Reason){
	case AR_NULL:			// AR_NULL is valid for manual unban, this meen all reasons will be canceled
		aClient->m_bArgosIsLeecherMod = 0;
		aClient->m_bArgosIsLeecherNick = 0;
		aClient->m_bArgosIsLeecherOpCode = 0;
		aClient->m_bArgosIsLeecherHash = 0;

		aClient->m_bArgosIsNickThief = 0;
		aClient->m_bArgosIsModThief = 0;
		aClient->m_bArgosIsHashThief = 0;
		aClient->m_bArgosIsCreditHack = 0;

		aClient->m_bArgosIsHashChanged = 0;

		aClient->m_bArgosIsGhostMode = 0;
		aClient->m_bArgosIsFakeClient = 0;

		aClient->m_bArgosIsAgressiv = 0;
		aClient->m_bArgosIsFileScanner = 0;
		aClient->m_bArgosIsFileFaker = 0;
		aClient->m_bArgosIsCorruptedSender = 0;

		aClient->m_bArgosIsRankFlooder = 0;

		aClient->m_bArgosIsFailed = 0;

		aClient->m_bArgosIsXSExploit = 0;

		aClient->m_bArgosIsNickChanger = 0;
		aClient->m_bArgosIsModChanger = 0;

		aClient->m_bArgosIsNullNick = 0;

		aClient->m_bArgosIsSpam	= 0;

		aClient->m_bArgosIsBadHello = 0;

		aClient->m_bArgosIsBadProt = 0;

		break;

	case AR_LEECHERMOD:		aClient->m_bArgosIsLeecherMod = 0;	break;
	case AR_LEECHERNICK:	aClient->m_bArgosIsLeecherNick = 0;	break;
	case AR_LEECHEROPCODE:	aClient->m_bArgosIsLeecherOpCode = 0; break;
	case AR_LEECHERHASH:	aClient->m_bArgosIsLeecherHash = 0; break;

	case AR_NICKTHIEF:		aClient->m_bArgosIsNickThief = 0;	break;
	case AR_MODTHIEF:		aClient->m_bArgosIsModThief = 0;	break;
	case AR_HASHTHIEF:		aClient->m_bArgosIsHashThief = 0;	break;
	case AR_CREDITHACK:		aClient->m_bArgosIsCreditHack = 0;	break;

	case AR_HASHCHANGED:	aClient->m_bArgosIsHashChanged = 0;	break;

	case AR_GHOSTMODE:		aClient->m_bArgosIsGhostMode = 0;	break;
	case AR_FAKECLIENT:		aClient->m_bArgosIsFakeClient &= ~(1<<Flag1);	break;

	case AR_AGRESSIV:		aClient->m_bArgosIsAgressiv = 0;	break;
	case AR_FILESCANNER:	aClient->m_bArgosIsFileScanner = 0;	break;
	case AR_FILEFAKER:		aClient->m_bArgosIsFileFaker = 0;	break;
	case AR_CORRUPTEDSENDER:aClient->m_bArgosIsCorruptedSender = 0;	break;

	case AR_RANKFLOODER:	aClient->m_bArgosIsRankFlooder = 0;	break;

	case AR_FAILED:			aClient->m_bArgosIsFailed = 0;		break;

	case AR_XSEXPLOIT:		aClient->m_bArgosIsXSExploit = 0;	break;

	case AR_NICKCHANGER:	aClient->m_bArgosIsNickChanger = 0;	break;
	case AR_MODCHANGER:		aClient->m_bArgosIsModChanger = 0;	break;

	case AR_NULLNICK:		aClient->m_bArgosIsNullNick = 0;	break;

	case AR_SPAM:			aClient->m_bArgosIsSpam	= 0;		break;

	case AR_BADHELLO:		if(Flag1) aClient->m_bArgosIsBadHello &= ~Flag1; else aClient->m_bArgosIsBadHello = 0;	break;

	case AR_BADPROT:		aClient->m_bArgosIsBadProt = 0;		break;

	default:
		ASSERT(0);
	}

	if((Reason == AR_LEECHERMOD
	 || Reason == AR_LEECHERNICK
	 || Reason == AR_LEECHEROPCODE) &&
	   (aClient->m_bArgosIsLeecherMod == 0
	 && aClient->m_bArgosIsLeecherNick == 0
	 && aClient->m_bArgosIsLeecherOpCode == 0)
	 ){
 		aClient->m_eLeecherType = LT_NO;
		if(aClient->Comment){
			delete aClient->Comment;
			aClient->Comment = NULL;
		}
	 }

	if(!IsArgos(aClient))
		RemoveArgosClient(dwIP);
	else
		CalcPunishment(aClient,dwIP);
}

bool CArgos::IsArgos(uint32 dwIP, bool bMod){
	if(!thePrefs.UseArgosSystem())
		return false;

	TArgosClient* aClient = GetArgosClient(dwIP);
	if(aClient == NULL)
		return false;

	return IsArgos(aClient, bMod);
}

bool CArgos::IsArgos(TArgosClient* Client, bool bMod){
	if(bMod)
		return (Client->m_bArgosIsLeecherMod || Client->m_bArgosIsLeecherNick || (Client->m_bArgosIsFakeClient & 2) || Client->m_bArgosIsLeecherHash);

	return (
		 Client->m_bArgosIsLeecherMod ||
		 Client->m_bArgosIsLeecherNick ||
		 Client->m_bArgosIsLeecherOpCode ||
		 Client->m_bArgosIsLeecherHash ||

		 Client->m_bArgosIsNickThief ||
		 Client->m_bArgosIsModThief ||
		 Client->m_bArgosIsHashThief ||
		 Client->m_bArgosIsCreditHack ||

		 Client->m_bArgosIsHashChanged ||

		 Client->m_bArgosIsGhostMode ||
		 Client->m_bArgosIsFakeClient ||

		 Client->m_bArgosIsAgressiv ||
		 Client->m_bArgosIsFileScanner ||
		 Client->m_bArgosIsFileFaker ||
		 Client->m_bArgosIsCorruptedSender ||

		 Client->m_bArgosIsRankFlooder ||

		 Client->m_bArgosIsFailed ||

		 Client->m_bArgosIsXSExploit ||

		 Client->m_bArgosIsNickChanger ||
		 Client->m_bArgosIsModChanger ||

		 Client->m_bArgosIsNullNick ||

		 Client->m_bArgosIsSpam ||

		 Client->m_bArgosIsBadHello || 

		 Client->m_bArgosIsBadProt
		 );
}

bool CArgos::IsArgos(uint32 dwIP, EArgosReason Reason, UINT Flag1){

	TArgosClient* aClient = GetArgosClient(dwIP);
	if(aClient == NULL)
		return false;

	switch(Reason){
	case AR_NULL:			return false;

	case AR_LEECHERMOD:		return aClient->m_bArgosIsLeecherMod;
	case AR_LEECHERNICK:	return aClient->m_bArgosIsLeecherNick;
	case AR_LEECHEROPCODE:	return I2B(aClient->m_bArgosIsLeecherOpCode);
	case AR_LEECHERHASH:	return aClient->m_bArgosIsLeecherHash;

	case AR_NICKTHIEF:		return aClient->m_bArgosIsNickThief;
	case AR_MODTHIEF:		return aClient->m_bArgosIsModThief;
	case AR_HASHTHIEF:		return I2B(aClient->m_bArgosIsHashThief);
	case AR_CREDITHACK:		return aClient->m_bArgosIsCreditHack;

	case AR_HASHCHANGED:	return I2B(aClient->m_bArgosIsHashChanged);

	case AR_GHOSTMODE:		return aClient->m_bArgosIsGhostMode;
	case AR_FAKECLIENT:		return I2B(aClient->m_bArgosIsFakeClient & 1<<Flag1);

	case AR_AGRESSIV:		return I2B(aClient->m_bArgosIsAgressiv);
	case AR_FILESCANNER:	return aClient->m_bArgosIsFileScanner;
	case AR_FILEFAKER:		return aClient->m_bArgosIsFileFaker;
	case AR_CORRUPTEDSENDER:return aClient->m_bArgosIsCorruptedSender;

	case AR_RANKFLOODER:	return aClient->m_bArgosIsRankFlooder;

	case AR_FAILED:			return aClient->m_bArgosIsFailed;

	case AR_XSEXPLOIT:		return I2B(aClient->m_bArgosIsXSExploit);

	case AR_NICKCHANGER:	return aClient->m_bArgosIsNickChanger;
	case AR_MODCHANGER:		return aClient->m_bArgosIsModChanger;

	case AR_NULLNICK:		return aClient->m_bArgosIsNullNick;

	case AR_SPAM:			return I2B(aClient->m_bArgosIsSpam);

	case AR_BADHELLO:		return I2B(Flag1 ? (aClient->m_bArgosIsBadHello & Flag1) : aClient->m_bArgosIsBadHello);

	case AR_BADPROT:		return I2B(aClient->m_bArgosIsBadProt);

	default:
		ASSERT(0);
		return false;
	}
}

// Punishment:
// 0 - no punischment
// 1 - 9 Upload Score punischment 10-x/10
// 10 - upload stop
// <= 100 - Full IP Ban!

void CArgos::CalcPunishment(TArgosClient* Client, uint32 dwIP){

	int Punishment = 0;

	UINT DontBan = FALSE;
	if(thePrefs.IsDontBanFriends()){ //removed anti-community ban
		CUpDownClient* user = theApp.clientlist->FindClientByIP(dwIP);
		if(user){
			if(thePrefs.IsDontBanFriends() && user->IsFriend())
				DontBan = thePrefs.IsDontBanFriends();
		}
	}

	if((Client->m_bArgosIsLeecherMod || 
		Client->m_bArgosIsLeecherNick || 
		Client->m_bArgosIsLeecherOpCode || 
		Client->m_bArgosIsLeecherHash || 
		(Client->m_bArgosIsFakeClient & 2)
		) && (DontBan == FALSE)){
		if(Client->m_eLeecherType == LT_BREAKER)
			Punishment = max(Punishment,thePrefs.GetArgosGPLBreakerPunishment());
		else if(Client->m_eLeecherType == LT_HARD)
			Punishment = max(Punishment,thePrefs.GetArgosHardLeecherPunishment());
		else if(Client->m_eLeecherType == LT_NORMAL)
			Punishment = max(Punishment,thePrefs.GetArgosNormalLeecherPunishment());
		else if(Client->m_eLeecherType == LT_SOFT)
			Punishment = max(Punishment,thePrefs.GetArgosSoftLeecherPunishment());
		else if(Client->m_eLeecherType == LT_MOD)
			Punishment = max(Punishment,thePrefs.GetArgosBadModPunishment());
		else 
			{Punishment = 0; ASSERT(0);}
	}


	if(DontBan != TRUE){
		if(Client->m_bArgosIsNickThief)
			Punishment = max(Punishment,thePrefs.GetArgosNickThiefPunishment());

		if(Client->m_bArgosIsModThief)
			Punishment = max(Punishment,thePrefs.GetArgosModThiefPunishment());
		if(Client->m_bArgosIsHashThief)
			Punishment = max(Punishment,thePrefs.GetArgosHashThiefPunishment());
		if(Client->m_bArgosIsCreditHack)
			Punishment = max(Punishment,thePrefs.GetArgosCreditHackPunishment());

		if(Client->m_bArgosIsHashChanged)
			Punishment = max(Punishment,thePrefs.GetArgosHashChangedPunishment());
	}

	if(DontBan == FALSE){
		if(Client->m_bArgosIsGhostMode)
			Punishment = max(Punishment,thePrefs.GetArgosGhostModePunishment());
	}

	if(DontBan != TRUE){
		if(Client->m_bArgosIsFakeClient)
			Punishment = max(Punishment,thePrefs.GetArgosFakeClientPunishment());

		if(Client->m_bArgosIsAgressiv)
			Punishment = max(Punishment,thePrefs.GetArgosAgressonPunishment());
	}

	if(Client->m_bArgosIsFileScanner)
		Punishment = max(Punishment,thePrefs.GetArgosFileScannerPunishment());
	if(Client->m_bArgosIsFileFaker)
		Punishment = max(Punishment,thePrefs.GetArgosFileFakerPunishment());
	if(Client->m_bArgosIsCorruptedSender)
		Punishment = max(Punishment,thePrefs.GetArgosCorruptedSenderPunishment());

	if(DontBan == FALSE){
		if(Client->m_bArgosIsRankFlooder)
			Punishment = max(Punishment,thePrefs.GetArgosRankFlooderPunishment());;
	}

	if(DontBan != TRUE){
		if(Client->m_bArgosIsFailed)
			Punishment = max(Punishment,thePrefs.GetArgosFailedPunishment());

		if(Client->m_bArgosIsXSExploit)
			Punishment = max(Punishment,thePrefs.GetArgosXSExploitPunishment());
	}

	if(DontBan == FALSE){
		if(Client->m_bArgosIsNickChanger)
			Punishment = max(Punishment,thePrefs.GetArgosNickChangerPunishment());
		if(Client->m_bArgosIsModChanger)
			Punishment = max(Punishment,thePrefs.GetArgosModChangerPunishment());

		if(Client->m_bArgosIsNullNick)
			Punishment = max(Punishment,thePrefs.GetArgosNullNickPunishment());

		if(Client->m_bArgosIsSpam)
			Punishment = max(Punishment,thePrefs.GetArgosSpamPunishment());
	}

	if(DontBan != TRUE){
		if(Client->m_bArgosIsBadHello){
			Punishment = max(Punishment,thePrefs.GetArgosBadHelloPunishment());
			if((Client->m_bArgosIsBadHello & 32) && thePrefs.IsArgoseMCryptDetection() == TRUE) // IP ban for these spys
				Punishment = 100;
		}
	}

	if(DontBan != TRUE){
		if(Client->m_bArgosIsBadProt)
			Punishment = max(Punishment,thePrefs.GetArgosBadProtPunishment());
	}

	Client->m_uPunishment = (uint8)Punishment; 

	if(Punishment >= 10){
		CUpDownClient* client = theApp.clientlist->FindClientByIP(dwIP);
		if(client && theApp.uploadqueue->IsDownloading(client) &&
		  (Punishment >= 100 || !(thePrefs.GetCreditCompensation() == 10 && IsNullCompensation(client))))
			theApp.uploadqueue->RemoveFromUploadQueue(client,_T("Argos Banned"));
	}

	if(Punishment >= 100)
		theApp.clientlist->AddBannedClient(dwIP); // Ban IP
	else
		theApp.clientlist->RemoveBannedClient(dwIP); // UnBan IP
}

float CArgos::GetPunishment(const CUpDownClient* Client)
{
	TArgosClient* aClient = GetArgosClient(Client->GetConnectIP());
	if(aClient == NULL)
		return 1.0F;

	uint8 Punishment = aClient->m_uPunishment;
	if(Punishment >= 10){
		if(thePrefs.GetCreditCompensation() == 10 && IsNullCompensation(Client))
			Punishment = 9;
		else
			return 0.0F;
	}

	float Score = (float)(10 - Punishment) / 10;
	if(thePrefs.GetCreditCompensation()){
		float First = Score;
		float Limit = (float)(10 - min(thePrefs.GetCreditCompensation(),9)) / 10;
		Score *= GetCompensation(Client);
		if(First < Limit && Score > Limit)
			return Limit;
		if(Score > 1.0F)
			return 1.0F;
	}

	return Score;
}

bool CArgos::IsNullCompensation(const CUpDownClient* Client){
	return (Client->Credits() && Client->Credits()->GetDownloadedSession() >= PARTSIZE);
}

float CArgos::GetCompensation(const CUpDownClient* Client)
{
	if(!Client->Credits())
		return 1.0F;

	return Client->Credits()->GetArgosScoreRatio(Client->GetConnectIP());
}

void CArgos::CheckClient(CUpDownClient* Client, bool bNotOfficial, bool bInfo)
{
	if(thePrefs.IsArgosNickThiefDetection() && !bInfo)
		CheckForNickThief(Client);

	if(thePrefs.IsArgosModThiefDetection())
		CheckForModThief(Client);

	if(thePrefs.IsArgosFakeClientDetection())
		ArgosCheckSoftware(Client);

	if(thePrefs.IsArgosGhostModDetection())
		ArgosCheckModification(Client, bNotOfficial);
		
	if(thePrefs.IsArgosLeecherModDetection() || thePrefs.IsArgosLeecherNickDetection())
		AddClientToTest(Client);
}

void CArgos::ArgosCheckSoftware(CUpDownClient* Client)
{
	if (
		((Client->GetVersion()>589) && ((Client->GetSourceExchange1Version()>0) || (Client->GetExtendedRequestsVersion()>0)) && (Client->GetClientSoft()==SO_EDONKEY)) //Fake Donkeys
	 || ((Client->GetClientSoft() == SO_EDONKEYHYBRID) &&  ((Client->GetSourceExchange1Version()>0) || (Client->GetExtendedRequestsVersion()>0))) //Fake Hybrids
	 || (Client->SupportSecIdent()!=0 && (Client->GetClientSoft()==SO_EDONKEYHYBRID || Client->GetClientSoft()==SO_EDONKEY)) // this clients don't support sui
	 || (Client->GetVersion() > MAKE_CLIENT_VERSION(0, 30, 0) && Client->GetMuleVersion() > 0 && Client->GetMuleVersion()!=0x99 && Client->GetClientSoft()==SO_EMULE) //Fake emuleVersion
	 || (Client->GetVersion() == MAKE_CLIENT_VERSION(0,44,3) && Client->GetClientModVer().IsEmpty() && Client->GetUnicodeSupport()==utf8strNone && Client->GetClientSoft()==SO_EMULE)
	)
		DoArgos(Client->GetConnectIP(),AR_FAKECLIENT);
	else if (IsArgos(Client->GetConnectIP(),AR_FAKECLIENT))
		UnArgos(Client->GetConnectIP(),AR_FAKECLIENT);
}

void CArgos::ArgosCheckModification(CUpDownClient* Client, bool bNotOfficial)
{
	if (bNotOfficial && Client->GetClientModVer().IsEmpty() 
	 && (Client->GetClientSoft() == SO_EMULE) 
	 && (Client->GetVersion() <= MAKE_CLIENT_VERSION(CemuleApp::m_nVersionMjr, CemuleApp::m_nVersionMin, CemuleApp::m_nVersionUpd))
	 )	
		DoArgos(Client->GetConnectIP(),AR_GHOSTMODE);
	else if (IsArgos(Client->GetConnectIP(),AR_GHOSTMODE) 
	 && Client->GetClientModVer().IsEmpty()) // UnBan only when we got the mod string, ohterwice wait for the tmeout
		UnArgos(Client->GetConnectIP(),AR_GHOSTMODE);
}

void CArgos::CheckForModThief(CUpDownClient* Client)
{	
	CString tocomp = Client->GetClientModVer();
	if(tocomp.IsEmpty())
		return;

	CString OurMod = CString(MOD_NAME); //cache it
	CString OurVersion = StrLine(_T("eMule v%u.%u%c"), VERSION_MJR, VERSION_MIN, _T('a') + VERSION_UPDATE);
	if (StrStrI(tocomp, OurMod) //uses our string
	 && (tocomp.GetLength() != OurMod.GetLength() //but not the correct length
	 || !StrStr(tocomp, OurMod) //but not in correct case		
	 || (Client->GetClientSoftVer() != OurVersion) //or wrong version

	 // our protocol extensions and their valid versions
	 || (Client->GetIncompletePartVersion() != 1) // valid 1

	// we don't support all these protocol extensions
/*
		// our protocol extensions, are thair valid versions
	 || (Client->GetCommentVersion() != 2) // valid 2
	 || (Client->GetIncompletePartVersion()		&& Client->GetIncompletePartVersion() != 2 && Client->GetNeoModProtVersion() != 1) // valid 0 or 2, the new neo sends 1 in the hello and 2 in the mod info
	 || (Client->GetSubChunksVer()				&& Client->GetSubChunksVer() != 1) // valid 0 or 1
	 || (Client->GetHidenPartStatusVersion()	&& Client->GetHidenPartStatusVersion() != 1) // valid 0 or 1
	 || (Client->GetBlockedPartStatusVersion()	&& Client->GetBlockedPartStatusVersion() != 1) // valid 0 or 1
	 || (Client->GetExtraInfoVersion()			&& Client->GetExtraInfoVersion() != 1) // valid 0 or 1
	 || (Client->GetDownloadTimeVersion()		&& Client->GetDownloadTimeVersion() != 1) // valid 0 or 1
	 || (Client->GetL2HACSupport()				&& Client->GetL2HACTime() < MIN2MS(10)) // time should be always above 10 MIN
*/
	 ))
		DoArgos(Client->GetConnectIP(),AR_MODTHIEF);		
	 else if (IsArgos(Client->GetConnectIP(),AR_MODTHIEF))
		UnArgos(Client->GetConnectIP(),AR_MODTHIEF);
}

void CArgos::CheckForNickThief(CUpDownClient* Client){
	CString tocomp = (CString)Client->GetUserName();
	//is he mirroring our current tag?
	if(tocomp.Find(m_sAntiNickThiefTag) != -1)
		DoArgos(Client->GetConnectIP(),AR_NICKTHIEF);
	else if (IsArgos(Client->GetConnectIP(),AR_NICKTHIEF))
		UnArgos(Client->GetConnectIP(),AR_NICKTHIEF);
}

void CArgos::CreateAntiNickThiefTag()
{
	m_sAntiNickThiefTag.Empty();
	int maxchar = 4+rand()%4;
	for(int i = 0; i < maxchar; ++i)
		m_sAntiNickThiefTag.AppendFormat(_T("%c"), ((rand()%2) ? _T('A') : _T('a')) + rand()%25);
}

CString	CArgos::GetAntiNickThiefNick(){
	return StrLine(thePrefs.IsPlainAntiNickThief() ? _T("%s %s") : _T("%s [%s]"), thePrefs.GetUserNick(), m_sAntiNickThiefTag);
}

void CArgos::SUIPassed(CUpDownClient* Client){
	if (IsArgos(Client->GetConnectIP(),AR_HASHTHIEF))
		UnArgos(Client->GetConnectIP(),AR_HASHTHIEF);
}

/////////////////////////////////////////////////////////////////////////////////////////
// Scanner Engine:

UINT AFX_CDECL CArgos::RunProc(LPVOID pParam)
{
	DbgSetThreadName("Argos Engine");

	// BEGIN SLUGFILLER: SafeHash
	CReadWriteLock lock(&theApp.m_threadlock);
	if (!lock.ReadLock(0))
		return 0;
	// END SLUGFILLER: SafeHash

    CArgos* cargos = (CArgos*)pParam;

	return cargos->RunInternal();
}

UINT CArgos::RunInternal()
{
	CTypedPtrList<CPtrList, TTestClient*> TestClientQueue;

	while(doRun){
        QueueLocker.Lock();
        while(!m_TestClientQueue.IsEmpty()) {
            TTestClient* TestClient = m_TestClientQueue.RemoveHead();
            TestClientQueue.AddTail(TestClient);
        }
        QueueLocker.Unlock();

		if(TestClientQueue.IsEmpty())
			Sleep(100);
		
		UINT uDLP = thePrefs.IsDLPDetection();

		threadLocker.Lock();

		TTestClient* TestClient;
		while(!TestClientQueue.IsEmpty())
		{
			TestClient = TestClientQueue.RemoveHead();
			TLeecherInfo* Info = NULL;

			if(uDLP != 2) // DLP may be used exclusiv
				Info = PerformListCheck(TestClient);

			if(uDLP != 0){
				TLeecherInfo* InfoDLP = PerformDLPCheck(TestClient);
				if(InfoDLP && (!Info || InfoDLP->LeecherType < Info->LeecherType)) // use the strongest identification
					Info = InfoDLP;
			}

			if(Info)
			{
				TArgosResult* ArgosResult = new TArgosResult;
				ArgosResult->IP = TestClient->IP;
				ArgosResult->Info = *Info;
				LeecherFound(ArgosResult);
			}
			else if(TestClient->Confirm)
				LeecherNotFound(TestClient->IP);

			if(TestClient->OpCode)
				delete TestClient->OpCode; 
			delete TestClient;
		}

		threadLocker.Unlock();
    }

    QueueLocker.Lock();
	while(!m_TestClientQueue.IsEmpty()){
		TTestClient* TestClient = m_TestClientQueue.RemoveHead();
		if(TestClient->OpCode)
			delete TestClient->OpCode;
        delete TestClient;
	}
    QueueLocker.Unlock();

	threadEndedEvent->SetEvent();

	return FALSE;
}

void CArgos::EndThread()
{
	threadLocker.Lock();
	doRun = false;
	threadLocker.Unlock();

	threadEndedEvent->Lock();
}

TLeecherInfo* CArgos::PerformListCheck(TTestClient* TestClient)
{
	if(TestClient->OpCode)
	{
		for (POSITION pos = KnownLeecherOpcodes.GetHeadPosition(); pos!=NULL; KnownLeecherOpcodes.GetNext(pos))
		{
			TLeecherOpcode* LeecherOpcode = KnownLeecherOpcodes.GetAt(pos);
			bool Found = (TestClient->OpCode->OpCode == LeecherOpcode->OpCode.OpCode
					&& TestClient->OpCode->OpSource == LeecherOpcode->OpCode.OpSource
					&& (LeecherOpcode->OpCode.OpValue == 0 || TestClient->OpCode->OpValue == LeecherOpcode->OpCode.OpValue));

			if (Found && LeecherOpcode->Extra)
				Found = TestExtra(LeecherOpcode->Extra, TestClient);

			if (Found)
				return &LeecherOpcode->Info;
		}
	}
	else{
		for (POSITION pos = KnownLeechers.GetHeadPosition(); pos!=NULL; KnownLeechers.GetNext(pos))
		{
			bool Found = false;
			TKnownLeecher* KnownLeecher = KnownLeechers.GetAt(pos);
			switch (KnownLeecher->Test)
			{
			case TS_MOD:		Found = (StrStrI(TestClient->strMod,  KnownLeecher->String) != NULL); break;
			case TS_MODc:		Found = (StrStr (TestClient->strMod,  KnownLeecher->String) != NULL); break;
			case TS_NICK:		Found = (StrStrI(TestClient->strNick, KnownLeecher->String) != NULL); break;
			case TS_NICKc:		Found = (StrStr (TestClient->strNick, KnownLeecher->String) != NULL); break;
			case TS_SOFTWARE:	Found = (StrStrI(TestClient->strSoft, KnownLeecher->String) != NULL); break;
			case TS_HASH:		Found = (StrStr(md4str(TestClient->abyHash), KnownLeecher->String) != NULL); break;
			default:
				ASSERT(0);
			}

			if (Found && KnownLeecher->Extra)
				Found = TestExtra(KnownLeecher->Extra, TestClient);

			if (Found)
				return &KnownLeecher->Info;
		}
	}
	return NULL;
}

bool CArgos::TestExtra(CExStrList* Extra, TTestClient* TestClient)
{
	TLString* LString;
	for (POSITION curPos = Extra->GetHeadPosition(); curPos!=NULL; Extra->GetNext(curPos))
	{
		LString = &Extra->GetAt(curPos);
		switch (LString->Test)
		{
		case TS_MOD:		if ((StrStrI(TestClient->strMod,  LString->String) != NULL) != LString->Operator) return false; break;
		case TS_MODc:		if ((StrStr (TestClient->strMod,  LString->String) != NULL) != LString->Operator) return false; break;
		case TS_NICK:		if ((StrStrI(TestClient->strNick, LString->String) != NULL) != LString->Operator) return false; break;
		case TS_NICKc:		if ((StrStr (TestClient->strNick, LString->String) != NULL) != LString->Operator) return false; break;
		case TS_SOFTWARE:	if ((StrStrI(TestClient->strSoft, LString->String) != NULL) != LString->Operator) return false; break;
		case TS_HASH:		if ((StrStrI(md4str(TestClient->abyHash), LString->String) != NULL) != LString->Operator) return false; break; 
		default:
			ASSERT(0);
			return false;
		}
	}
	return true;
}

void CArgos::AddClientToTest(CUpDownClient* Client, TOpCode* OpCode) 
{

	if(OpCode && thePrefs.IsArgosGhostModDetection() && (OpCode->OpSource != OC_HELLO && OpCode->OpSource != OC_INFO))
		ArgosCheckModification(Client);

	if(!doRun)
		return;

	TTestClient* TestClient = new TTestClient;

	TestClient->IP		= Client->GetConnectIP();

	TestClient->strNick	= CString(Client->GetUserName());
	TestClient->strMod	= CString(Client->GetClientModVer());
	TestClient->strSoft = CString(Client->GetClientSoftVer());
	if(Client->HasValidHash())
		md4cpy(TestClient->abyHash, Client->GetUserHash());

	TestClient->OpCode	= OpCode;
	TestClient->Confirm	= IsArgos(TestClient->IP,true);

	QueueLocker.Lock();
    m_TestClientQueue.AddTail(TestClient);
	QueueLocker.Unlock();
}

void CArgos::AddClientToTest(uint32 dwIP, TOpCode* OpCode) 
{
	if(!doRun)
		return;

	TTestClient* TestClient = new TTestClient;

	TestClient->IP		= dwIP;

	TestClient->OpCode	= OpCode;

	TestClient->Confirm	= IsArgos(dwIP,true);

	QueueLocker.Lock();
    m_TestClientQueue.AddTail(TestClient);
	QueueLocker.Unlock();
}

void CArgos::LeecherFound(TArgosResult* ArgosResult)
{
	VERIFY( PostMessage(theApp.emuledlg->m_hWnd, TM_ARGOS_RESULT, (WPARAM)ArgosResult, MSG_LEECHER_FOUND) );
}

void CArgos::LeecherNotFound(uint32 dwIP)
{
	VERIFY( PostMessage(theApp.emuledlg->m_hWnd, TM_ARGOS_RESULT, (WPARAM)dwIP, MSG_LEECHER_NOT_FOUND) );
}

void CArgos::LogIdentifiedLeecher(TArgosResult* ArgosResult)
{
	switch (ArgosResult->Info.DetectionLevel){
	case 1: Statistic.Level1Hits++; break;
	case 2: Statistic.Level2Hits++; break;
	case 3: Statistic.Level3Hits++; break;
	}

	if(thePrefs.IsArgosLogLeechers())
		ModLog(GetResString(IDS_ARGOS_LEECHER_FOUND), 
			GetClientNickSoft(ArgosResult->IP),
			ArgosResult->Info.DetectionLevel, 
			GetIdentTypeStr(ArgosResult->Info.Identified), ArgosResult->Info.IdentifiedEx ? GetResString(IDS_ARGOS_IT_EX) : _T(""), 
			GetLeecherTypeStr(ArgosResult->Info.LeecherType),
			ArgosResult->Info.Comment.IsEmpty() ? GetResString(IDS_ARGOS_NA) : ArgosResult->Info.Comment
			);
}

////////////////////////////////////////////////////////////////////////////////////////
// DLP - Dynamic leecher protection
TLeecherInfo* CArgos::PerformDLPCheck(TTestClient* TestClient)
{
	if(m_dlpCheckClient){
	try
	{
			TArgosResult result = m_dlpCheckClient(TestClient->strNick, TestClient->strMod, TestClient->strSoft, TestClient->abyHash, TestClient->OpCode, TestClient->IP);
	if(result.IP == 0)
		return NULL;

	static TLeecherInfo Info; // static Temporary info structure
	Info = result.Info;
	ASSERT(result.Info.LeecherType != LT_NO); // we must have a leecher type on this point...
	return &Info;
}
	catch(...){
		ASSERT(0);
		DebugLogError(_T("ARGOS-DLP: Unknown exception while performing dlp check, dlp.dll may be corrupted..."));
	}
}
	return NULL;
}

bool CArgos::VerifyDLPSignatur()
{
	FILE* readFile = _tfsopen(GetDefaultFilePathDLP(true), _T("r"), _SH_DENYWR);
	if (readFile == NULL)
		return false;

	bool	bPassed = false;

	try{
		TCHAR	szBuffer[512];
		CString Authority;
		uint32	AUTH;

		byte	SIGN[128];
		int		sOffSet = 0;

		bool	bOnSignature = false;

		while (_fgetts(szBuffer,ARRSIZE(szBuffer),readFile) != NULL)
		{
			size_t len = wcslen(szBuffer);
			if(szBuffer[len-1] == '\n'){
				szBuffer[len-1] = 0;
				len--;
			}

			if(StrStr(szBuffer,_T("SIGNATUR BEGIN"))){
				bOnSignature = true;
				Authority.Empty();
				sOffSet = 0;
			}else if(StrStr(szBuffer,_T("SIGNATUR END"))){
				bOnSignature = false;

				for(AUTH = 0; AUTH < ARRSIZE(SignatureAuthorityList); AUTH++){
					if(Authority.CompareNoCase(SignatureAuthorityList[AUTH].Authority) == 0)
						break;
				}
				if(AUTH == ARRSIZE(SignatureAuthorityList)){
					ModLog(LOG_ERROR, GetResString(IDS_ARGOS_DLP_SIGN_FILE_ERROR_AUTH), Authority);
					continue;
				}

				CryptoPP::StringSource ss_Pubkey(SignatureAuthorityList[AUTH].PublikKey, ARRSIZE(SignatureAuthorityList[AUTH].PublikKey), true, 0);
				CryptoPP::RSASSA_PKCS1v15_SHA_Verifier pubkey(ss_Pubkey);

				CryptoPP::VerifierFilter *Verifier = new CryptoPP::VerifierFilter(pubkey);
				Verifier->Put(SIGN, ARRSIZE(SIGN));
				CryptoPP::FileSource dlpFile(CStringA(GetDefaultFilePathDLP()), true, Verifier);

				if((bPassed = (Verifier->GetLastResult() == TRUE)) == false)
					ModLog(LOG_WARNING, GetResString(IDS_ARGOS_DLP_SIGN_FILE_ERROR_SIGN_AUTH), Authority);
				else
					break;
			}
			else if(StrStr(szBuffer,_T("AUTHORITY"))){
				Authority = CString(szBuffer+10).Trim();
			}
			else if(bOnSignature){
				if(sOffSet + DecodeLengthBase16(len) > ARRSIZE(SIGN))
					continue;
				DecodeBase16(szBuffer,len,SIGN+sOffSet,ARRSIZE(SIGN)-sOffSet);
				sOffSet += DecodeLengthBase16(len);
			}
		}
	}catch(...){
		ModLog(LOG_WARNING, GetResString(IDS_ARGOS_DLP_SIGN_FILE_ERROR_PROC));
	}

	fclose(readFile);

	return bPassed;
}

void DLPLoger(int uType,CStringW strMessage)
{
	switch(uType)
	{
	case  1: 
		ModLog(LOG_SUCCESS, GetResString(IDS_ARGOS_DLP_LOG),strMessage); break;
	case -1: 
		ModLog(LOG_ERROR, GetResString(IDS_ARGOS_DLP_LOG),strMessage); break;
	case -2: 
		ModLog(LOG_WARNING, GetResString(IDS_ARGOS_DLP_LOG),strMessage); break;
	case  0: 
	default:
		ModLog(LOG_INFO, GetResString(IDS_ARGOS_DLP_LOG),strMessage);
	}
}

bool CArgos::LoadDLPlibrary()
{
#if !defined(_DEBUG) && !defined(_DEVELOPER)
	if(!VerifyDLPSignatur()){
		ModLog(LOG_ERROR, GetResString(IDS_ARGOS_DLP_SIGN_FILE_ERROR));
		return false;
	}
#endif

	if (!PathFileExists(GetDefaultFilePathDLP())){
		ModLog(LOG_WARNING, GetResString(IDS_ARGOS_DLP_FILE_MISSING));
		return false;
	}

	threadLocker.Lock();

	uint8 uError = 0;

	if(m_dlpInstance)
		UnLoadDLPlibrary();

	try
	{
	m_dlpInstance = ::LoadLibrary(GetDefaultFilePathDLP());
		if(m_dlpInstance)
		{
			DLPGETVERSION dlpGetVersion = (DLPGETVERSION)GetProcAddress(m_dlpInstance,("dlpGetVersion"));
			if(dlpGetVersion && dlpGetVersion() >= 2) // 1 is not compatible anymore
			{
				DLPINITIALISE dlpInitialise	= (DLPINITIALISE)GetProcAddress(m_dlpInstance,("dlpInitialise"));
				if(dlpInitialise)
				{
					const UINT uDetectionLevel	= thePrefs.GetArgosDetectionLevel();
					const UINT uModDetection	= thePrefs.IsArgosLeecherModDetection();
					const UINT uNickDetection	= thePrefs.IsArgosLeecherNickDetection();
					const UINT uHashDetection	= thePrefs.IsArgosLeecherHashDetection();
					UINT uPreferences = 
					//	((u					& 0xff)	<<  24) |
					//	((u					& 0x03)	<<  22) |
					//	((u					& 0x03)	<<  20) |
					//	((u					& 0x03)	<<  18) |
					//	((u					& 0x03)	<<  16) |
					//	((u					& 0x03)	<<  14) |
						((uHashDetection	& 0x03)	<<  12) |
						((uNickDetection	& 0x03)	<<  10) |
						((uModDetection		& 0x03)	<<  8 ) |
					//	((u					& 0x0f)	<<  4 ) |
						((uDetectionLevel	& 0x0f)	<<  0 ) ;

					if(dlpInitialise(uPreferences, &DLPLoger))
					{
						m_dlpCheckClient = (DLPCHECKCLIENT)GetProcAddress(m_dlpInstance,("dlpCheckClient"));
						if(m_dlpCheckClient)
						{
							ModLog(LOG_INFO, GetResString(IDS_ARGOS_DLP_LOADED));
							threadLocker.Unlock();
						return true;
						}
						else
							uError = 1;
					}
					else
						uError = 3;
				}
				else
					uError = 1;
			}
			else
			uError = 1;
	}
		else
			uError = 1;
	
		if(m_dlpInstance)
			::FreeLibrary(m_dlpInstance);
		m_dlpInstance = NULL;
		m_dlpCheckClient = NULL;
	}
	catch(...){
		ASSERT(0);
		uError = 2;
		if(m_dlpInstance){
			HINSTANCE dlpInstance = m_dlpInstance;
			m_dlpInstance = NULL;
			::FreeLibrary(dlpInstance);
		}
		m_dlpCheckClient = NULL;
	}

	ModLog(LOG_ERROR,GetResString(IDS_ARGOS_DLP_LOAD_FAILED),uError == 2 ? GetResString(IDS_ARGOS_FATAL) : uError == 3 ? GetResString(IDS_ARGOS_INTERNAL) : GetResString(IDS_ARGOS_GENERIC));

	threadLocker.Unlock();
	return false;
}

void CArgos::UnLoadDLPlibrary()
{
	if(m_dlpInstance){
		::FreeLibrary(m_dlpInstance);
		ModLog(LOG_INFO, GetResString(IDS_ARGOS_DLP_UNLOADED));
	}

	m_dlpInstance = NULL;
	m_dlpCheckClient = NULL;
}

////////////////////////////////////////////////////////////////////////////////////////
// Known Leecher Data file

bool CArgos::LoadSignaturFile()
{
	HashMap.RemoveAll();

	FILE* readFile = _tfsopen(GetDefaultFilePath(true), _T("r"), _SH_DENYWR);
	if (readFile == NULL)
		return false;

	try{
		TCHAR	szBuffer[512];
		CString Authority;
		CString Authentication;
		uint32	AUTH;
		CryptoPP::SHA512 SHACalculator;
		byte	cBuffer[16];
		TCHAR	Type;
		byte	SIGN[128];
		int		sOffSet = 0;
		byte	FinalHASH[64];
		bool	bPassed = false;

		CMap <CSKey, const CSKey&, TCHAR, TCHAR> tmpHashMap; 

		bool	bInside = false;
		bool	bOnHashMap = false;
		bool	bOnSignature = false;

		while (_fgetts(szBuffer,ARRSIZE(szBuffer),readFile) != NULL)
		{
			size_t len = wcslen(szBuffer);
			if(szBuffer[len-1] == '\n'){
				szBuffer[len-1] = 0;
				len--;
			}

			if(StrStr(szBuffer,_T("AUTHENTICATION BEGIN"))){
				bInside = true;
				bPassed = false;
				Authentication = GetResString(IDS_ARGOS_NONE);
			}else if(StrStr(szBuffer,_T("AUTHENTICATION END"))){
				bInside = false;
				if(bPassed == false)
					ModLog(LOG_ERROR, GetResString(IDS_ARGOS_SIGN_FILE_ERROR_SIGN), Authentication);
				Authentication.Empty();
			} 
			else if(!bInside){
				continue;
			}
			else if(StrStr(szBuffer,_T("ID"))){
				Authentication = CString(szBuffer+3).Trim();
			}else


			if(StrStr(szBuffer,_T("HASHMAP BEGIN"))){
				bOnHashMap = true;
				bPassed = false;
				tmpHashMap.RemoveAll();
			}else if(StrStr(szBuffer,_T("HASHMAP END"))){
				bOnHashMap = false;
				SHACalculator.TruncatedFinal (FinalHASH,64);
			}
			else if(bOnHashMap){
				swscanf(szBuffer,_T("%c"),&Type);
				DecodeBase16(szBuffer+2,min(len-2,32),cBuffer,ARRSIZE(cBuffer));
				SHACalculator.Update ((const byte*)&Type, 1);
				SHACalculator.Update ((const byte*)cBuffer, 16);
				tmpHashMap.SetAt(CSKey(cBuffer),Type);
			}
			else if(bPassed){
				continue;
			}else if(StrStr(szBuffer,_T("SIGNATUR BEGIN"))){
				bOnSignature = true;
				Authority.Empty();
				sOffSet = 0;
			}else if(StrStr(szBuffer,_T("SIGNATUR END"))){
				bOnSignature = false;
				if(!tmpHashMap.IsEmpty())
				{
					for(AUTH = 0; AUTH < ARRSIZE(SignatureAuthorityList); AUTH++){
						if(Authority.CompareNoCase(SignatureAuthorityList[AUTH].Authority) == 0)
							break;
					}
					if(AUTH == ARRSIZE(SignatureAuthorityList)){
						ModLog(LOG_ERROR, GetResString(IDS_ARGOS_SIGN_FILE_ERROR_AUTH), Authority);
						continue;
					}

					CryptoPP::StringSource ss_Pubkey(SignatureAuthorityList[AUTH].PublikKey, ARRSIZE(SignatureAuthorityList[AUTH].PublikKey), true, 0);
					CryptoPP::RSASSA_PKCS1v15_SHA_Verifier pubkey(ss_Pubkey);
					bPassed = pubkey.VerifyMessage(FinalHASH, ARRSIZE(FinalHASH), SIGN, ARRSIZE(SIGN));

					if(bPassed){
						CSKey tmpkey(0);
						POSITION pos = tmpHashMap.GetStartPosition();
						while(pos){
							tmpHashMap.GetNextAssoc(pos, tmpkey, Type);
							HashMap.SetAt(tmpkey,Type);
						}
					}
					else
						ModLog(LOG_WARNING, GetResString(IDS_ARGOS_SIGN_FILE_ERROR_SIGN_AUTH), Authority, Authentication);
				}
			}
			else if(StrStr(szBuffer,_T("AUTHORITY"))){
				Authority = CString(szBuffer+10).Trim();
			}
			else if(bOnSignature){
				if(sOffSet + DecodeLengthBase16(len) > ARRSIZE(SIGN))
					continue;
				DecodeBase16(szBuffer,len,SIGN+sOffSet,ARRSIZE(SIGN)-sOffSet);
				sOffSet += DecodeLengthBase16(len);
			}
		}
	}catch(...){
		ModLog(LOG_WARNING, GetResString(IDS_ARGOS_DLP_SIGN_FILE_ERROR_PROC));
	}

	fclose(readFile);

	return (!HashMap.IsEmpty());
}

/********************************************************************************************************
*		Leechers.dat file Format, rev 0.57 by Xanatos
*		~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* 
* Level, Leecher, Detector 'Comment
*  
* Level - Leecher level, 0 - 3, 0 - entry disabled
* Leecher - is it a real leecher
* 		0 - GPL breakers
* 		1 - hard leecher
* 		2 - normal leecher
* 		3 - soft leecher
* 		4 - bad mod
* Detector - Type:String
* 
* Detector Specyficytions:
* 
* Type: M; detector = Part/Whole Mod String
* Type: m; detector = Chase sensitiv Part/Whole Mod String
* Type: N; detector = Part/Whole Nick Name
* Type: n; detector = Chase sensitiv Part/Whole Nick Name
* Type: S; detector = Part/Whole Software
* Type: H; detector = UserHash
* Type: O; detector = SourcexOpCode&Value
* 
* 		OpCode Sources:
*			1 = Hello Tag
*			2 = Info Tag
*			3 = Packet Opcode
*			4 = Extended Packet Opcode
*			5 = UDP Packet Opcode
*			6 = RAW TCP
*			7 = RAW UDP
*			0 = MOD Info Packet
*			8 = TCP Mod Packet
*			9 = UDP Mod Packet
*		
* 		OpCode Value:
*			Obtional uint32 Value of the tag, only valid for Hello Tag(1) and Info Tag(2)
*
* Type: # - Extended Detector Format: #:Type:String;OperatorType:String;...
* 			Operator: ! - NOT, this is obtional
*			Type: M - Mod String; N - Nick String; S - Software
*				Case Sensitive variant: m - Mod String; n - Nick String
* 
*		Note:	1.) You can use a unlimited number of entries,
*					separated by ';', but the line can't be longer then 1024 chars.
*				2.) To get a positiv identification All entries must be positiv.
*					The NOT Operator meens that if the string is contained the entry is negative! 
*					Note: The NOT operator is not valid for the first entry !!!
*				3.) You can use URL formates chars in the Strings. Example: %59 = ;
*				4.) Type O is only valid for the first entry.
* 
* Comment: Comment is showen in the Argos Client page Page, the comment is obtional
*
* 
*/

bool CArgos::LoadFileInfo(LPCTSTR Path, int &Version, double &RevisionDate, CString* Autor)
{
	FILE* readFile = _tfsopen(Path, _T("r"), _SH_DENYWR);
	if (readFile == NULL)
		return false;

	TCHAR	szBuffer[1024];
	while (_fgetts(szBuffer,ARRSIZE(szBuffer),readFile) != NULL)
	{
		if (szBuffer[0] == '#' || szBuffer[0] == '/' || wcslen(szBuffer) < 4)
			continue;

		if (szBuffer[0] == '*'){
			if(StrStr(szBuffer,_T("AUTOR"))){
				if(Autor)
					*Autor = CString(szBuffer+8).Trim();
			}else if(StrStr(szBuffer,_T("VERSION"))){
				Version = (int)(_tstof(szBuffer+10)*100);
			}else if(StrStr(szBuffer,_T("REVISION"))){
				CString sDate = CString(szBuffer+11).Trim();
				int nDay = _tstoi(sDate.Left(2));
				int nMonth = _tstoi(sDate.Mid(3,2));
				int nYear = _tstoi(sDate.Mid(6,4));
				RevisionDate = COleDateTime(nYear,nMonth,nDay,0,0,0).m_dt;
			}else if(StrStr(szBuffer,_T("+++")))
				break;
		}
	}

	fclose(readFile);

	return true;
}

int CArgos::LoadFromFile()
{
#if !defined(_DEBUG) && !defined(_DEVELOPER)
	if(!LoadSignaturFile()){
		ModLog(LOG_ERROR, GetResString(IDS_ARGOS_SIGN_FILE_ERROR));
		return 0;
	}
#endif

	if (!PathFileExists(GetDefaultFilePath())){
		ModLog(LOG_WARNING, GetResString(IDS_ARGOS_FILE_MISSING));
		return false;
	}

	CString		Autor;
	int			Version = 0;
	double		RevisionDate = 0;
	LoadFileInfo(GetDefaultFilePath(),Version,RevisionDate,&Autor);

	FILE* readFile = _tfsopen(GetDefaultFilePath(), _T("r"), _SH_DENYWR);
	if (readFile == NULL)
		return 0;

	threadLocker.Lock();
	
	RemoveAllKnownLeechers();

	TCHAR		szBuffer[1024];

	CString		FullLine;
	int			ComPos;

	UINT		Level;
	TCHAR		LeecherType;
	CString		FullDetector;

	CString		Detector;
	CString		MainDetector;
	CString		ExDetector;

	TCHAR		szOpCode[2];
	uint8		OpCode[1];
	bool		haveOpValue;
	TCHAR		szOpValue[8];
	uint8		OpValue[4];
	TCHAR		OpSource;

	TCHAR		Type;
	bool		Extended;
	TCHAR		ExType;

	TLeecherInfo Info;

#if !defined(_DEBUG) && !defined(_DEVELOPER)
	UINT		uCodePage = _AtlGetConversionACP();
	char		szBfr[1024];
	int			bftLen;
	CryptoPP::MD5 MD5Calculator;
	byte		MD5[16];
	TCHAR		TypeLimit;
#endif

	int			len;
	int			curPos;
	int			beginPos;

	int			Counter = 0;
	int			SkipCounter = 0;
	int			Line = 0;

	CExStrList*		Extra = NULL;
	TLeecherOpcode* LeecherOpcode = NULL;
	TKnownLeecher*	KnownLeecher = NULL;

	while (_fgetts(szBuffer,ARRSIZE(szBuffer),readFile) != NULL)
	{
		Line++;

		if (szBuffer[0] == '#' || szBuffer[0] == '/' || szBuffer[0] == '*' || wcslen(szBuffer) < 5)
			continue;

		try
		{
			if(swscanf(szBuffer,_T("%d"),&Level) != 1) throw GetResString(IDS_ARGOS_ERROR_FORMAT);
			if(!Level || Level > (UINT)thePrefs.GetArgosDetectionLevel()){
				SkipCounter++;
				continue;
			}

			if (swscanf(szBuffer+2,_T("%c"),&LeecherType) != 1) throw GetResString(IDS_ARGOS_ERROR_FORMAT);
			if (wcslen(szBuffer+3) < 4 ) throw GetResString(IDS_ARGOS_ERROR_FORMAT);
			FullLine = szBuffer+4;

			ComPos = FullLine.ReverseFind('\'');
			if(ComPos != -1){ // Comment found
				FullDetector = FullLine.Left(ComPos).Trim();
				Info.Comment = FullLine.Mid(ComPos+1).Trim();
			}else{
				FullDetector = FullLine.Trim();
				Info.Comment.Empty();
			}

#if !defined(_DEBUG) && !defined(_DEVELOPER)
			bftLen = WideCharToMultiByte(uCodePage, 0, FullDetector, FullDetector.GetLength(), szBfr, ARRSIZE(szBfr), NULL, 0);
			MD5Calculator.Update ((const byte*)szBfr, bftLen);
			MD5Calculator.TruncatedFinal (MD5,16);

			if(!HashMap.Lookup(CSKey(MD5),TypeLimit))
				throw GetResString(IDS_ARGOS_ERROR_ILLEGAL_DETECTOR);
			if(LeecherType < TypeLimit)
				throw GetResString(IDS_ARGOS_ERROR_RESTRICTED_TYPE);
#endif

			Info.LeecherType = GetLeecherType(LeecherType);
			Info.DetectionLevel = Level;

			Type = FullDetector.GetAt(0);

			curPos = FullDetector.Find(KL_BEGIN_MARK);
			if(curPos == -1)
				throw GetResString(IDS_ARGOS_ERROR_FORMAT);
			ASSERT( curPos == 1 );
			Detector = FullDetector.Mid(curPos+1);

			if((Extended = (Type == KL_EXTENDED)) == true)
				Type = Detector.GetAt(0);

			// load only needed entries
			if((!thePrefs.IsArgosLeecherModDetection() && (Type == KL_TYPE_MOD || Type == KL_TYPE_MODc))
			 ||((thePrefs.IsArgosLeecherNickDetection() == 0 || (thePrefs.IsArgosLeecherNickDetection() == 2 && Level != 1)) // obtional tets only importatt nicks even when we have a high detection level
				&& (Type == KL_TYPE_NICK || Type == KL_TYPE_NICKc))
			 ||(!(thePrefs.IsArgosLeecherHashDetection()) && Type == KL_TYPE_HASH)
			 ||(!(thePrefs.IsArgosFakeClientDetection() == TRUE) && Type == KL_TYPE_SOFTWARE)
			 ||(!thePrefs.IsArgosOpcodeDetection() && Type == KL_TYPE_OPCODE)
			 ){
				SkipCounter++;
				continue;
			 }

			if(Extended)
			{
				//Type = Detector.GetAt(0); // moved above
				Info.Identified	= GetIdentType(Type);
				Info.IdentifiedEx = TRUE;

				len = Detector.GetLength();
				curPos = Detector.Find(KL_BEGIN_MARK);
				if(curPos == -1)
					throw GetResString(IDS_ARGOS_ERROR_FORMAT);
				ASSERT( curPos == 1 || curPos == 2 );
				curPos += 1;
				MainDetector = DeCodeString(ParseString(Detector,curPos,KL_FORMAT_EX_SEPARATOR));

				Extra = new CExStrList();
				while(curPos < len)
				{
					ExDetector = ParseString(Detector,curPos,KL_FORMAT_EX_SEPARATOR);

					TLString LString;
					
					ExType = ExDetector.GetAt(0);
					if(ExType == KL_OPERATOR_NOT){
						LString.Operator = false;
						ExType = ExDetector.GetAt(1);
					}else
						LString.Operator = true;

					if((LString.Test = GetTestType(ExType)) == TS_NULL){
						delete Extra;
						throw GetResString(IDS_ARGOS_ERROR_TYPE);
					}

					beginPos = ExDetector.Find(KL_BEGIN_MARK);
					if(beginPos == -1){
						delete Extra;
						throw GetResString(IDS_ARGOS_ERROR_FORMATX);
					}
					ASSERT( beginPos == 1 || beginPos == 2 );
					LString.String = DeCodeString(ExDetector.Mid(beginPos+1));

					Extra->AddTail(LString);
				}
			}
			else{
				Info.Identified	= GetIdentType(Type);
				Info.IdentifiedEx = FALSE;
				Extra = NULL;
				MainDetector = Detector;
			}

			if (Type == KL_TYPE_OPCODE){
				LeecherOpcode = new TLeecherOpcode;
				haveOpValue = (MainDetector.GetLength() > 4);
				if((haveOpValue == false) ? (swscanf(MainDetector, _T("%cx%c%c"), &OpSource, &szOpCode[0], &szOpCode[1]) != 3)
										  : (swscanf(MainDetector, _T("%cx%c%c&%c%c%c%c%c%c%c%c"), &OpSource, &szOpCode[0], &szOpCode[1], 
																			&szOpValue[0], &szOpValue[1], &szOpValue[2], &szOpValue[3], 
																			&szOpValue[4], &szOpValue[5], &szOpValue[6], &szOpValue[7]) != 11)	
				){
					delete LeecherOpcode;
					throw GetResString(IDS_ARGOS_ERROR_FORMAT);
				}
				DecodeBase16(szOpCode,2,OpCode,1);
				LeecherOpcode->OpCode.OpCode = OpCode[0];
				LeecherOpcode->OpCode.OpSource = GetOpSource(OpSource);
				LeecherOpcode->OpCode.OpValue = 0;
				if(haveOpValue){
					DecodeBase16(szOpValue,8,OpValue,4);
					LeecherOpcode->OpCode.OpValue |= (OpValue[3]);
					LeecherOpcode->OpCode.OpValue |= (OpValue[2]<<8);
					LeecherOpcode->OpCode.OpValue |= (OpValue[1]<<16);
					LeecherOpcode->OpCode.OpValue |= (OpValue[0]<<24);
				}
				LeecherOpcode->Extra = Extra;
				Info.DetectedOpCode = LeecherOpcode->OpCode;
				LeecherOpcode->Info = Info;
				KnownLeecherOpcodes.AddTail(LeecherOpcode);
			}else{
				KnownLeecher = new TKnownLeecher;
				KnownLeecher->String = MainDetector;
				if((KnownLeecher->Test = GetTestType(Type)) == TS_NULL){
					delete KnownLeecher;
					throw GetResString(IDS_ARGOS_ERROR_TYPE);
				}
				KnownLeecher->Extra = Extra;
				KnownLeecher->Info = Info;
				KnownLeechers.AddTail(KnownLeecher);
			}
			
			Counter++;
		}
		catch(CString &str){
			ModLog(LOG_ERROR, GetResString(IDS_ARGOS_FILE_ERROR), str, Line);
		}
	}

	threadLocker.Unlock();

	fclose(readFile);

	ModLog(LOG_SUCCESS, GetResString(IDS_ARGOS_FILE_LOADED), Counter, SkipCounter, Autor, COleDateTime(RevisionDate).Format());
	
	return Counter;
}

void CArgos::RemoveAllKnownLeechers()
{
	threadLocker.Lock();

	while(!KnownLeechers.IsEmpty()){
		TKnownLeecher* KnownLeecher = KnownLeechers.RemoveHead();
		if(KnownLeecher->Extra)
			delete KnownLeecher->Extra;
		delete KnownLeecher;
	}

	while(!KnownLeecherOpcodes.IsEmpty()){
		TLeecherOpcode* LeecherOpcode = KnownLeecherOpcodes.RemoveHead();
		if(LeecherOpcode->Extra)
			delete LeecherOpcode->Extra;
		delete LeecherOpcode;
	}

	threadLocker.Unlock();
}

ELeecherType CArgos::GetLeecherType(TCHAR LeecherType){
	switch (LeecherType)
	{
	case KLT_BREAKER: return LT_BREAKER;
	case KLT_HARD: return LT_HARD;
	case KLT_NORMAL: return LT_NORMAL;
	case KLT_SOFT: return LT_SOFT;
	case KLT_MOD: return LT_MOD;
	default: return LT_NO;
	}
}
ETestStr CArgos::GetTestType(TCHAR Type){
	switch (Type)
	{
	case KL_TYPE_MOD: return TS_MOD;
	case KL_TYPE_MODc: return TS_MODc;
	case KL_TYPE_NICK: return TS_NICK;
	case KL_TYPE_NICKc: return TS_NICKc;
	case KL_TYPE_SOFTWARE: return TS_SOFTWARE;
	case KL_TYPE_HASH: return TS_HASH;
	default: return TS_NULL;
	}
}
ELeecherIdent CArgos::GetIdentType(TCHAR Type){
	switch (Type)
	{
	case KL_TYPE_MOD: return IT_MOD;
	case KL_TYPE_MODc: return IT_MOD;
	case KL_TYPE_NICK: return IT_NICK;
	case KL_TYPE_NICKc: return IT_NICK;
	case KL_TYPE_SOFTWARE: return IT_SOFTWARE;
	case KL_TYPE_HASH: return IT_HASH;
	case KL_TYPE_OPCODE: return IT_OPCODE;
	default: return IT_NO;
	}
}
EOpcodeProc CArgos::GetOpSource(TCHAR OpSource)
{
	switch (OpSource)
	{
	case OCS_HELLO: return OC_HELLO;
	case OCS_INFO: return OC_INFO;
	case OCS_PACKET: return OC_PACKET;
	case OCS_PACKET_EX: return OC_PACKET_EX;
	case OCS_PACKET_UDP: return OC_PACKET_UDP;
	case OCS_RAW_TCP: return OC_RAW_TCP;
	case OCS_RAW_UDP: return OC_RAW_UDP;
	case OCS_MODINFO: return OC_MODINFO;	
	case OCS_PACKET_MOD: return OC_PACKET_MOD;
	case OCS_PACKET_MOD_UDP: return OC_PACKET_MOD_UDP;
	default: return OC_NULL;
	}
}

CString CArgos::GetIdentTypeStr(ELeecherIdent Type){
	switch (Type)
	{
	case IT_MOD: return GetResString(IDS_ARGOS_IT_MOD);
	case IT_NICK: return GetResString(IDS_ARGOS_IT_NICK);
	case IT_SOFTWARE: return GetResString(IDS_ARGOS_IT_SOFTWARE);
	case IT_HASH: return GetResString(IDS_ARGOS_IT_HASH);
	case IT_OPCODE: return GetResString(IDS_ARGOS_IT_OPCODE);
	default: return GetResString(IDS_ARGOS_IT_NULL);
	}
}

CString CArgos::GetLeecherTypeStr(ELeecherType Type){
	switch (Type)
	{
	case LT_BREAKER: return GetResString(IDS_ARGOS_LT_BREAKER);
	case LT_HARD: return GetResString(IDS_ARGOS_LT_HARD);
	case LT_NORMAL: return GetResString(IDS_ARGOS_LT_NORMAL);
	case LT_SOFT: return GetResString(IDS_ARGOS_LT_SOFT);
	case LT_MOD: return GetResString(IDS_ARGOS_LT_MOD);
	default: return GetResString(IDS_ARGOS_LT_NULL);
	}
}

CString CArgos::GetArgosString(const CUpDownClient* Client, bool bShort)
{
	TArgosClient* client = GetArgosClient(Client->GetConnectIP());
	if(client == NULL)
		return _T("");

	CString buffer;
	if(bShort){
		buffer.AppendFormat(GetResString(IDS_ARGOS_STRING_SHORT),GetPunishment(Client)
			,(client->m_uPunishment >= 100 ? 0.0F : (float)(10 - client->m_uPunishment) / 10)
			,(client->m_uPunishment >= 100 ? 0.0F : client->m_uPunishment == 10 && (thePrefs.GetCreditCompensation() < 10 || !theApp.argos->IsNullCompensation(Client))) ? 0.0F : theApp.argos->GetCompensation(Client));
	}else{

		if(client->m_bArgosIsLeecherMod){
			if(!buffer.IsEmpty())
				buffer.Append(_T("; "));
			buffer.Append(GetResString(IDS_ARGOS_LEECHER_MOD));
			buffer.Remove(':');
		}
		if(client->m_bArgosIsLeecherNick){
			if(!buffer.IsEmpty())
				buffer.Append(_T("; "));
			buffer.Append(GetResString(IDS_ARGOS_LEECHER_NICK));
			buffer.Remove(':');
		}
		if(client->m_bArgosIsLeecherOpCode){
			if(!buffer.IsEmpty())
				buffer.Append(_T("; "));
			buffer.Append(GetResString(IDS_ARGOS_LEECHER_OPCODE));
			buffer.Remove(':');
		}
		if(client->m_bArgosIsLeecherHash){
			if(!buffer.IsEmpty())
				buffer.Append(_T("; "));
			buffer.Append(GetResString(IDS_ARGOS_LEECHER_HASH));
			buffer.Remove(':');
		}

		if(client->m_bArgosIsNickThief){
			if(!buffer.IsEmpty())
				buffer.Append(_T("; "));
			buffer.Append(GetResString(IDS_ARGOS_NICKTHIEF));
			buffer.Remove(':');
		}
		if(client->m_bArgosIsModThief){
			if(!buffer.IsEmpty())
				buffer.Append(_T("; "));
			buffer.Append(GetResString(IDS_ARGOS_MODTHIEF));
			buffer.Remove(':');
		}
		if(client->m_bArgosIsHashThief){
			if(!buffer.IsEmpty())
				buffer.Append(_T("; "));
			buffer.Append(GetResString(IDS_ARGOS_HASHTHIEF));
			buffer.Remove(':');
		}
		if(client->m_bArgosIsCreditHack){
			if(!buffer.IsEmpty())
				buffer.Append(_T("; "));
			buffer.Append(GetResString(IDS_ARGOS_CREDITHACKSRC));
			buffer.Remove(':');
		}

		if(client->m_bArgosIsHashChanged){
			if(!buffer.IsEmpty())
				buffer.Append(_T("; "));
			buffer.Append(GetResString(IDS_ARGOS_HASHCHANGE));
			buffer.Remove(':');
		}

		if(client->m_bArgosIsGhostMode){
			if(!buffer.IsEmpty())
				buffer.Append(_T("; "));
			buffer.Append(GetResString(IDS_ARGOS_GHOSTMODSRC));
			buffer.Remove(':');
		}
		if(client->m_bArgosIsFakeClient){
			if(!buffer.IsEmpty())
				buffer.Append(_T("; "));
			buffer.Append(GetResString(IDS_ARGOS_FAKE_CLIENTSRC));
			buffer.Remove(':');
		}

		if(client->m_bArgosIsAgressiv){
			if(!buffer.IsEmpty())
				buffer.Append(_T("; "));
			buffer.Append(GetResString(IDS_ARGOS_ASKAGGRESSIVE));
			buffer.Remove(':');
		}
		if(client->m_bArgosIsFileScanner){
			if(!buffer.IsEmpty())
				buffer.Append(_T("; "));
			buffer.Append(GetResString(IDS_ARGOS_FILE_SCANNER));
			buffer.Remove(':');
		}
		if(client->m_bArgosIsFileFaker){
			if(!buffer.IsEmpty())
				buffer.Append(_T("; "));
			buffer.Append(GetResString(IDS_ARGOS_FILE_FAKER));
			buffer.Remove(':');
		}
		if(client->m_bArgosIsCorruptedSender){
			if(!buffer.IsEmpty())
				buffer.Append(_T("; "));
			buffer.Append(GetResString(IDS_ARGOS_BADSENDER));
			buffer.Remove(':');
		}

		if(client->m_bArgosIsRankFlooder){
			if(!buffer.IsEmpty())
				buffer.Append(_T("; "));
			buffer.Append(GetResString(IDS_ARGOS_RANK_FLOODER));
			buffer.Remove(':');
		}

		if(client->m_bArgosIsFailed){
			if(!buffer.IsEmpty())
				buffer.Append(_T("; "));
			buffer.Append(GetResString(IDS_ARGOS_FAILEDSRC));
			buffer.Remove(':');
		}
		if(client->m_bArgosIsXSExploit){
			if(!buffer.IsEmpty())
				buffer.Append(_T("; "));
			buffer.Append(GetResString(IDS_ARGOS_XSEXPLOITSRC));
			buffer.Remove(':');
		}

		if(client->m_bArgosIsNickChanger){
			if(!buffer.IsEmpty())
				buffer.Append(_T("; "));
			buffer.Append(GetResString(IDS_ARGOS_NICKCHANGERSRC));
			buffer.Remove(':');
		}
		if(client->m_bArgosIsModChanger){
			if(!buffer.IsEmpty())
				buffer.Append(_T("; "));
			buffer.Append(GetResString(IDS_ARGOS_MODCHANGERSRC));
			buffer.Remove(':');
		}

		if(client->m_bArgosIsSpam){
			if(!buffer.IsEmpty())
				buffer.Append(_T("; "));
			buffer.Append(GetResString(IDS_ARGOS_SPAM_SRC));
			buffer.Remove(':');
		}

		if(client->m_bArgosIsBadHello){
			if(!buffer.IsEmpty())
				buffer.Append(_T("; "));
			buffer.Append(GetResString(IDS_ARGOS_SUSPECTHELLOSRC));
			buffer.Remove(':');
		}

		if(client->m_bArgosIsBadProt){
			if(!buffer.IsEmpty())
				buffer.Append(_T("; "));
			buffer.Append(GetResString(IDS_ARGOS_SUSPECTPROTSRC));
			buffer.Remove(':');
		}

	}
	return buffer;
}

void CArgos::GetStatistics(int stats[26])
{
	memset(stats, 0, sizeof(stats[0]) * 26);

	POSITION pos = m_ArgosList.GetStartPosition();
	uint32 nKey;
	TArgosClient* pResult;
	while (pos != NULL){
		m_ArgosList.GetNextAssoc(pos, nKey, pResult);

		if(pResult->m_bArgosIsLeecherMod)
			stats[0]++;
		if(pResult->m_bArgosIsLeecherNick)
			stats[1]++;
		if(pResult->m_bArgosIsLeecherOpCode)
			stats[2]++;
		if(pResult->m_bArgosIsLeecherHash)
			stats[3]++;

		if(pResult->m_bArgosIsNickThief)
			stats[4]++;
		if(pResult->m_bArgosIsModThief)
			stats[5]++;
		if(pResult->m_bArgosIsHashThief)
			stats[6]++;
		if(pResult->m_bArgosIsCreditHack)
			stats[7]++;

		if(pResult->m_bArgosIsHashChanged)
			stats[8]++;

		if(pResult->m_bArgosIsGhostMode)
			stats[9]++;
		if(pResult->m_bArgosIsFakeClient)
			stats[10]++;

		if(pResult->m_bArgosIsAgressiv)
			stats[11]++;
		if(pResult->m_bArgosIsFileScanner)
			stats[12]++;
		if(pResult->m_bArgosIsFileFaker)
			stats[13]++;
		if(pResult->m_bArgosIsCorruptedSender)
			stats[14]++;

		if(pResult->m_bArgosIsRankFlooder)
			stats[15]++;

		if(pResult->m_bArgosIsFailed)
			stats[16]++;

		if(pResult->m_bArgosIsXSExploit)
			stats[17]++;

		if(pResult->m_bArgosIsSpam)
			stats[18]++;

		if(pResult->m_bArgosIsBadHello)
			stats[19]++;

		if(pResult->m_bArgosIsBadProt)
			stats[20]++;

		if(pResult->m_bArgosIsNickChanger)
			stats[21]++;

		if(pResult->m_bArgosIsModChanger)
			stats[22]++;

	}

	stats[23] = Statistic.Level1Hits;
	stats[24] = Statistic.Level2Hits;
	stats[25] = Statistic.Level3Hits;
}

CString GetClientNickSoft(uint32 dwIP){
	CUpDownClient* Client = theApp.clientlist->FindClientByIP(dwIP);

	CString UserName = (Client) ? Client->GetUserName() : GetResString(IDS_ARGOS_UNKNOWN_UN);
	CString Software = Client ? Client->DbgGetFullClientSoftVer() : GetResString(IDS_ARGOS_UNKNOWN_SV);

	return StrLine(_T("(%s@%s)"),UserName,Software);
}

CString CArgos::GetDefaultFilePath(bool bSig) const
{
	return thePrefs.GetMuleDirectory(EMULE_CONFIGDIR) + (bSig ? DFLT_KNOWNLEECHER_SIGN_FILENAME : DFLT_KNOWNLEECHER_FILENAME);
}

CString CArgos::GetDefaultFilePathDLP(bool bSig) const
{
	return thePrefs.GetMuleDirectory(EMULE_CONFIGDIR) + (bSig ? DFLT_DLP_SIGN_FILENAME : DFLT_DLP_FILENAME);
}

