#include "BaseServ.h"
#include "Dongle.h"

///////////////////////////////////////////////////////////
// TBit_array
///////////////////////////////////////////////////////////

TBit_array::TBit_array(size_t size) 
          : _bit(NULL), _size(0)
{
  if (size)
    resize(index(size));
}

void TBit_array::copy(const TBit_array& ba)
{
  if (_bit)
  {
    delete _bit;
    _bit = NULL;
    _size = 0;
  }
  resize(ba._size-1);
  memcpy(_bit, ba._bit, _size);
}

TBit_array::TBit_array(const TBit_array& ba) : _bit(NULL), _size(0)
{ copy(ba); }

TBit_array& TBit_array::operator=(const TBit_array& ba)
{
  copy(ba);
  return *this;
}

TBit_array::~TBit_array()
{
  if (_bit)
    delete _bit;
}

void TBit_array::set()
{
  if (_bit) 
    memset(_bit, 0xFF, _size);
}

void TBit_array::reset()
{
  if (_bit) 
    memset(_bit, 0x0, _size);
}

void TBit_array::resize(size_t size)
{
  const size_t oldsize = _size;
  unsigned char* oldbit = _bit;

  _size = size+1;
  _bit = new unsigned char[_size];
  reset();

  if (oldsize)
  {
    memcpy(_bit, oldbit, oldsize);
    delete oldbit;
  }
}

bool TBit_array::operator[] (size_t n) const
{
  const size_t i = index(n);
  if (i >= _size) return false;
  return (_bit[i] & mask(n)) != 0;
}

TBit_array& TBit_array::operator|= (const TBit_array& ba)
{
  if (_size < ba._size)
    resize(ba._size);

  for (size_t i = 0; i < _size; i++)
    _bit[i] |= ba._bit[i];

  return *this;
}

void TBit_array::set(size_t n)
{
  const size_t i = index(n);
  if (i >= _size)
    resize(i);
  _bit[i] |= mask(n);
}

void TBit_array::reset(size_t n)
{
  const size_t i = index(n);
  if (i < _size)
    _bit[i] &= ~mask(n);
}

void TBit_array::not(size_t n)
{
  const size_t i = index(n);
  if (i >= _size) resize(i);
  _bit[i] ^= mask(n);
}

size_t TBit_array::ones() const
{
  size_t one = 0;
  for (size_t i = 0; i < _size; i++)
  {
    const unsigned long b = _bit[i];
    if (b)
    {
      for (unsigned long m = 1; m; m <<= 1)
        if (b & m) one++;
    }
  }
  return one;
}

long TBit_array::last_one() const
{
  const long bits = sizeof(unsigned char); 
  for (size_t i = _size; i--;)
  {
    const unsigned long b = _bit[i];
    if (b)
    {
      for (int j = bits; j--;)
        if ((1<<j) & b) return (bits * i) + j;
    }
  }
  return -1;
}

long TBit_array::first_one() const
{
  const long bits = sizeof(unsigned char); 
  for (size_t i = 0; i < _size; i++)
  {
    const unsigned long b = _bit[i];
    if (b)
    {
      for (int j = 0; j < bits; j++)
        if ((1<<j) & b) return (bits*i)+j;
    }
  }
  return -1;
}

bool TBit_array::ok() const
{
  return _bit != NULL && _size > 0;
}

///////////////////////////////////////////////////////////
// Date utilities
///////////////////////////////////////////////////////////

wxDateTime julian2date(long julian)
{
  long x, z, m, d, y;
  const long daysPer400Years = 146097L;
  const long fudgedDaysPer4000Years = 1460970L + 31;

  x = julian + 68569L;
  z = 4 * x / daysPer400Years;
  x = x - (daysPer400Years * z + 3) / 4;
  y = 4000 * (x + 1) / fudgedDaysPer4000Years;
  x = x - 1461 * y / 4 + 31;
  m = 80 * x / 2447;
  d = x - 2447 * m / 80;
  x = m / 11;
  m = m + 2 - 12 * x;
  y = 100 * (z - 49) + y + x;

	wxDateTime date;
	date.SetYear(y);
	date.SetMonth((wxDateTime::Month)m);
	date.SetDay(d);
  return date;
}

