#include <cstring>

#include "md5.h"

#ifdef __WIN32__
#define WINDOWS_OS
#else
#define UNIX_OS
#endif

using namespace std;
using namespace security_tools;

int find_index(char * buf, size_t size, const char * tag)
{
   int ptr1,ret=-1;
   for(ptr1 = 0; ptr1 < size; ptr1++) {
      if(strncmp(&buf[ptr1], tag, strlen(tag)) == 0) {
         ret = ptr1;
         break;
      }
   }
   return ret;
}


#ifdef WINDOWS_OS
#include <cstdlib>
#include <windows.h>
#include <wincrypt.h>
#include <fstream>
#include <iostream>

using namespace std;

#define BUFSIZE 1024
#define MD5LEN  16

                                           //9901c615869ef4f4fb315ccfae2d23f8
extern char HACKING_CODE[];

bool get_md5(const char * filename, string & ret)
{
    DWORD dwStatus = 0;
    BOOL bResult = FALSE;
    HCRYPTPROV hProv = 0;
    HCRYPTHASH hHash = 0;
    HANDLE hFile = NULL;
    BYTE rgbFile[BUFSIZE];
    DWORD cbRead = 0;
    BYTE rgbHash[MD5LEN];
    DWORD cbHash = 0;
    CHAR rgbDigits[] = "0123456789abcdef";
    //const char * filename="filename.txt";
    // Logic to check usage goes here.

    ret = "";

    hFile = CreateFile((const char*)filename,
        GENERIC_READ,
        FILE_SHARE_READ,
        NULL,
        OPEN_EXISTING,
        FILE_FLAG_SEQUENTIAL_SCAN,
        NULL);

    if (INVALID_HANDLE_VALUE == hFile)
    {
        dwStatus = GetLastError();
        return dwStatus;
    }

    // Get handle to the crypto provider
    if (!CryptAcquireContext(&hProv,
        NULL,
        NULL,
        PROV_RSA_FULL,
        CRYPT_VERIFYCONTEXT))
    {
        dwStatus = GetLastError();
        CloseHandle(hFile);
        return dwStatus;
    }

    if (!CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash))
    {
        dwStatus = GetLastError();
        CloseHandle(hFile);
        CryptReleaseContext(hProv, 0);
        return dwStatus;
    }

    while (bResult = ReadFile(hFile, rgbFile, BUFSIZE, 
        &cbRead, NULL))
    {
        if (0 == cbRead)
        {
            break;
        }

        char * ptr = (char*)rgbFile;
        size_t index = 0;
        while(memcmp(ptr, "$start$", 7) != 0) {
           ptr++;
           index++;
           if(index >= BUFSIZE) {
              break;
           }
        }

        if(index < BUFSIZE) {
           if(memcmp(ptr, "$start$", 7) == 0) {
              ptr += 7;

              while(memcmp(ptr, "$end$", 5) != 0) {
                 *ptr = '0';
                 ptr++;
                 index++;
                 if(index >= BUFSIZE) {
                    break;
                 }
              }
           }
        }

        if (!CryptHashData(hHash, rgbFile, cbRead, 0))
        {
            dwStatus = GetLastError();
            CryptReleaseContext(hProv, 0);
            CryptDestroyHash(hHash);
            CloseHandle(hFile);
            return dwStatus;
        }
    }

    if (!bResult)
    {
        dwStatus = GetLastError();
        CryptReleaseContext(hProv, 0);
        CryptDestroyHash(hHash);
        CloseHandle(hFile);
        return dwStatus;
    }

    cbHash = MD5LEN;
    if (CryptGetHashParam(hHash, HP_HASHVAL, rgbHash, &cbHash, 0))
    {
        for (DWORD i = 0; i < cbHash; i++)
        {
            ret += (char)rgbDigits[rgbHash[i] >> 4];
            ret += (char)rgbDigits[rgbHash[i] & 0xF];
        }
    }
    else
    {
        dwStatus = GetLastError();
    }

    CryptDestroyHash(hHash);
    CryptReleaseContext(hProv, 0);
    CloseHandle(hFile);

    return !dwStatus; 
}   
#endif

#ifdef UNIX_OS

#include <openssl/md5.h>
#include <unistd.h>
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

bool get_md5(const char * file_name, string & checksum)
{
   int n;
   MD5_CTX c;
   char buf[512];
   ssize_t bytes;
   unsigned char out[MD5_DIGEST_LENGTH];
   bool ret = false;

   checksum = "";

   int fd = open(file_name, O_RDONLY);

   if(fd <= 0) 
      return false;

   MD5_Init(&c);
   int offset = read(fd, buf, 512);
   int ptr1,ptr2;
   while(offset > 0)
   {
      MD5_Update(&c, buf, offset);
      memset(buf, 0, 512);
      offset = read(fd, buf, 512);
      if(offset <= 0) {
         break;
      }
      ret = true;
   }

   close(fd);

   MD5_Final(out, &c);

   for(n=0; n<MD5_DIGEST_LENGTH; n++) {
      memset(buf, 0, 512);
      sprintf(buf, "%02x", out[n]);
      checksum += buf;
   }

   return ret;        
}

#endif

bool security_tools::compute_md5(const string & file_name, string & checksum)
{
   bool ret = false;
   if(get_md5(file_name.c_str(), checksum)) {
      ret = true;
   }
   return ret;
}
