#include "stdafx.h"
#include "UniKeyRegisterer.h"
#include "UniKeyRegistererDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


#include "Helper.h"


//////////////////////////////////////////////////////////////////////////
CUniKeyRegistererDlg::CUniKeyRegistererDlg(CWnd* pParent)
	: CDialog(CUniKeyRegistererDlg::IDD, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
	//m_pDbm = NULL;
	m_bResetClicked = false;
}


//////////////////////////////////////////////////////////////////////////
void CUniKeyRegistererDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_CONNECT_STATE, m_imgConnectState);
	DDX_Control(pDX, IDC_STATE_INFO, m_lStateInfo);
	DDX_Control(pDX, IDC_SID, m_lSID);
	DDX_Control(pDX, IDC_HID, m_lHID);

	DDX_Control(pDX, IDC_T1, m_lLastAccess);
	DDX_Control(pDX, IDC_T2, m_lLastTimeTamper);
	DDX_Control(pDX, IDC_T3, m_lLastConnectTime);

	DDX_Control(pDX, IDOK, m_bOk);
	DDX_Control(pDX, IDC_RESET_BUTTON, m_bReset);
}


//////////////////////////////////////////////////////////////////////////
BEGIN_MESSAGE_MAP(CUniKeyRegistererDlg, CDialog)
	ON_WM_PAINT()
	ON_WM_DESTROY()
	ON_WM_QUERYDRAGICON()
	//}}AFX_MSG_MAP
	ON_BN_CLICKED(IDC_RESET_BUTTON, &CUniKeyRegistererDlg::OnButtonClicked)
	ON_BN_CLICKED(IDOK, &CUniKeyRegistererDlg::OnOkClicked)
END_MESSAGE_MAP()


//////////////////////////////////////////////////////////////////////////
BOOL CUniKeyRegistererDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon


	// change fonts of important statics
	CFont* theFont = new CFont();
	theFont->CreateFont(
		16,                        // nHeight
		0,                         // nWidth
		0,                         // nEscapement
		0,                         // nOrientation
		FW_BOLD,	               // nWeight
		FALSE,                     // bItalic
		FALSE,                     // bUnderline
		0,                         // cStrikeOut
		ANSI_CHARSET,              // nCharSet
		OUT_DEFAULT_PRECIS,        // nOutPrecision
		CLIP_DEFAULT_PRECIS,       // nClipPrecision
		DEFAULT_QUALITY,           // nQuality
		DEFAULT_PITCH | FF_SWISS,  // nPitchAndFamily
		"Arial"); 

	m_lSID.SetFont(theFont, true);
	m_lHID.SetFont(theFont, true);


	// read configuration

	CFile file;	
	if (file.Open("UniKeyRegisterer.cfg", CFile::modeRead))
	{
		m_cfg.sServerUrl = CHelper::ReadLine(file);
		SetWindowText("UniKeyRegisterer - "+m_cfg.sServerUrl);
		file.Close();
	}

	// prepare DB for usage

	/*m_pDbm = new CDatabaseManager(cfg.sServerUrl, cfg.sDBName, cfg.sDBUserName, cfg.sDBPassword);
	if (!m_pDbm->IsOpen())
		exit(0);*/

	m_bReset.EnableWindow(false);

	StartDonglecheckThread();

	//MessageBox(FormatTime("Y-m-d H:i:s",time(0)).GetBuffer());

	return TRUE;
}


//////////////////////////////////////////////////////////////////////////
CString CUniKeyRegistererDlg::FormatTime(const CString &format, const time_t& rawTime)
{
	tm *timeInfo = localtime( &rawTime );
	CString formatCpy = format;
	CString result;

	for (int i=0;i<formatCpy.GetLength();i++)
	{
		char ch = formatCpy.GetBuffer()[i];

		if (ch=='Y')
		{
			int year = 1900+timeInfo->tm_year;
			char buf[5] = {year/1000+'0', (year/100)%10+'0', (year/10)%10+'0', year%10+'0', 0};
			result += (char*)&buf;
		}
		else if (ch=='m')
		{
			int mon = 1+timeInfo->tm_mon;
			char buf[3] = {mon/10+'0', mon%10+'0', 0};
			result += (char*)&buf;
		}
		else if (ch=='d')
		{
			char buf[3] = {timeInfo->tm_mday/10+'0', timeInfo->tm_mday%10+'0', 0};
			result += (char*)&buf;
		}
		else if (ch=='H')
		{
			char buf[3] = {timeInfo->tm_hour/10+'0', timeInfo->tm_hour%10+'0', 0};
			result += (char*)&buf;
		}
		else if (ch=='i')
		{
			char buf[3] = {timeInfo->tm_min/10+'0', timeInfo->tm_min%10+'0', 0};
			result += (char*)&buf;
		}
		else if (ch=='s')
		{
			char buf[3] = {timeInfo->tm_sec/10+'0', timeInfo->tm_sec%10+'0', 0};
			result += (char*)&buf;
		}
		else
			result += ch;
	}

	return result;
}


