// Registration.cpp : implementation file
//

#include "stdafx.h"
#include "im_bas.h"
#include "Registration.h"
#include <io.h>
#include <fcntl.h>
#include <ctype.h>
#include <sys/stat.h>


#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define PW_MIN_LEN   3              //minimum number of chars in password
#define PW_MIN_NUM   10000000       //number of digits in password return number


/////////////////////////////////////////////////////////////////////////////
// CRegistration dialog

/////////////////////////////////////////////////////////////////////////////
// Description: Constructor for the class CRegistration, internal variables are 
// initialised.
//
// Parameter: module - The module is an unique (integer) identifier for the task
// whoms license will be check or updated.
// Parameter: *pParent - Pointer to CWnd object which is the parent of our newly
// created registration dialog. Default the pointer is NULL, which is normally 
// sufficient
// 
// Returns:
//
// 
//Comment:
//
/////////////////////////////////////////////////////////////////////////////
CRegistration::CRegistration( int module, CWnd* pParent)
	: CDialog(CRegistration::IDD, pParent)
{


	//{{AFX_DATA_INIT(CRegistration)
	m_cKeyCode = _T("IM_BAS");
	m_cKeyPassword = _T("");
	//}}AFX_DATA_INIT

  //strcpy( m_oModule.name, GetProductName());
  //m_oModule.id = module;
  //m_oModule.version = GetFileVersion().GetAt(0);
  //m_oModule.options = 0;

  m_iModule = module;
  m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
  m_nFirst = 1;

  //Wizard .opt file, including directory
  CString file = FlTask::SysDirectory();
  file += _T("/opt/");
  file += TASK_OPT_FILE;

  //open the specified option file
  m_hOptionFile = _open( file, O_RDWR | O_BINARY, S_IREAD | S_IWRITE);
}


/////////////////////////////////////////////////////////////////////////////
// Description: Destructor for class CRegsitration.
// 
// Parameter: 
// 
// Returns: 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
CRegistration::~CRegistration()
{

  //close option file
  close( m_hOptionFile);
  m_hOptionFile = -1;
} //~CRegistration


/////////////////////////////////////////////////////////////////////////////
// Description: This method is called by the framework to exchange and validate 
// dialog data. Never call this function directly. It is called by the UpdateData
// method. Call UpdateData to initialize a dialog box controls or retrieve data
// from a dialog box. 
//
// Parameter: *pDX - A pointer to a CDataExchange object.
//
// 
// Returns:
//
// 
// Comment:
//
/////////////////////////////////////////////////////////////////////////////
void CRegistration::DoDataExchange(CDataExchange* pDX)
{


	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CRegistration)
	DDX_Control(pDX, IDC_PRODUCT, m_cProduct);
	DDX_Control(pDX, IDC_COMPANY, m_cCompany);
	DDX_Control(pDX, IDC_EMAILLINK, m_cEmailLink);
	DDX_Control(pDX, IDOK, m_cButtonOk);
	DDX_Control(pDX, IDCANCEL, m_cButtonCancel);
	DDX_Control(pDX, IDC_KEYPASSWORD, m_cEditKeyPassword);
	DDX_Control(pDX, IDC_KEYCODE, m_cEditKeyCode);
	DDX_Text(pDX, IDC_KEYCODE, m_cKeyCode);
	DDV_MaxChars(pDX, m_cKeyCode, 8);
	DDX_Text(pDX, IDC_KEYPASSWORD, m_cKeyPassword);
	DDV_MaxChars(pDX, m_cKeyPassword, 45);
	//}}AFX_DATA_MAP
} //DoDataExchange


BEGIN_MESSAGE_MAP(CRegistration, CDialog)
	//{{AFX_MSG_MAP(CRegistration)
	ON_BN_CLICKED(IDC_PRODUCT, OnProduct)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CRegistration message handlers

