#include "StdAfx.h"

#include "hlapi_c.h"
#define EYECAST (Word DATAFAR_ *)

extern "C"
{
#include "skeytsr.h"
}
static KEY_NET _Eutron;

#pragma pack(1)
struct TEutronHeader
{ 
  char           _serno[8];
  unsigned short _year_assist;
  unsigned short _max_users;
  unsigned long  _last_date;
  unsigned long  _scad_date;
  unsigned long  _checksum;  // Must be the last item!
};
#pragma pack()

#include "connect.h"
#include "server.h"
#include "tracing.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif



// @doc INTERNAL

// @rdesc Ritorna il valore corrispondente alla chiave appartenente
//        ad una sezione del file di configurazione

CString GetIniString(LPCSTR sec, // @parm Sezione
		 			           LPCSTR key, // @parm Chiave
					           LPCSTR def) // @parm Valore di default
{
	// Nome del file .ini
	static CString m_strIniFile;
	
	// Lunghezza massima di un nome di directory
	// o di un valore del file .ini
	const DWORD dwSize = _MAX_DIR + 1;

	// Costruisce il nome del file .ini se necessario
	if (m_strIniFile.IsEmpty())
	{
		GetCurrentDirectory(dwSize, m_strIniFile.GetBuffer(dwSize));
		m_strIniFile.ReleaseBuffer();
		m_strIniFile += "\\prawin.ini";
	}

	// Legge il valore della chiave nella sezione specificata
	CString tmp;
	char* buf = tmp.GetBuffer(dwSize);
	GetPrivateProfileString(sec, key, def, buf, dwSize, m_strIniFile);
	tmp.ReleaseBuffer();
	return tmp;
}

// @doc EXTERNAL

// @func Permette di criptare una parola
//
// @rdesc Ritorna la stringa criptata
const char* Encode(
  const char* strin, // @parm Stringa da criptare
  char* strout)      // @parm Stringa criptata

  // @xref <f decode>
{
  const char * const encryption_key = "QSECOFR-";
  for (int i = 0; strin[i]; i++)
    strout[i] = strin[i] + (i < 8 ? encryption_key[i] : strin[i-8]);
  strout[i] = '\0';
  return strout; 
}


BOOL TDongleServer::ReadEutronWords(unsigned short reg, 
									unsigned short num,
									unsigned short* data)
{
	ASSERT(num > 0 && num <= 16);
	_Eutron.net_command = NET_KEY_ACCESS;
	memcpy(&_Eutron.command, BLOCK_READING_MODE, 2);
	unsigned short* pointer = (unsigned short*)(&_Eutron.data[0]);
	unsigned short* number  = (unsigned short*)(&_Eutron.data[2]);
	*pointer = reg;
	*number = num;
	smartlink(&_Eutron);
	BOOL ok = _Eutron.status == ST_OK;
	if (ok)
		memcpy(data, &_Eutron.data[4], num*2);
	return ok;
}

BOOL TDongleServer::ReadHardlockWords(unsigned short reg, 
								  	  unsigned short num,
									  unsigned short* data)
{
	BOOL ok = HL_AVAIL() == STATUS_OK;
	if (ok)
	{
		for (unsigned short n = 0; n < num; n++)
			HL_READ(reg+n, EYECAST &data[n]);
		HL_CODE(EYECAST data, 1);
	}
	return ok;
}


BOOL TDongleServer::HardlockLogin()
{
	const char* const REFKEY = "CAMPOKEY";
	const char* const VERKEY = "�pو�c�<";

	unsigned char ref[9]; strcpy((char*)ref, REFKEY);
	unsigned char ver[9]; strcpy((char*)ver, VERKEY);

  const int status = HL_LOGIN(26952, LOCAL_DEVICE, ref, ver);

	Trace(-1, "Login Hardlock %s: %d",
		     status == STATUS_OK ? "OK" : "FALLITO", status);

	_SerNo = 0xFFFF;
  if (status == STATUS_OK)
	{
		Word Val[4] = { 0, 0, 0, 0 }; 
		ReadHardlockWords(0, 4, Val);
		if (Val[0] == 0xFAE8)
			_SerNo = Val[1];
		else
			_SerNo = 0;

		if (_SerNo != 0)
		{
			ReadHardlockWords(60, 4, Val);
			_AssistanceYear = Val[0];
			_MaxUsers = Val[1];

			int index = 0;
			for (unsigned short reg = 48; reg < 60; reg += 4)
			{
				ReadHardlockWords(reg, 4, Val);
        Val[0] ^= _SerNo;
        Val[1] ^= _SerNo;
        Val[2] ^= _SerNo;
				memcpy(&_int_tab[index], Val, 3);
				index += 3;
			}
		}
	}

  return status == STATUS_OK;
}

