//this file is part of eMule
//Copyright (C)2006 David Xanatos ( Xanatos@Lycos.at / http://NeoMule.tk )
//
//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 <atlstr.h>
#include "dlp.h"

unsigned char	uDetectionLevel;
unsigned char	uModDetection;
unsigned char	uNickDetection;
unsigned char	uHashDetection;

CDLP DLPx;

void(*Log)(int,CStringW) = NULL;

BOOL WINAPI  DllMain (HANDLE hModule, DWORD dwFunction, LPVOID lpNot)
{
	switch (dwFunction)
	{
		case DLL_PROCESS_ATTACH:
		case DLL_THREAD_ATTACH:
		case DLL_THREAD_DETACH:
		case DLL_PROCESS_DETACH:
			break;
	}

	return TRUE;
}


unsigned int __declspec(dllexport) dlpGetVersion()
{
	return 2;
}

bool __declspec(dllexport) dlpInitialise(UINT preferences, void(*log)(int,CStringW)) {
//	u					= ((preferences >> 24)	& 0xff);
//	u					= ((preferences >> 22)	& 0x03);
//	u					= ((preferences >> 20)	& 0x03);
//	u					= ((preferences >> 18)	& 0x03);
//	u					= ((preferences >> 16)	& 0x03);
//	u					= ((preferences >> 14)	& 0x03);
	uHashDetection		= ((preferences >> 12)	& 0x03);
	uNickDetection		= ((preferences >> 10)	& 0x03);
	uModDetection		= ((preferences >> 8)	& 0x03);
//	u					= ((preferences >> 4)	& 0x0f);
	uDetectionLevel		= ((preferences >> 0)	& 0x0f);

	Log = log; // set log output

	return DLPx.Reload();
}

void md4str(const unsigned char* hash, TCHAR* pszHash)
{
	static const TCHAR _acHexDigits[] = _T("0123456789ABCDEF");
	for (int i = 0; i < 16; i++){
		*pszHash++ = _acHexDigits[hash[i] >> 4];
		*pszHash++ = _acHexDigits[hash[i] & 0xf];
	}
	*pszHash = _T('\0');
}

CString md4str(const unsigned char* hash)
{
	TCHAR szHash[16*2+1];
	md4str(hash, szHash);
	return CString(szHash);
}

TArgosResult __declspec(dllexport) dlpCheckClient(CString strNick, CString strMod, CString strSoft, unsigned char abyHash[16], TOpCode* Opcode, UINT uIP)
{
	TArgosResult Result;
	Result.IP = 0; // 0 indicates no identification
	Result.Info.DetectionLevel = 1;
	Result.Info.IdentifiedEx = FALSE;
	Result.Info.Identified = IT_NO;
	Result.Info.LeecherType = LT_NO;

	if(Opcode){
		LPCTSTR reason = NULL;
		if(Opcode->OpSource == OC_HELLO)
			reason = DLPx.DLPCheckHelloTag(Opcode->OpCode);
		else if(Opcode->OpSource == OC_INFO)
			reason = DLPx.DLPCheckInfoTag(Opcode->OpCode);

		if(reason != NULL){
			Result.Info.DetectedOpCode = *Opcode;
			Result.Info.Comment = reason;
			Result.Info.Identified = IT_OPCODE;
			Result.Info.LeecherType = LT_NORMAL;
			Result.IP = uIP;
			return Result;
		}
	} else {
		if(uHashDetection && uDetectionLevel <= 3){ // low level
			LPCTSTR reason = DLPx.DLPCheckUserhash(abyHash);
			if(reason){
				Result.Info.Comment = reason;
				Result.Info.Identified = IT_HASH;
				Result.Info.LeecherType = LT_HARD;
				Result.IP = uIP;
				return Result;
			}
		}

		if(uModDetection && uDetectionLevel <= 3){ // low level
			LPCTSTR reason = DLPx.DLPCheckModstring_Hard(strMod, strSoft);
			if(reason){
				Result.Info.Comment = reason;
				Result.Info.Identified = IT_MOD;
				Result.Info.LeecherType = LT_HARD;
				Result.IP = uIP;
				return Result;
			}
		}

		if(uNickDetection && uDetectionLevel <= 3){  // low level
			LPCTSTR reason = DLPx.DLPCheckUsername_Hard(strNick);
			if(reason){
				Result.Info.Comment = reason;
				Result.Info.Identified = IT_NICK;
				Result.Info.LeecherType = LT_HARD;
				Result.IP = uIP;
				return Result;
			}
		}

		if(uHashDetection && uDetectionLevel <= 2){ // normal level
			LPCTSTR reason = DLPx.DLPCheckNameAndHashAndMod(strNick, md4str(abyHash), strMod);
			if(reason){
				Result.Info.Comment = reason;
				Result.Info.Identified = IT_HASH;
				Result.Info.LeecherType = LT_HARD;
				Result.IP = uIP;
				return Result;
			}
		}

		if(uModDetection && uDetectionLevel == 1){ // high level
			LPCTSTR reason = DLPx.DLPCheckModstring_Soft(strMod, strSoft);
			if(reason){
				Result.Info.Comment = reason;
				Result.Info.Identified = IT_MOD;
				Result.Info.LeecherType = LT_SOFT;
				Result.IP = uIP;
				return Result;
			}
		}

		if(uNickDetection && uDetectionLevel == 1){  // high level
			LPCTSTR reason = DLPx.DLPCheckUsername_Soft(strNick);
			if(reason){
				Result.Info.Comment = reason;
				Result.Info.Identified = IT_NICK;
				Result.Info.LeecherType = LT_SOFT;
				Result.IP = uIP;
				return Result;
			}
		}
	}

	return Result;
}