/////////////////////////////////////////////////////////////////////////////
// Description: Initilise all the data and controls of the dialog box just before 
// showing.
//
// Parameter: 
// 
// Returns: TRUE unless the focus is set to a control, exception: OCX Property 
// Pages return FALSE.
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
BOOL CRegistration::OnInitDialog() 
{


	CDialog::OnInitDialog();
	
	//set the icon for this dialog. The framework does this automatically
	//when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			//set big icon
	SetIcon(m_hIcon, FALSE);		//set small icon

  //Button styles
  m_cButtonOk.SetIcon( IDI_OK);
  m_cButtonOk.SetFlat( FALSE);
  m_cButtonCancel.SetIcon( IDI_EXPLORE);
  m_cButtonCancel.SetFlat( FALSE);

	//Set HyperLink for E-Mail
	m_cEmailLink.SetURL(_T(IDS_MAILADDR));
	m_cEmailLink.SetUnderline( true);
	m_cEmailLink.SetLinkCursor(AfxGetApp()->LoadCursor(MAKEINTRESOURCE( IDC_CHAND)));
  
  m_cCompany.SetWindowText( GetCompanyName());

  m_cProduct.SetWindowText( GetFileDescription());
	m_cProduct.SetURL(_T(IDS_INFOURL));
	m_cProduct.SetUnderline( false);
	m_cProduct.SetLinkCursor(AfxGetApp()->LoadCursor(MAKEINTRESOURCE( IDC_CHAND)));

  SetWindowText( GetProductName());

	return TRUE;  //return TRUE unless you set the focus to a control
	              //EXCEPTION: OCX Property Pages should return FALSE
} //OnInitDialog


/////////////////////////////////////////////////////////////////////////////
// Description: Registration code entered by user, validate and report any 
// invalid or incomplete authorisatoin sequence.
// 
// Parameter: 
// 
// Returns: 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
void CRegistration::OnOK() 
{


	//update the dialog box data, befor using
  UpdateData();
  int ready = TRUE;


  if (SearchOption( &m_cKeyPassword) >= 0)
    AddOption( (char *)((LPCTSTR)m_cKeyPassword));
  else
  {

    CString invalid;
    if (!invalid.LoadString( IDS_INVALIDSERIAL))
      invalid = _T("Invalid authorization sequence!");

    //popup message box with error report
    MessageBox( invalid, GetProductName(), MB_OK|MB_ICONEXCLAMATION|MB_TOPMOST);
    return;
  }

  EndDialog( 0); //we are finished, remove dialog from screen
}


/////////////////////////////////////////////////////////////////////////////
// Description: Check if a valid registration of the software can be found.
// The registration is unique or every FactoryLink license, part of the license
// code is the FL serial number. 
//
//
// Parameter:
//
// 
// Returns:
//
// 
// Comment:
//
/////////////////////////////////////////////////////////////////////////////
BOOL CRegistration::IsRegistered()
{


  //check for option in license file
  if (SearchOption() >= 0) return TRUE;

  //registration is not found
  return FALSE;
} //IsRegistered


/////////////////////////////////////////////////////////////////////////////
// Description: 
// 
// Parameter: 
// 
// Returns: 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
void CRegistration::OnProduct() 
{


	// TODO: Add your control notification handler code here
} //OnProduct


/////////////////////////////////////////////////////////////////////////////
// Description: Close registration dialog without updating the registration.
//
//
// Parameter:
//
// 
// Returns:
//
// 
// Comment:
//
/////////////////////////////////////////////////////////////////////////////
void CRegistration::OnCancel() 
{

  
  EndDialog( 0);
	//CDialog::OnCancel();
} //OnCancel