long date2julian(const wxDateTime& date)
{                      
  const int d = date.GetDay(), m = date.GetMonth()+1, y = date.GetYear();
  
  return (long)(d - 32076)
    + 1461L * (y + 4800L + (m - 14) / 12) / 4
      + 367 * ( m - 2 - (m - 14) / 12 * 12) / 12
        - 3 * ((y + 4900L + (m - 14) / 12) / 100) / 4
          + 1;
}

long date2long(const wxDateTime& date)
{
	long n = date.GetYear()*10000 + (date.GetMonth()+1)*100 + date.GetDay();
	return n;
}

///////////////////////////////////////////////////////////
// Utilities
///////////////////////////////////////////////////////////

const char* const encryption_key = "QSECOFR-";

wxString encode(const wxChar* data)
{
	wxString tmp;
	wxChar* buf = tmp.GetWriteBuf(80);
  for (int i = 0; data[i]; i++)
    buf[i] = data[i] + (i < 8 ? encryption_key[i] : data[i - 8]);
  buf[i] = '\0';
	tmp.UngetWriteBuf();
  return tmp; 
}

wxString decode(const char* data)
{
	wxString tmp;
	wxChar* buf = tmp.GetWriteBuf(80);
  for (int i = 0; data[i]; i++)
    buf[i] = data[i] - (i < 8 ? encryption_key[i] : buf[i - 8]);
  buf[i] = '\0';
	tmp.UngetWriteBuf();
  return tmp; 
}

///////////////////////////////////////////////////////////
// Hardlock stuff
///////////////////////////////////////////////////////////

#include "hlapi_c.h"

#define USERADR    26952
#define AGAADR     26953
#define PRASSIADR  26954
#define PROCOMADR  26955
#define REFKEY     (unsigned char*)"CAMPOKEY"
#define VERKEY     (unsigned char*)"�pو�c�<"


///////////////////////////////////////////////////////////
// Smartkey stuff
///////////////////////////////////////////////////////////

#include "skeylink.h"
static KEY_NET* _eutron_key = NULL;

#pragma pack(push, 1)

struct TEutronHeader
{ 
  char           _serno[8];             //  8
  unsigned short _year_assist;          // 10
  unsigned short _max_users;            // 12
  unsigned long  _last_date;            // 16
  unsigned long  _scad_date;            // 20
  unsigned long  _checksum;             // 24 
  unsigned short _version;              // 26
  unsigned short _patch;                // 28
  unsigned short _offset_to_bits;       // 30
  unsigned short _size_of_bits;         // 32
};

struct TEutronFooter
{                
  unsigned long _size;                  // Should be sizeof(TEutronFooter)
  unsigned long _checksum;              // Much smarter position than header
  unsigned long _filler1;
  unsigned long _filler2;
  unsigned long _filler3;
  unsigned long _filler4;
  unsigned long _filler5;
  unsigned long _last_assist;                   // Last date of assistance query
  unsigned long _assistance[MAX_DONGLE_ASSIST]; // Pre-payed assistance
  
  unsigned long checksum(bool set);
  bool valid();
  TEutronFooter();
};

#pragma pack(pop)

TEutronFooter::TEutronFooter()
{
  const int s = sizeof(TEutronFooter);
  memset(&_size, 0, s);
  _size = s;
}

unsigned long TEutronFooter::checksum(bool set)
{
  if (set) _size = sizeof(TEutronFooter);

  const unsigned short offset = sizeof(_size) + sizeof(_checksum);
  unsigned char* ptr = (unsigned char*)(&_size) + offset;
  const unsigned short len = unsigned short(_size - offset);
  
  unsigned long cs = 0;
  for (unsigned short i = 0; i < len; i++, ptr++)
    cs += *ptr | ~(short(*ptr << 8));
  if (set) _checksum = cs;
  return cs;  
}

bool TEutronFooter::valid() 
{
  if (_size == 0 || _checksum == 0)
    return false;
  return _checksum == checksum(false);
}

///////////////////////////////////////////////////////////
// Bit helper functions
///////////////////////////////////////////////////////////

inline bool test_bit(unsigned short w, int b)
{                            
  bool on = (w & (1 << b)) != 0;
  return on;
}

