//this file is part of NeoMule
//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);
}

bool __declspec(dllexport) dlpCheckClient(TTestClient* client, TArgosResult*& result)
{
	if(uHashDetection && uDetectionLevel <= 2){ // low level
		LPCTSTR reason = DLPx.DLPCheckUserhash(client->abyHash);
		if(reason){
			result = new TArgosResult;
			result->GPLBreaker = true;
			result->Comment = reason;
			return true;
		}
	}

	if(uModDetection && uDetectionLevel <= 2){ // low level
		LPCTSTR reason = DLPx.DLPCheckModstring_Hard(client->strMod,client->strSoft);
		if(reason){
			result = new TArgosResult;
			result->GPLBreaker = true;
			result->Comment = reason;
			return true;
		}
	}

	if(uNickDetection && uDetectionLevel <= 2){  // low level
		LPCTSTR reason = DLPx.DLPCheckUsername_Hard(client->strNick);
		if(reason){
			result = new TArgosResult;
			result->GPLBreaker = false;
			result->Comment = reason;
			return true;
		}
	}

	if(uHashDetection && uDetectionLevel <= 1){ // normal level
		LPCTSTR reason = DLPx.DLPCheckNameAndHashAndMod(client->strNick,md4str(client->abyHash),client->strMod);
		if(reason){
			result = new TArgosResult;
			result->GPLBreaker = false;
			result->Comment = reason;
			return true;
		}
	}

	if(uModDetection && uDetectionLevel == 0){ // high level
		LPCTSTR reason = DLPx.DLPCheckModstring_Soft(client->strMod,client->strSoft);
		if(reason){
			result = new TArgosResult;
			result->GPLBreaker = false;
			result->Comment = reason;
			return true;
		}
	}

	if(uNickDetection && uDetectionLevel == 0){  // high level
		LPCTSTR reason = DLPx.DLPCheckUsername_Soft(client->strNick);
		if(reason){
			result = new TArgosResult;
			result->GPLBreaker = false;
			result->Comment = reason;
			return true;
		}
	}
	
	return false;
}

////////////////////////////////////////////
// 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"));

				DLPCheckMessageSpam = (DLPCHECKMESSAGESPAM)GetProcAddress(dlpInstance,("DLPCheckMessageSpam"));

				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 &&
					DLPCheckMessageSpam &&
					DLPCheckUserhash
					)
				{
					dlpavailable=true;
					LogMessage(1 ,_T("Dynamic Anti-Leecher Protection v %u loaded"), GetDLPVersion());
				}
				else
				{
					LogMessage(-1,_T("failed to initialize the antiLeech.dll"));
					::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;
}