/////////////////////////////////////////////////////////////////////////////
// Description: This function checks the format of the option file.
// 
// Parameter: 
// 
// Returns: The function returns the number of modules in the option file: >= 0,
// or reports an error with the value -1
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
int CRegistration::CheckFile()
{

	long fsize = lseek( m_hOptionFile, 0, SEEK_END),
				readt;
  char *buf,
       *buf_copy,
       **hold,
       *walk,
       line[ 180];
  int  nr_modules = 0;


  if( !fsize) return -1;

  //allocate space to read buffer
  if( ( buf = ( char *)calloc( 1, ( size_t)fsize +1)) == ( char *)0)
    return -1;

  //set the filepointer to the start of the file
  if( lseek( m_hOptionFile, 0, SEEK_SET) == -1) {

    free( buf);
    return -1;
  }

  //read the total file
  if( ( readt = read( m_hOptionFile, buf, ( unsigned)fsize)) != ( int)fsize) {

		free( buf);
    return -1;
  }

  //strip off the comment lines
  buf_copy = buf;
  hold = &buf_copy;

  do {

    walk = strbreak( hold, "\n", line);

    //check if this is a command line
    if( !strlen( line) ||
        line[ 0] == '*' ||
        line[ 0] == 0x1A ||
        line[ 0] == 0x0A ||
        line[ 0] == 0x0D
        ) continue;

    nr_modules++;

  } while( walk);

  free( buf);

  //set the filepointer to the beginning of the file
  if( lseek( m_hOptionFile, 0, SEEK_SET) == -1)
    return -1;

  return nr_modules;
} //CheckFile


/////////////////////////////////////////////////////////////////////////////
// Description: Searches for a token in a string.
// 
// Parameter: 
// 
// Returns: 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
char *CRegistration::strbreak( char **str, const char *token, char *copy)
{

  unsigned  length = strlen( *str),
            i;
  char      *start;

  //search string for token
  for( i = 0, start = *str; i < length; i++, (*str)++) {

    if( !strncmp( *str, token, strlen( token))) {

      *str += strlen( token); //set pointer to next part of string
      break;
    }
  }

  if( copy) {

    strncpy( copy, start, i);
    copy[ i] = '\0';
  }

  if( i != length)
    return *str;
  else
    return NULL;                               
} //strbreak


/////////////////////////////////////////////////////////////////////////////
// Description: This routine is called to check that a given hex value in ASCII
// is valid and converts the value to its byte value.
// 
// Parameter: 
// 
// Returns: 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
char *CRegistration::hex2bin( char *bin, char *hex, int nbyte)
{

  char ch1, ch2;
  int i;
 
  //check all are valid hex digits

  for( i = 0; i < nbyte; i++)
    if( !isxdigit( hex[i])) return NULL;


  //take a pair of characters at a time to create one byte

  for( i = 0; i < nbyte; i += 2) {

    ch1 = ( char)toupper( hex[i]);
    ch2 = ( char)toupper( hex[i+1]);

    //check ch1 is a valid octal digit, and error if not
    if( ch1 >= '0' && ch1 <= '9')
      ch1 -= '0';
    else
      ch1 -= ('A' - 0xA);


    //check ch2 is a valid octal digit, and error if not
    if( ch2 >= '0' && ch2 <= '9')
      ch2 -= '0';
    else
      ch2 -= ('A' - 0xA);


    //compose the valid octal digits into one byte
    bin[ i/2] = ( char)( ( ch1 << 4) + ch2);
  }

  return bin;
} //hex2bin


/////////////////////////////////////////////////////////////////////////////
// Description: 
// 
// Parameter: 
// 
// Returns: 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
char *CRegistration::bin2hex( char *hex, char *bin, int nbyte)
{

  int i;
  static char tmp[ 256 ];

  for( i = 0; i < nbyte; i++) {


    tmp[ i * 2]      = (((bin[i] >> 4) & 0x0F) < 0xA) ? ((bin[i] >> 4) & 0x0F) + '0'
                                                       : ((bin[i] >> 4) & 0x0F) - 0xA + 'A';

    tmp[ (i * 2) +1] = ((bin[i] & 0x0F) < 0xA) ? (bin[i] & 0x0F) + '0'
                                                : (bin[i] & 0x0F) -0xA + 'A';
  }

  if( hex )
  {
    memcpy( hex, tmp, nbyte *2);
    return hex;
  }
  else
    return tmp;
} //bin2hex