inline void set_bit(unsigned short& w, int b, bool on = true)
{                        
  if (on)  
    w |= 1 << b;
  else  
    w &= ~(1 << b);
}

inline void reset_bit(unsigned short& w, unsigned char b)
{                        
  w &= ~(1 << b);
}

///////////////////////////////////////////////////////////
// TDongle
///////////////////////////////////////////////////////////

TDongle::TDongle() 
       : _hardware(_dongle_unknown), _type(_no_dongle), _serno(0xFFFF), 
         _dirty(false), _max_users(1), _year_assist(2002)
{
  memset(_eprom, 0, sizeof(_eprom));
}  

TDongle::~TDongle() 
{ 
  if (_serno != 0xFFFF)
    Logout(); 
}

// Data punta ad un array di 4 words 
// Deve essere cosi' per problemi del C,
// non trasformare in array: pena di morte! 
void TDongle::garble(unsigned short* data) const
{   
  switch (_hardware)
  {
  case _dongle_hardlock:
    HL_CODE(data, 1);
    break;
  case _dongle_eutron:
    if (_eutron_key)
    {   
      _eutron_key->net_command = NET_KEY_ACCESS;
      _eutron_key->command = SCRAMBLING_MODE;
      memcpy(_eutron_key->data, data, 8);
      smartlink(_eutron_key);
      if (_eutron_key->status == ST_OK)
        memcpy(data, _eutron_key->data, 8);
    }  
    break;
  default:
    break;  
  }  
}

bool TDongle::already_programmed() const
{     
  if (_hardware == _dongle_hardlock)
  {
    unsigned short data[4];
    memcpy(data, &_eprom[60], sizeof(data));
    garble(data);
    
    if (data[0] < 2000 || data[0] > 3000)
      return false;
      
    if (data[1] == 0 || data[1] >= 10000)  
      return false;
    const long giulio = *((const long*)&data[2]);
    const wxDateTime date = julian2date(giulio);
    if (date.GetYear() < 2000 || date > wxDateTime::Now())
      return false;
  } else
  if (_hardware == _dongle_eutron)
  {
    const TEutronHeader* eh = (const TEutronHeader*)_eprom;
    if (eh->_serno[0] == 0 || eh->_checksum == 0)
      return false;   // Really virgin.
      
    unsigned long cs = 0;
    for (byte* ptr = (byte*)_eprom; ptr < (byte*)&eh->_checksum; ptr++)
      cs += *ptr | ~(short(*ptr << 8));
    if (eh->_checksum != cs)  
      return false;   // Malicious programming!
  }   

  return true;  
}
           
bool TDongle::hardlock_login(bool test_all_keys)
{
  bool ok = true;
  _type = _user_dongle;
  if (test_all_keys)
  {
    HL_LOGOUT();
    if (HL_LOGIN(AGAADR, LOCAL_DEVICE, REFKEY, VERKEY) == STATUS_OK) 
      _type = _aga_dongle;
    else
    {
      HL_LOGOUT();
      if (HL_LOGIN(PRASSIADR, LOCAL_DEVICE, REFKEY, VERKEY) == STATUS_OK)
        _type = _prassi_dongle;
      else
      {
        HL_LOGOUT();
        if (HL_LOGIN(PROCOMADR, LOCAL_DEVICE, REFKEY, VERKEY) == STATUS_OK)
          _type = _procom_dongle;
      }
    }
  }  
  HL_LOGOUT();
  ok = HL_LOGIN(USERADR, DONT_CARE, REFKEY, VERKEY) == STATUS_OK;
  
  if (ok)
  {     
    _hardware = _dongle_hardlock;
    
    HL_READBL((unsigned char*)_eprom);

    unsigned short data[4];
    memcpy(data, _eprom, sizeof(data));
    garble(data);
    
    if (data[0] == 0xFAE8)
      _serno = data[1];
    else
    {                  
      if (data[0] == 0x3283 || data[0] == 0xA3AA) // chiave programmatori !!
      {         
        if (_type == _user_dongle)
          _type = _developer_dongle;
        _serno = 0;  
      }  
    }  
  }  

  if (ok) 
  {
    _max_users = 1;
    _last_update = wxDateTime::Now();
    _year_assist = _last_update.GetYear();

    if (_type == _user_dongle)
    {  
      const bool already = already_programmed();
      
      _module.reset(); // Disattiva  tutti i moduli
      const int last_word = already ? 12 : 4;                    
      unsigned short data[4];

      // Legge flag di attivazione dei moduli
      for (int i = 0; i < last_word; i += 4)
      {
        memcpy(data, &_eprom[48+i], sizeof(data));
        garble(data);
        if (data[3] == _serno) // Validate block
        {
          for (int j = 0; j < 3; j++)
          {
            unsigned short parola = data[j] ^ _serno;
            if (parola)
            {
              for (int b = 15; b >= 0; b--)
              {
                if (test_bit(parola, b))             
                {
                  const unsigned short bit = i * 12 + j * 16 + b;
                  _module.set(bit+1);
                }  
              }  
            }  
          }
        }
        else
          break;
      }
      _module.set(0, true); // Forza l'attivazione della base

      // Legge anno di assitenza e numero massimo di utenti
      memcpy(data, &_eprom[60], sizeof(data));
      garble(data);      
      
      if (already)
      {
        _year_assist = data[0];
        _max_users = data[1];
        const long& giulio = (const long&)data[2];
        _last_update = julian2date(giulio);
      }
      else
      {
        _year_assist = 0;
        _dirty = true;
      }
    }  
    else
    {                   
      _module.set(255); // Last module on key
      _module.set();     // Activate all modules

      _max_users = 4;
      _last_update = wxDateTime::Now();
      _year_assist = _last_update.GetYear();
    }  
  }  
  else
    _type = _no_dongle;
  
  return ok;  
}