BOOL TDongleServer::EutronLogin()
{
  BOOL ok = FALSE;
  memset(&_Eutron, 0, sizeof(_Eutron));
  _Eutron.net_command = NET_KEY_OPEN;

  const char* labels[2] = { "AGA.CAMPO", "25EBAI" };
  for (int k = 0; k < 2; k++)
  {
    memset(_Eutron.label, 0, LABEL_LENGTH);
    memcpy(_Eutron.label, labels[k], strlen(labels[k]));  
    memset(_Eutron.password, 0, PASSWORD_LENGTH);
	  if (k == 0)
	  {
		  char pwd[PASSWORD_LENGTH];
		  Encode(labels[k], pwd);
		  memcpy(_Eutron.password, pwd, strlen(pwd));  
	  }
    smartlink(&_Eutron);
    ok = _Eutron.status == ST_OK;
    if (ok)
    {               
	    if (k == 1)
	      _SerNo = 0;
      break;
    }
  }  
  Trace(-1, "Login Eutron %s: %d", ok ? "OK" : "FALLITO", _Eutron.status);

  if (ok && _SerNo != 0)
  { 
	  TEutronHeader eh;
	  if (ReadEutronWords(0, sizeof(TEutronHeader)/2, (unsigned short*)&eh))
	  {
		  char serno[16]; 
		  strncpy(serno, eh._serno, 8);
		  serno[8] = '\0';
		  _SerNo = (unsigned long)atol(serno);
		  if (eh._max_users > 0)
		  {
		    _MaxUsers = eh._max_users;
		    _AssistanceYear = eh._year_assist;  
		    ReadEutronWords(16, 16, _int_tab);
		  }  
	  }  
  }

  return _Eutron.status == ST_OK;
}


BOOL TDongleServer::Login()
{
	BOOL ok = HardlockLogin() || EutronLogin();
	if (ok)
	{
		if (_SerNo == 0 || _MaxUsers == 0)
		{
			const CTime tNow = CTime::GetCurrentTime();
			_AssistanceYear = tNow.GetYear();
			if (_SerNo == 0)
			{
				_MaxUsers = 4;
				memset(_int_tab, 0xFF, sizeof(_int_tab));
			}
			else
			{
				_MaxUsers = 1;
				memset(_int_tab, 0x00, sizeof(_int_tab));
			}
		}

		Trace(-1, "Numero di serie %u", _SerNo);
		Trace(-1, "Anno assistenza %u", _AssistanceYear);
		Trace(-1, "Numero utenti   %u", _MaxUsers);
  
    CString modules = "Moduli attivi: ";
    if (_SerNo > 0 && _SerNo != 0xFFFF)
    {
      for (int n = 0; n < MAX_MODULES; n++)
      {
 				const unsigned short index = n / 16;
				if (index < 4)
        {
					if ((_int_tab[index] >> (n % 16)) & 0x1)
          {
            char buff[24];
            sprintf(buff, "%d ", n+1);
            modules += buff;
          }
        }
      }
    }
    else
    {
      if (_SerNo == 0)
        modules += "Tutti";
      else
        modules += "Nessuno";
    }
    Trace(-1, modules);
	}
	return ok;
}

void TDongleServer::Logout()
{
	if (_Eutron.label[0])
	{
		_Eutron.net_command = NET_KEY_CLOSE;
		smartlink(&_Eutron);
	}
	else
	{
		HL_LOGOUT();
	}
	_SerNo = 0xFFFF;
	_MaxUsers = 0;
	_AssistanceYear = 0;
	memset(_int_tab, 0, sizeof(_int_tab));
}