/////////////////////////////////////////////////////////////////////////////
// Description: Search the total option file for a module.
// 
// Parameter: *authorization - Authorization string, entered by user. Default
// parameter value is 'NULL'.
// 
// Returns: 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
int CRegistration::SearchOption( CString *authorization)
{

  TO_ENCRYPT e;
  CRYPTREC   *crypt,
             *key;
  char *buf = NULL,
       *record;
  char ascii_key[ KEY_LEN * 2],
       ascii_crypt[ CRYPT_LEN * 2],
       version,
       id,
       options,
       pw[ KEY_LEN +1];
  int  j;
  char auth[ 100];


  memset( &e, 0, sizeof( TO_ENCRYPT));

  if (!authorization)
  {

    if (m_hOptionFile < 0) return -1;
    long fsize = lseek( m_hOptionFile, 0, SEEK_END);

    //calculate the number of records to process
    if( CheckFile() == -1) return E_CORRUPTED;

    //allocate space to read buffer
    if( ( buf = ( char *)calloc( 1, ( size_t)fsize +1)) == ( char *)0)
      return -1;

    //read the total file
    if( read( m_hOptionFile, buf, ( unsigned)fsize) != ( int)fsize) {

      free( buf);
      return -1;
    }

    //search for the option
    if( ( record = strstr( buf, (char *)((LPCTSTR)GetInternalName()))) == NULL) {

      free( buf);
      return -1;
    }

    //move to the first encrypted code
    record += strlen( (char *)((LPCTSTR)GetInternalName()));
  }
  else
  {

    //clear internal buffer
    memset( auth, 0, 100);

    //start with a space, it will be removed
    auth[ 0] = ' ';

    //copy the sequence, add spaces if user left them out
    for (int i = 0, k = 1; i < authorization->GetLength(); i++)
    {

      if ((k % 5) == 0) auth[ k++] = ' ';

      if (authorization->GetAt( i) != ' ') auth[ k++] = authorization->GetAt( i);
    }

    record = auth;

    *authorization = auth;

    if (strlen( record) < 45) return -1;
  }

  while( *record == ' ') record++;

  //adjust to CRYPTREC format
  record--;

  crypt = ( CRYPTREC *)( record + (2 * sizeof( CRYPTREC)));
  key = ( CRYPTREC *)record;

  //filter out the spaces in the string to decrypt
  for( j = 0; j < ( (CRYPT_LEN *2) / CRYPTREC_LEN); j++)
    strncpy( &ascii_crypt[ j * CRYPTREC_LEN], crypt[ j].data, CRYPTREC_LEN);

  //convert the readable string to the original decrypted string
  hex2bin( ( char *)&e, ascii_crypt, CRYPT_LEN * 2);

  //filter out the spaces in the string to decrypt
  for( j = 0; j < ((KEY_LEN *2) / CRYPTREC_LEN); j++)
    strncpy( &ascii_key[ j * CRYPTREC_LEN], key[ j].data, CRYPTREC_LEN);

  if (buf) free( buf);

  //do the real encrypting
  hex2bin( pw, ascii_key, KEY_LEN * 2);
  pw[ KEY_LEN] = '\0';

  k_encrypt( pw, ( char *)&e, CRYPT_LEN);

  hex2bin( &version, e.version, 2);   //major version of the module
  hex2bin( &id, e.id, 2);             //unique id the module

  hex2bin( &options, e.options, 2);   //options of the module

	//update the return values of the MODULE structure
	strncpy( m_cSerial, e.serial, SERIAL_LEN);
	m_cVersion[0] = e.version[ 0];
	m_cVersion[1] = e.version[ 1];

  //check the decrypted string
  char *test = FlTask::SerialNumber();

  if( !strncmp( test, e.serial, SERIAL_LEN) &&
      ( id == m_iModule ) &&
      ( version == (GetFileVersion().GetAt( 0) - '0'))
    ) {

    return ( int)options;
  }

  return FAULT;
} //SearchOption