bool TDongle::eutron_login(bool test_all_keys)
{
  bool ok = false;

  if (_eutron_key == NULL)             
    _eutron_key = new KEY_NET;
  memset(_eutron_key, 0, sizeof(KEY_NET));
  _eutron_key->net_command = NET_KEY_OPEN;
  // _eutron_key->command = LOCATING_MODE;
	_eutron_key->status = ST_HW_FAILURE; // Don't leave ST_OK = 0 here!

  const char* labels[5] = { "AGA.INFORMATICA", "AGA.PRASSI", "AGA.PROCOM", 
                            "AGA.CAMPO", "25EBAI" };
  TDongleType types[5]  = { _aga_dongle, _prassi_dongle, _procom_dongle, 
                            _user_dongle, _developer_dongle };
  for (int k = test_all_keys ? 0 : 3; k < 5; k++)
  {
    memset(_eutron_key->label, 0, LABEL_LENGTH);
    memcpy(_eutron_key->label, labels[k], strlen(labels[k]));  
    memset(_eutron_key->password, 0, PASSWORD_LENGTH);
    memcpy(_eutron_key->password, ::encode(labels[k]), strlen(labels[k]));  

    smartlink(_eutron_key);
    ok = _eutron_key->status == ST_OK;
    if (ok)
    {                  
      _hardware = _dongle_eutron;
      _type = types[k];
      break;
    }
  }  
  if (ok)
  { 
    _serno = 0;
    _max_users = 1;
    _last_update = wxDateTime::Now();
    _year_assist = _last_update.GetYear();

    if (_type == _user_dongle)
    {   
      _module.reset();   // Disattiva tutti i moduli
      if (read_words(0, sizeof(TEutronHeader) / 2, _eprom))
      {
        const TEutronHeader* eh = (const TEutronHeader*)_eprom;
        wxString serno = eh->_serno; serno.Truncate(8);
        _serno = unsigned(atol(serno));
        if (already_programmed())
        {
          _max_users   = eh->_max_users;
          _last_update = eh->_last_date;
          _year_assist = eh->_year_assist;
          
          // Calcola il numero della word dove cominciano i bit di attivazione
          unsigned short otb = eh->_offset_to_bits;
          if (otb == 0) otb = 16;   // Compatibile Hardlock

          unsigned short sob = eh->_size_of_bits;
          if (sob == 0) sob = 16;   // Compatibile Hardlock
          
          unsigned short data[64];
          if (read_words(otb, sob, data))
          {  
            int module = 1;
            for (unsigned short w = 0; w < sob; w++)
            {
              for (int b = 0; b < 16; b++)
              {
                if (test_bit(data[w], b))
                  _module.set(module);
                module++;  
              }    
            }
          }
        }  
        else
          _dirty = true;
      }  
      _module.set(0, true); // Forza l'attivazione della base
    }
    else
    {
			_max_users = 4;
      _module.set(255); // Last module on key
      _module.set();    // Activate all modules
    }  
  }
  else
  {
    delete _eutron_key;
    _eutron_key = NULL;
  }
  return ok;
}