////////////////////////////////////////////
// Small helper function by David

void LogMessage(UINT uType, LPCTSTR line, ...)
{
	va_list argptr;
	va_start(argptr, line);
	const size_t bufferSize = 1000;
	TCHAR bufferline[bufferSize];	
	if (_vsnwprintf(bufferline, bufferSize, line, argptr) == -1)
		bufferline[bufferSize - 1] = _T('\0');
	va_end(argptr);	

	if(Log)
		Log(uType,bufferline);
}

//////////////////////////////////////////////////////////////////////
//
// The Part bellow is taken from the Xtreme made by Xman
//

//DLP = Dynamic Leecher Protection
//this code is part of Xtreme-Mod
//author: Xman


CDLP::CDLP(/*CString appdir_in*/)
{
	//get application start directory
	TCHAR buffer[490];
	::GetModuleFileName(0, buffer, 490);
	LPTSTR pszFileName = _tcsrchr(buffer, L'\\') + 1;
	*pszFileName = L'\0';

	appdir = buffer;
	//appdir=appdir_in;

	dlpavailable=false;
	dlpInstance=NULL;
}

CDLP::~CDLP()
{
	if(dlpInstance!=NULL)
	{
		::FreeLibrary(dlpInstance);
	}
}

bool CDLP::Reload()
{
	dlpavailable=false;
	bool waserror=false;

	CString newdll=appdir + _T("antiLeech.dll.new");
	CString newdll2=appdir + _T("config\\") + _T("antiLeech.dll.new");
	CString olddll=appdir + _T("antiLeech.dll.old");
	CString currentdll=appdir + _T("antiLeech.dll");
	CString currentdll2=appdir + _T("config\\") + _T("antiLeech.dll");

	BOOL b2 = FALSE;
	if(PathFileExists(newdll) || (b2 = PathFileExists(newdll2)) != FALSE)
	{
		LogMessage(0,_T("found new version of antiLeech.dll"));
		//new version exists, try to unload the old and load the new one
		if(dlpInstance!=NULL)
		{
			::FreeLibrary(dlpInstance);
			dlpInstance=NULL;
		}
		if(PathFileExists(currentdll))
		{
			if(PathFileExists(olddll))
			{
				if(_tremove(olddll)!=0)
					waserror=true;
			}
			if(waserror==false)
				if(_trename(currentdll,olddll)!=0)
					waserror=true;
		}
		if(waserror==false)
		{
			if(_trename(b2 ? newdll2 : newdll,currentdll)!=0)
				waserror=true;
		}
		if(waserror)
			LogMessage(-2,_T("error during copying the antiLeech.dll's, try to load the old one"));
	}

	if(dlpInstance==NULL)
	{
		if(PathFileExists(currentdll) || PathFileExists(currentdll2))
		{
			if(PathFileExists(currentdll2))
				dlpInstance=::LoadLibrary(currentdll2);
			else
				dlpInstance=::LoadLibrary(currentdll);

			if(dlpInstance!=NULL)
			{
				//testfunc = (TESTFUNC)GetProcAddress(dlpInstance,("TestFunc"));
				GetDLPVersion = (GETDLPVERSION)GetProcAddress(dlpInstance,("GetDLPVersion"));
				DLPCheckModstring_Hard = (DLPCHECKMODSTRING_HARD)GetProcAddress(dlpInstance,("DLPCheckModstring_Hard"));
				DLPCheckModstring_Soft = (DLPCHECKMODSTRING_SOFT)GetProcAddress(dlpInstance,("DLPCheckModstring_Soft"));

				DLPCheckUsername_Hard = (DLPCHECKUSERNAME_HARD)GetProcAddress(dlpInstance,("DLPCheckUsername_Hard"));
				DLPCheckUsername_Soft = (DLPCHECKUSERNAME_SOFT)GetProcAddress(dlpInstance,("DLPCheckUsername_Soft"));

				DLPCheckNameAndHashAndMod = (DLPCHECKNAMEANDHASHANDMOD)GetProcAddress(dlpInstance,("DLPCheckNameAndHashAndMod"));

				DLPCheckUserhash = (DLPCHECKUSERHASH)GetProcAddress(dlpInstance,("DLPCheckUserhash"));

				DLPCheckHelloTag = (DLPCHECKHELLOTAG)GetProcAddress(dlpInstance,("DLPCheckHelloTag"));
				DLPCheckInfoTag = (DLPCHECKINFOTAG)GetProcAddress(dlpInstance,("DLPCheckInfoTag"));
				if( GetDLPVersion &&
					DLPCheckModstring_Hard &&
					DLPCheckModstring_Soft &&
					DLPCheckUsername_Hard &&
					DLPCheckUsername_Soft &&
					DLPCheckNameAndHashAndMod &&
					DLPCheckHelloTag &&
					DLPCheckInfoTag &&
					DLPCheckUserhash
					)
				{
					dlpavailable=true;
					LogMessage(1 ,_T("Dynamic Anti-Leecher Protection v %u loaded"), GetDLPVersion());
				}
				else
				{
					LogMessage(-1,_T("failed to initialize the antiLeech.dll, please use an up tp date version of DLP"));
					::FreeLibrary(dlpInstance);
					dlpInstance=NULL;
				}
			}
			else
			{
				LogMessage(-1,_T("failed to load the antiLeech.dll; ErrorCode: %u"),GetLastError());
			}
		}
		else
		{
			LogMessage(-1,_T("antiLeech.dll not found!"));
			dlpavailable=false;
		}
	}
	else
	{
		LogMessage(0,_T("no new version of antiLeech.dll found. kept the old one"));
		dlpavailable=true;
	}
	return dlpavailable;
}