BOOL TDongleServer::OnConnect(const CString& topic)
{
	BOOL ok = Users() < MaxUsers();
	if (!ok)
		Trace(0, "Refusing Topic %s", (const char*)topic);
	return ok;
}

TConnection* TDongleServer::OnCreateConnection(DWORD id)
{
	TConnection* c = NULL;

  CString strPeer;
  GetPeerName(id, strPeer);
  
  const int n = GetPeerConnections(strPeer);
	if (n > 0 || Peers() < MaxUsers())
	{
		Trace(0, "Connecting %lu from %s", id, strPeer);
		c = new TPrassiConnection(this, id, strPeer);
	}
	else
		Trace(0, "Refusing Connection %lu from %s", id, strPeer);
	return c;
}

BOOL TDongleServer::OnRemoveConnection(DWORD id)
{
  CString strPeer;
  GetPeerName(id, strPeer);
	Trace(0, "Disconnecting %lu from %s", id, strPeer); 
	BOOL ok = TSocketServer::OnRemoveConnection(id);
  return ok;
}

static int CountUsers(TConnection& conn, void* pJolly)
{
	CMapStringToOb& users = *(CMapStringToOb*)pJolly;
	const TPrassiConnection& c = (TPrassiConnection&)conn;
  const CString& name = c.User();
  BOOL ok = name != "******";
  if (ok)
    users.SetAt(name, NULL);
  return ok;
}

unsigned short TDongleServer::Users() const
{
  CMapStringToOb u;
  ((TDongleServer*)this)->ForEachConnection(CountUsers, &u);
  unsigned short nTotal = (unsigned short)u.GetCount();
  return nTotal;
}

static int CountPeerConnections(TConnection& conn, void* pJolly)
{
	const CString& name = *(const CString*)pJolly;
	const TPrassiConnection& c = (TPrassiConnection&)conn;
  return c.PeerName() == name;
}

int TDongleServer::GetPeerConnections(const CString& strPeer) const
{
  int nTotal = ((TDongleServer*)this)->ForEachConnection(CountPeerConnections, (void*)&strPeer);
  return nTotal;
}

static int CountPeers(TConnection& conn, void* pJolly)
{
	CMapStringToOb& users = *(CMapStringToOb*)pJolly;
	const TPrassiConnection& c = (TPrassiConnection&)conn;
  const CString& name = c.PeerName();
  users.SetAt(name, NULL);
  return 1;
}

unsigned short TDongleServer::Peers() const
{
  CMapStringToOb u;
  ((TDongleServer*)this)->ForEachConnection(CountPeers, &u);
  unsigned short nTotal = (unsigned short)u.GetCount();
  return nTotal;
}

BOOL TDongleServer::CanClose() const
{
	BOOL bCanExit = !HasConnections();
	if (!bCanExit)
	{
		int nCode = AfxMessageBox("Ci sono ancora degli utenti collegati:\n"
			                        "Si desidera uscire ugualmente?", 
								  MB_YESNO | MB_ICONQUESTION);
  	bCanExit = nCode == IDYES;
	}
	return bCanExit;
}

TDongleServer::TDongleServer() 
             : TSocketServer("DONGLE")
{
	 _SerNo = 0xFFFF;
	 _MaxUsers = 0;
	 _AssistanceYear = 0;
	 memset(_int_tab, 0, sizeof(_int_tab));
	 Login();
}

TDongleServer::~TDongleServer()
{
	if (_SerNo != 0xFFFF)
		Logout();
}


///////////////////////////////////////////////////////////
// Start/Stop server

static TDongleServer* pDDE = NULL;

BOOL StopServer()
{
	BOOL ok = pDDE != NULL;
	if (ok)
	{
		delete pDDE;
		pDDE = NULL;
	}
	return ok;
}

TDongleServer& GetServer()
{
	ASSERT(pDDE);
	return *pDDE;
}

BOOL StartServer()
{
	BOOL ok = pDDE == NULL;
	if (ok)
		pDDE = new TDongleServer;
	return ok;
}