bool TDongle::Login(bool test_all_keys)
{                                                                   
  bool ok = true;

  if (_type != _no_dongle) // Already logged in
    Logout();

  TDongleHardware hw = _hardware;               
  if (hw == _dongle_unknown)
    hw = (TDongleHardware)GetServerApp().GetConfigInt("Donglehw");
  switch(hw)
  {
  case _dongle_hardlock: 
    ok = hardlock_login(test_all_keys); 
    break;
  case _dongle_eutron: 
    ok = eutron_login(test_all_keys); 
    break;
  default:
    ok = false;
    break;
  }
  if (!ok) 
  {
    if (!ok && hw != _dongle_eutron)
      ok = eutron_login(test_all_keys);
    if (!ok && hw != _dongle_hardlock)
      ok = hardlock_login(test_all_keys);
    if (ok)
      GetServerApp().SetConfigInt("Donglehw",(int)_hardware);
  }
  return ok;
}

bool TDongle::Logout()
{ 
  if (_type != _no_dongle)
  {
    switch (_hardware)
    {
    case _dongle_hardlock:  
      HL_LOGOUT(); 
      break;
    case _dongle_eutron:   
      if (_eutron_key)
      {
        _eutron_key->net_command = NET_KEY_CLOSE;
        _eutron_key->command = 0;
        smartlink(_eutron_key);
      }  
      break;
    default:
      break;  
    }
  }  

  _type = _no_dongle;
  _serno = 0xFFFF;

  return true;
}

bool TDongle::Connected()
{
	bool ok = false;
  if (type() != _no_dongle)
	{
		unsigned short a[4] = { 0, 0, 0, 0 };
		garble(a);
		for (int i = 0; i < 4; i++)
			ok |= (a[0] != 0);
	}
	return ok;
}

// Data punta ad un array di 4 words 
// Deve essere cosi' per problemi del C,
// non trasformare in array: pena di morte! 
bool TDongle::read_words(unsigned short reg, unsigned short len, unsigned short* ud) const
{         
  bool ok = false;
  switch (_hardware)
  { 
  case _dongle_hardlock:
    {
      for (unsigned short i = 0; i < len; i++)
        HL_READ(reg+i, &ud[i]);
      ok = true;
    }  
    break;
  case _dongle_eutron:
    if (_eutron_key)
    {
      _eutron_key->net_command = NET_KEY_ACCESS;
      _eutron_key->command = BLOCK_READING_MODE;
      unsigned short* pointer = (unsigned short*)(&_eutron_key->data[0]);
      unsigned short* number  = (unsigned short*)(&_eutron_key->data[2]);
      while (len > 0)
      {
        *pointer = reg;
        *number = (len <= 16) ? len : 16;
        smartlink(_eutron_key);

        ok = _eutron_key->status == ST_OK;
        if (ok)
          memcpy(ud, &_eutron_key->data[4], (*number)*2);
        else
        {
          GetServerApp().WriteLog("*** EUTRON read error");
          break;
        }  
        len -= *number;
        reg += *number;
        ud  += *number;
      }    
    }
    break; 
  default:
    break;
  }  
  return ok;
}