//////////////////////////////////////////////////////////////////////////
unsigned long CUniKeyRegistererDlg::DonglecheckThreadEntry(LPVOID data)
{
	CUniKeyRegistererDlg* pDlg = (CUniKeyRegistererDlg*)data;

	pDlg->m_bQuitDonglecheckThread = false;
	do
	{
		if (!pDlg->m_bQuitDonglecheckThread)
			pDlg->UpdateConnectionStateView();
		Sleep(500);
	}
	while (!pDlg->m_bQuitDonglecheckThread);

	pDlg->m_bQuitDonglecheckThread = false;
	return 0;
}


//////////////////////////////////////////////////////////////////////////
void CUniKeyRegistererDlg::StartDonglecheckThread()
{
	DWORD dwThreadID;
	CreateThread(NULL, 0, (PTHREAD_START_ROUTINE)&CUniKeyRegistererDlg::DonglecheckThreadEntry, this, 0, &dwThreadID);
}


//////////////////////////////////////////////////////////////////////////
void CUniKeyRegistererDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}


//////////////////////////////////////////////////////////////////////////
void CUniKeyRegistererDlg::OnDestroy()
{
	m_bQuitDonglecheckThread = true;
	while (m_bQuitDonglecheckThread)
		Sleep(200);
	exit(0);

	/*if (m_pDbm)
	{
		delete m_pDbm;
		m_pDbm = NULL;
	}*/
}


//////////////////////////////////////////////////////////////////////////
HCURSOR CUniKeyRegistererDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}


//////////////////////////////////////////////////////////////////////////
int CUniKeyRegistererDlg::ConnectToCE2Server()
{
	SOCKADDR_IN srvAddr;

	srvAddr.sin_addr.s_addr = inet_addr(m_cfg.sServerUrl);
	srvAddr.sin_port = htons(80);
	srvAddr.sin_family = AF_INET;

	WSADATA wsa;
	if(WSAStartup(MAKEWORD(2,0), &wsa) != 0)
		return 1;

	if ((m_ce2ServerSocket = socket(AF_INET, SOCK_STREAM, 0)) == -1)
		return 2;

	if(connect(m_ce2ServerSocket, (struct sockaddr *)&srvAddr, sizeof(srvAddr)) == -1)
		return 3;

	return 0;
}


//////////////////////////////////////////////////////////////////////////
std::string CUniKeyRegistererDlg::GetValue(const std::string& source, const std::string& key)
{
	std::string subSource = source;

	int tagBegin = source.find(key);
	if (tagBegin<0)
		return "";

	subSource = source.substr(tagBegin);
	int tagMiddle = subSource.find("=");
	if (tagMiddle<0)
		return "";	

	subSource = subSource.substr(tagMiddle+1);
	int tagEnd = subSource.find(";");
	if (tagEnd<0)
		return "";

	return subSource.substr(0, tagEnd);
}


//////////////////////////////////////////////////////////////////////////
bool CUniKeyRegistererDlg::GetGUIDFromDB(DWORD hid, CString &res)
{
	// Is dongle HID already contained in DB?

	CString sHid = CHelper::ConvertToString(hid);

	/*CRecordset* pRs = m_pDbm->Query("SELECT GUID FROM SF_LICENSE_KEY_MAPPING WHERE DongleHID = "+sHid+";");
	CString guid = "";

	int n = pRs->GetODBCFieldCount( );
	while( !pRs->IsEOF() ) 
	{
		for( int i = 0; i < n; i++ ) 
			pRs->GetFieldValue( i, guid );
		pRs->MoveNext();
	}
	pRs->Close();
	delete pRs;*/

	int conRes;
	if ((conRes=ConnectToCE2Server())!=0)
		return false;

	char buffer[80096];
	CString req = "GET /comm.php?fkt=GetGUID&hid="+sHid+" HTTP/4.01\nHost: "+m_cfg.sServerUrl+"\n\n";

	int bytes = send(m_ce2ServerSocket, req.GetBuffer(), req.GetLength(), 0);
	if(bytes == -1)
		return false;

	bytes = recv(m_ce2ServerSocket, buffer, sizeof(buffer) - 1, 0);
	if (bytes == -1)
		return false;

	buffer[bytes] = '\0';
	int len = strlen(buffer);

	res = CString(GetValue(buffer, "GUID").c_str());
	return true;
}


//////////////////////////////////////////////////////////////////////////
bool CUniKeyRegistererDlg::RegisterDongleToDB(DWORD hid, CString& guid)
{
	CString sHid = CHelper::ConvertToString(hid);

	/*if (GetGUIDFromDB(hid)=="")
		m_pDbm->Execute("INSERT INTO SF_LICENSE_KEY_MAPPING (DongleHID) VALUES ("+sHid+")");

	m_pDbm->Execute("UPDATE SF_LICENSE_KEY_MAPPING SET GUID='"+guid+"', IsUsed=NULL WHERE DongleHID="+sHid);*/

	int res;
	if ((res=ConnectToCE2Server())!=0)
		return false;

	CString req = "GET /comm.php?fkt=AddHID&hid="+sHid+"&guid="+guid+" HTTP/1.1\nHost: "+m_cfg.sServerUrl+"\n\n";

	int bytes = send(m_ce2ServerSocket, req.GetBuffer(), req.GetLength(), 0);
	if(bytes == -1)
		return false;

	return true;
}