/////////////////////////////////////////////////////////////////////////////
// Description: Add a module to the option file.
// 
// Parameter: 
// 
// Returns: 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
int CRegistration::AddOption( char *serial)
{


  //set the file pointer to the end of the option file
  if( lseek( m_hOptionFile, 0, SEEK_END) == -1) return -1;

  //write the module to the option file
  if( write( m_hOptionFile, ( char *)((LPCTSTR)GetInternalName()), GetInternalName().GetLength()) != GetInternalName().GetLength())
    return -1;

  char space = ' ';

  for (int i = GetInternalName().GetLength(); i < 9; i++)
    if( write( m_hOptionFile, &space, 1) != 1)
      return -1;

  if( write( m_hOptionFile, serial, strlen( serial)) != strlen( serial))
    return -1;

  return OK;
} //AddOption


/////////////////////////////////////////////////////////////////////////////
// Description: 
// 
// Parameter: 
// 
// Returns: 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
void CRegistration::k_encrypt( char *pwstr, char *instr, long len )
{

  //password string must be minimal three characters
  if( strlen( pwstr) < PW_MIN_LEN ) return;

  //return encrypted string
  _tr_crypt( instr, pwstr, (long)len );
} //k_encrypt


/////////////////////////////////////////////////////////////////////////////
// Description: 
// 
// Parameter: 
// 
// Returns: 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
char *CRegistration::_tr_crypt( char *instr, char *pwstr, long len)
{

  short j,
        pwlen =  strlen( pwstr),
        passnum;
  long i;
  char buff;


  //get seed value

  passnum = ( short) (((( _tr_pnum( pwstr) / 997) - 1) % 254 ) + 1);

  for( i = j = 0; i < len; i++ )  //process whole string
  {
  
    passnum = ( short) ((( passnum + ( i - len )) - 1 ) % 254) + 1;
     
    buff = ( instr[i] ^ ( ( char)passnum ^ pwstr[j]) ); //XOR 3 var's

    instr[i] = ( buff ? buff : instr[i]); //if NULL return char

    if( j >= pwlen) j = 0;
    else j++;
  }

  return instr; //send back encrypted string
} //_tr_crypt


/////////////////////////////////////////////////////////////////////////////
// Description: 
// 
// Parameter: 
// 
// Returns: 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
long CRegistration::_tr_pnum( char *s)
{

 long ret = 1L;
 int i;


  if( s[0] && s[1] && s[2] ) //3 char minimum password len
  {

    //sum the ascii values of each char

    for( i = 0; s[i]; i++ ) ret += ( long)( s[i] + i);


    //bit shift ret 1 position to the left until ret exceeds PW_MIN_NUM
    for( ; ret < PW_MIN_NUM; ret <<= 1 ) ;

    return ret;
  }
  else
    return -1L;     
} //_tr_pnum


/////////////////////////////////////////////////////////////////////////////
// Description: The following encryption algorithm handles the full international 
// ASCII character set. Passnum is an integer between 1 and 254 that receives 
// a value returned from _tr_pnum based on the password. Passnum is a seed for
// the encryption-key variable that is modified in the for-loop by the position
// of the current character in reference to the total string (i - len). This 
// produces a non-repeating pattern even if all the characters are the same, 
// thus hiding the length of the password key. The password characters are always
// being rotated in a circular manner (que). ret[i] gets the bitwise XOR of the
// character with the XOR of passnum and the current character in the password que.
// This gives 24 bits of encryption for each character. This encryption/decryption 
// program can be broken by any computer-literate person with access to this source 
// code or a knowledge of cryptography.
// 
// Parameter: 
// 
// Returns: 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
void CRegistration::generate_pw( char *password, int len)
{

  int i;
  static unsigned seed;


  srand( seed + ( unsigned)time( NULL));

  for( i = 0; i < len; i++)
    #ifdef WIN
    password[ i] = random( 256);
    #else
    password[ i] = ( char)rand();
    #endif

  seed = ( unsigned )password[ 0];

  return;
} //generate_pw