// Data punta ad un array di 4 words 
// Deve essere cosi' per problemi del C,
// non trasformare in array: pena di morte! 
bool TDongle::write_words(unsigned short reg, unsigned short len, unsigned short* data) const
{                     
  bool ok = false;
  switch(_hardware)
  {                
  case _dongle_hardlock:
    {
      int err = STATUS_OK;
      for (unsigned short r = 0; r < len; r++)
      {           
        const unsigned short address = reg+r;
        err = HL_WRITE(address, data[r]);
        if (err != STATUS_OK)                                     
        {
          GetServerApp().WriteLog("*** HARDLOCK write error");
          break;
        }  
      }  
      ok = err == STATUS_OK;
    }  
    break;
  case _dongle_eutron:
    if (_eutron_key)
    {
      _eutron_key->net_command = NET_KEY_ACCESS;
      _eutron_key->command = BLOCK_WRITING_MODE;
      unsigned short* pointer = (unsigned short*)(&_eutron_key->data[0]);
      unsigned short* number  = (unsigned short*)(&_eutron_key->data[2]);
      while (len > 0)
      {
        *pointer = reg;
        *number = len > 16 ? 16 : len;
        memcpy(&_eutron_key->data[4], data, (*number)*2);
        smartlink(_eutron_key);
        ok = _eutron_key->status == ST_OK;
        if (!ok)
        {
          GetServerApp().WriteLog("*** EUTRON write error");
          break;
        }  
        reg  += *number;
        len  -= *number;
        data += *number;
      }    
    }
  default:
    break;  
  }
  return ok;
}

bool TDongle::burn_hardlock()
{
  unsigned short data[4];

  const wxDateTime today = wxDateTime::Now();   
  const bool already = already_programmed();
  if (already)  
  {       
    memcpy(data, &_eprom[60], sizeof(data));
    garble(data);                         
    if (data[0] < 1997 || data[0] > 2997)
		{
			GetServerApp().WriteLog("On Line Assistance error.");
      return false;
		}
    if (data[1] == 0 || data[1] >= 10000)
		{
			GetServerApp().WriteLog("*** Bad users number.");
      return false;
		}
    const long& val = (const long&)data[2];
    const wxDateTime date= julian2date(val);
    if (date > today)
		{
      GetServerApp().WriteLog("*** Too late sir: key has already expired!");
      return false;
		}
  }
  
  data[0] = _year_assist;
  data[1] = _max_users;
  long& val = (long&)data[2];
  val = date2julian(today);
  garble(data);
  write_words(60, 4, data);
  _last_update = today;

  // Il primo bit della memoria della chiave corrisponde al modulo uno 
  // non allo zero (che e' la base ed e' sempre attivo)
  unsigned short module = 1;
  for (int octect = 0; octect < 3; octect++)  
  {                                       
    for(int parola = 0; parola < 3; parola++)  
    {        
      unsigned short& p = data[parola];
      p = 0;
      for (int bit = 0; bit < 16; bit++)
      {          
        if (Active(module))
          set_bit(p, bit);
        module++;
      }
      p ^= _serno;
    }
    data[3] = _serno;
    garble(data);
    write_words(48 + 4*octect, 4, data);
  }
  
  return true;
}

bool TDongle::burn_eutron()
{
  TEutronHeader* eh = (TEutronHeader*)_eprom;
  memset(eh, 0, sizeof(TEutronHeader));
  
  _last_update = wxDateTime::Now();
  sprintf(eh->_serno, "%lu", (unsigned long)_serno);
  eh->_year_assist = _year_assist;
  eh->_max_users   = _max_users;
  eh->_last_date   = date2long(_last_update);
  eh->_scad_date   = 0;
  
  unsigned long cs = 0;
  for (unsigned char* ptr = (unsigned char*)_eprom; ptr < (unsigned char*)&eh->_checksum; ptr++)
    cs += *ptr | ~(short(*ptr << 8));
  eh->_checksum = cs;
  
  const unsigned short otb = sizeof(TEutronHeader) / 2;
  const unsigned short sob = 16;
  eh->_offset_to_bits = otb;
  eh->_size_of_bits = sob;
  
  bool ok = write_words(0, otb, _eprom);
  
  if (ok)
  {
    unsigned short data[sob]; memset(data, 0, sizeof(data));
    for (int module = 1; module < 256; module++)
    {                             
      if (Active(module))
      {
        unsigned short& w = data[(module-1) / 16];
        set_bit(w, (module-1) % 16, true);
      }  
    }
    ok = write_words(otb, sob, data);
  }
  
  return ok;
}

bool TDongle::Burn()
{          
  bool ok = _type == _user_dongle;

  if (ok) 
  {       
    switch(_hardware)
    {
      case _dongle_hardlock: ok = burn_hardlock(); break;
      case _dongle_eutron  : ok = burn_eutron(); break;
      default              : break;
    }  
  }   
  if (ok)
    _dirty = false; 

  return ok;  
}