//////////////////////////////////////////////////////////////////////////
CString CUniKeyRegistererDlg::PrepareDongleForDelivery(CUniKeyManager& ukm)
{
	CString guid = CHelper::GetGUID();

	// clear dongle and store sid to it
	ukm.RefreshKey();
	ukm.StoreData(guid.GetBuffer());

	return guid;
}


//////////////////////////////////////////////////////////////////////////
bool CUniKeyRegistererDlg::DongleIsInitialized(CUniKeyManager& ukm)
{
	DWORD hHid = ukm.GetHID();
	
	CString guid;
	if (!GetGUIDFromDB(hHid,guid))
	{
		MessageBox("Connection to Database failed!","Error");
		exit(1);
	}

	if (guid.GetLength()==0)
		return false;

	CString data = CString(ukm.ReadData());

	return data == guid;
}


//////////////////////////////////////////////////////////////////////////
void CUniKeyRegistererDlg::UpdateConnectionStateView()
{
	HICON greenIcon = (HICON)LoadImage( AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_GREEN), IMAGE_ICON, 0, 0, 0);
	HICON redIcon = (HICON)LoadImage( AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_RED), IMAGE_ICON, 0, 0, 0);

	CUniKeyManager ukm;

	// If dongle has a standard login, it has been plugged the first time.
	// Produce the Crytek specific connection values (SEED=123) then.
	if (ukm.Connect(1234, 1234, 1234, 1234))
	{
		m_bOk.EnableWindow(FALSE);

		m_imgConnectState.SetIcon(redIcon);
		m_bReset.EnableWindow(false);
		m_lHID.SetWindowText("0x"+CHelper::ToHex(ukm.GetHID()));
		ClearDongleStateUI();
		m_lStateInfo.SetWindowText("Preparing new dongle...");

		ukm.RequestConnectCode(123); // ATTENTION: This Function can only be called twice per dongle!
		ukm.Disconnect();
	}

	// Crytek specific connection to dongle
	if (ukm.Connect(31128, 56235, 22924, 14016))
	{
		DWORD hHid = ukm.GetHID();
		CString guid;
		if (!GetGUIDFromDB(hHid,guid))
		{
			MessageBox("Connection to Database failed!","Error");
			exit(1);
		}

		m_lHID.SetWindowText("0x"+CHelper::ToHex(hHid));

		if (!DongleIsInitialized(ukm) || m_bResetClicked)
		{
			m_bResetClicked = false;

			m_bOk.EnableWindow(FALSE);

			m_imgConnectState.SetIcon(redIcon);			
			ClearDongleStateUI();
			m_lStateInfo.SetWindowText("Writing content to dongle...");
			guid = PrepareDongleForDelivery(ukm);

			m_lStateInfo.SetWindowText("Registering dongle...");

			if (!RegisterDongleToDB(hHid, guid))
				MessageBox("Registration to Database failed!","Error");
			ukm.Disconnect();			
		}

		//if (ukm.CheckTimeTamper())
		//	MessageBox("Tampered","",MB_OK);


		m_bOk.EnableWindow(TRUE);

		m_imgConnectState.SetIcon(greenIcon);
		m_bReset.EnableWindow(true);
		m_lStateInfo.SetWindowText("Dongle connected.");

		m_lSID.SetWindowText(guid);
		m_lLastAccess.SetWindowText(ukm.FormatTime("d.m.Y - H:i", ukm.GetDynamicData().lastAccessDate));
		m_lLastTimeTamper.SetWindowText(ukm.FormatTime("d.m.Y - H:i", ukm.GetDynamicData().lastTimeTamper));
		m_lLastConnectTime.SetWindowText(ukm.FormatTime("d.m.Y - H:i", ukm.GetDynamicData().lastServerConnect));

		return;
	}

	m_bOk.EnableWindow(TRUE);

	m_imgConnectState.SetIcon(redIcon);
	m_bReset.EnableWindow(false);
	m_lStateInfo.SetWindowText("No dongle connected.");
	m_lHID.SetWindowText("");
	ClearDongleStateUI();
}


//////////////////////////////////////////////////////////////////////////
void CUniKeyRegistererDlg::ClearDongleStateUI()
{
	m_lSID.SetWindowText("");
	m_lLastAccess.SetWindowText("");
	m_lLastTimeTamper.SetWindowText("");
	m_lLastConnectTime.SetWindowText("");
}


//////////////////////////////////////////////////////////////////////////
void CUniKeyRegistererDlg::OnButtonClicked()
{
	m_bResetClicked = true;
	m_bReset.EnableWindow(false);
}


//////////////////////////////////////////////////////////////////////////
void CUniKeyRegistererDlg::OnOkClicked()
{
	exit(0);
}
