///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
//
// Provides the client (browser) side functions for sending information to the collector server
// via an Image Source HTTP request.  This was designed specifically for Digital Insight
//
// This version supports both the DI Retail system and the DI Business system
//
// ---------------------------------------------------------------------------------------
// For the DI Retail online banking system - ie, based on RunSubmit()
// In addition to configuring the parameters in this file, 
// the changes must be made to the DI login HTML page
//	1) A <SCRIPT> tag to loag this js file must be added to the HTML page
//	2) The runSubmit() function for the Login Form onSubmit handler must be changed to GArunImageSubmit()
//		One could also eliminate the post and action parameters of the form, but that is not mandatory
//	3) The Login form tag MUST include an ID attribute which is also set in login_form_id in this flie
//	4) The GAImage() js function must be executed within the body of the HTML page in order to initialize the
//		img src and associated state flags.
//
// ---------------------------------------------------------------------------------------
// For the DI Business online banking system - ie, based on PlaceButton() and validateSubmit()
// In addition to configuring the parameters in this file, 
//
//
// Changes made to handle the Business system:
// - added ability to collect the CompanyID (as a hash) in addition to the UserID
// - created replacement function for DI's validateSubmit()
///////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
//
// Configuration parameters that are used along with iclient.js
// These parameters can either be defined here, 
// or usually they are overwritten file in a separate iclient_config_XXX.js file
// that is loaded after this file
//
///////////////////////////////////////////////////////////////
// Flag to show alerts or not
var show_alerts = 0;

// Config: - Collection Image parameters
var img_id = "collector_image";
var image_url = "diFiles/skins/default/images/ga_invisible_img.gif";
var collector_url = "https://www.staronega.com/ga_collector/ga_img_collector.php";

/////////////
// These parameters define the login form
////////////

// Name (ID)) of Login form of the form
var login_form_id = "Login";                // Applies to both DI Retail and Business banking

var fieldname_userid = "userNumber";      // Set for DI Retail banking 
//var fieldname_userid = "userID";        	// Set for DI Business banking 

//var fieldname_companyid = "";     		// Set for DI Retail banking 
var fieldname_companyid = "companyID";     	// Set for DI Business banking 

var action_url = "https://www.starone.org/onlineserv/HB/Login.cgi";	// Set for DI Retail banking 
//var action_url = 'index.cgi?state=login';   		// Set for DI Business banking 

//////////
// Other configuration parameters
/////////
// Config: Timeout parameters
var timeout = 4000 ; // Max number of milliseconds to wait for the collection image to complete

// Config:  Control flags for what information to collect and transmit
var userhash_flag = true;		// Whether or not to transmit the userID hash
var companyhash_flag = false;	// Whether or not to transmit the CompanyID hash
var	cookie_flag = true;			// Whether or not to transmit the client cookie (cookie is set if client_cookie_name is set)
var	components_flag = false;	// Whether or not to collect and transmit browser components (not implemented yet)

// Config:  Name of client-captured persistant cookie (if any)
var client_cookie_name = "ga"; // Name of the client cookie

// Parameters for the salt
// THE ver_hash AND salt PARAMETER VALUES MUST BE RECORDED IN ORDER TO MAKE AN ASSOCIATION WITH THE REAL USER ID
var salt = "star one1 salt";
var ver_hash = 1.0;				// Hash and Salt version

// Version information
var	ver_client = 1.0;	// Client version

///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
//
//	The following is common code to all configurations and should not be overwritten
//
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////


// Internal parameters for signalling whether to submit on image load
var submit_ready_flag = false;
//	Internal parameter to prevent multiple login submits (from both image reload and timer)
var submit_performed = false;

///////////////////////////////////////////////////////////////
// THESE FUNCTIONS ARE USED ON THE HTML LOGIN PAGE
//	GAImage()						- Sets up the (hidden) image that is used to transmit information to the server
//	GArunImageSubmit(arg1, arg2)	- Replaces Digital Insights runSubmit() function (for Retail Login)
//	GAvalidateSubmit()				- Replaces Digital Insights validateSubmit() function (for Business Login)
///////////////////////////////////////////////////////////////

//////////////////////
//  FUNCTION
//  GAImage()
//	Creates the original hidden image tag
//  Also makes sure that the submit_ready_flag is initialized 
function GAImage(){
	submit_ready_flag = false;
	document.write( '<img SRC=' + image_url +' ID=' + img_id + ' onLoad="GAsubmit_handler( )"> ' );
}

//////////////////////
//  FUNCTION
//  GArunImageSubmit(runSubmitArg1, runSubmitArg2, URL)
//
//  USED FOR DI RETAIL LOGIN
//	Event handler that is called when the login button is processed (onsubmit handler)
//	It changes the image source which causes a new HTTP request
//  It then calls the Digital Insight runSubmit() function (which is normally the onsubmit handler)
//
function GArunImageSubmit(arg1, arg2)
{
	//	Now do runSubmit (from Digital Insight)
	var error = "";
	if( show_alerts ) alert("BEFORE runSubmit");	

    // Wrap runSumbmit call in a try catch however store the return result.
    var runSubmit_result = false;
    try { runSubmit_result = runSubmit(arg1, arg2); }    
    catch(e) 
    {
	if( show_alerts ) alert("CATCH ERROR FROM RUNSUBMIT = "+e.name+" = "+e.message +"|");        
	error = "runsubmit";
	return false;
    }

	if( show_alerts ) alert("AFTER runSubmit");	

	// Make sure DI's runSubmit executes sucessfully.
	if ( !runSubmit_result )
	{
		return false;
	}

	// Collect the parameters to return to the collection server
	param_array = GAcreateParamArray( document.getElementById(login_form_id), client_cookie_name, error );
	if( show_alerts ) alert("AFTER GAcreateParamArray - cookies = " + document.cookie);	

	// Submit the parameters to the collection server by encoding them in a URL
	// and sending them by changing an image source URL
	// The image onLoad handler (defined on the image tag) will cause the form to be submitted once the request is finished
	submit_ready_flag = true;
	var url = GAcreateURL( collector_url, param_array);	
	if( show_alerts ) alert("GArunImageSubmit IMG SRC = "+	url);	
	document.getElementById(img_id).src = url;

	if( show_alerts ) alert("AFTER IMAGE REPLACE");	
    
	// Set TimeOut in case collection image takes too long to load
	window.setTimeout( 'GAsubmit_handler();', timeout);
//	if( show_alerts ) alert("TimeOut set to "+ timeout);	

	// By returning FALSE the default form action is not performed
	// Instead, the Login form is submitted by either the Image onLoad handler or the window TimeOut handler
	return( false );	
}

//////////////////////
//  FUNCTION
//  GAvalidateSubmit()
//
//  USED FOR DI BUSINESS LOGIN
//	This function gets called within the Event handler that is called when the login button is processed (onsubmit handler)
//	First, it replicates all the form validation logic of DI's original validateSubmit()
//	Once those tests pass, it collects the beacon data and reloads the beacon image (causing an HTTP request)
//	It also starts a timer.  On the completion of the time or image load it submits the login form
//	It returns TRUE if the validation passes (ie, form is being submitted), or FALSE otherwise
//  The actual Event Handler is a snippet of javascript that is defined by placeButton() which
//  always returns FALSE regardless of what GAValidateSubmit returns, hence, the action of the form
//	is never invoked.
//
function GAvalidateSubmit () {

	// This is the original validateSubmitI() validation logic
	var desc = '', errorDesc = '';
	var fldList = '';
	var theForm = document.login;                  // ISSUE:  Is this a pointer or a full object copy?  Make sure it is still in scope for the submit
	var JavaCurrentFLAG = true; //navigator.javaEnabled ();

   if (typeof(theForm) == 'undefined' || !theForm) return (false);
   if (javaOnLoadFLAG == false || JavaCurrentFLAG == false) {
      desc = 'Java';
      errorDesc = 'Your Web browser must have Java enabled to use this site. Please enable Java in your browser\'s preferences, and reload this page.';
   } // end if
   if (errorDesc.length == 0) {
      if (theForm.companyID.value == '') fldList += ' -- Company ID';
      if (theForm.custPassword.value == '') fldList += ' -- Company Password';
      if (theForm.userID.value == '') fldList += ' -- User ID';
      if (theForm.userPassword.value == '') fldList += ' -- User Password';
      if (fldList.length > 0) {
         desc = 'ID/Password';
         errorDesc = 'The fields may not be empty. Please supply the following' + fldList + '.';
      } // end if
   } // end if
   if (errorDesc.length > 0) {
      alert (desc + ': ' + errorDesc);
          submitClickFlag = false;
      return (false);
   } // end if
   theForm.SwapMe.value = 'SignOn';
   theForm.SwapMe.name  = 'SignOn';
   theForm.action       = 'index.cgi?state=login';
   
   // The original validateSubmit would now do: theForm.submit ();
   // Instead, GAbeaconSubmit() collects and sends the beacon and then submits the login form   
   GAbeaconSubmit('');
	
	// Now return true as in the original validateSubmit()
	return (true);
	   
} // end fun GAvalidateSubmit()



///////////////////////////////////////////////////////////////
// THIS FUNCTION DEFINES WHAT PARAMETERS ARE COLLECTED AND RETURNED
///////////////////////////////////////////////////////////////
//////////////////////
//  FUNCTION
//  GAcreateParamArray(login_form, cookie_name, error)
//		login_form	=> the login form that is used to extract some information
//		error		=> a string that records any error information (can be included in the param array)
//
//	This function defines what parameters are returned as well as what their names are
//
function GAcreateParamArray(login_form, cookie_name, error){
	//	if( show_alerts ) alert("IN - GAcreateParamArray");	
	for( var i = 0 ; i < login_form.length ; i++ ){
		var e = login_form.elements[i];
		if( e.name == fieldname_userid ){ 
			var userID = e.value ;}
		if( e.name == fieldname_companyid ){ 
			var companyID = e.value ;}
	}
    
    ////
    // Get Client Based Cookie Parameters
    ////
	var client_cookie_state = -1;
	var client_cookie_value = "";
	var client_cookie_expire = 730 ; 	// Number of days for the client cookie to live
	if( cookie_name ){                  // Is the client cookie used?
	    client_cookie_value = GAGetCookie( cookie_name );
		if( client_cookie_value ){ 		// Does the client cookie already exist?
			client_cookie_state = 1;
		}
		else{							// Else, create the client cookie
			client_cookie_value = GACreateClientCookie( cookie_name, client_cookie_expire );
			client_cookie_state = 0;
		}
	}
		  
    ////
    // Get any additional Parameters
    ////
	
	    
	// Load up the parameter array.
	// Everything in the parameter array will be transmitted in the URL string
	var param_array = new Array();
	param_array["vc"] 	= ver_client;									// Version of the client (this code)	
	param_array["vh"] 	= ver_hash;										// Version information for the hash and salt	
	param_array["ct"] 	= gaGetTime();									// Client Time
	param_array["e"] 	= error;										// Any reported error
	if( userhash_flag ) param_array["uh"] 	= sha1(salt + userID);		// Create the userID hash with the salt
	if( companyhash_flag ) param_array["ch"] = sha1(salt + companyID);	// Create the companyID hash with the salt
	if( cookie_flag ) 	param_array["cs"] 	= client_cookie_state;		// State of the client-defined cookie 
																		// 1 = not used, 0 = new cookie, 1 = cookie was there
	if( cookie_flag ) 	param_array["cv"] 	= client_cookie_value;		// Value of the client-defined cookie (current or new)
		
	// Return the parameters
	return param_array;	
}

	
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
//
//	The following functions are used internally and are not exposed in HTML
//
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////

//////////////////////
//  FUNCTION
//  GAbeaconSubmit()
//		Calls GAcreateParamArray() to collect the beacon parameters
//		Then fires the Beacon by reloading the Beacon Image
//		Also sets a timer
//		Submits the Login Form as soon as either the Beacon Image completes loading or the Timer expires
//
function GAbeaconSubmit(error_string){
	var param_array = GAcreateParamArray( document.getElementById(login_form_id), client_cookie_name, error_string );
	if( show_alerts ) alert("AFTER GAcreateParamArray - cookies = " + document.cookie);	
	
	// Submit the parameters to the collection server by encoding them in a URL
	// and sending them by changing an image source URL
	// The image onLoad handler (defined on the image tag) will cause the form to be submitted once the request is finished
	submit_ready_flag = true;
	var url = GAcreateURL( collector_url, param_array);	
	if( show_alerts ) alert("GArunImageSubmit IMG SRC = "+	url);	

	document.getElementById(img_id).src = url;
	if( show_alerts ) alert("AFTER IMAGE REPLACE");	
    
	// Set TimeOut in case collection image takes too long to load
	window.setTimeout( 'GAsubmit_handler();', timeout);
	
	return true;
}


//////////////////////
//  FUNCTION: 
//	GAcreateURL(url_base, param_array)
//
//  Creates the URL string used to send the information back to the collection server 
// 
function GAcreateURL(url_base, param_array){
	var start = "?";
	var url = url_base;
	for( name in param_array ){
		url += start + escape(name) + "=" + escape(param_array[name]) ;
		start = "&";
	}
	return url;	
}



//////////////////////
//  FUNCTION
//  GAsubmitForm()
//
//  Event handler that is called when the login button is processed (onsubmit handler)
//	It changes the image source which causes a new HTTP request
//  It then calls the Digital Insight runSubmit() function (which is normally the onsubmit handler)
//
function GAsubmitForm( ){
	form = document.getElementById(login_form_id); 
	form.action = action_url;
	form.method = "post";

	if( !submit_performed ){
		submit_performed = true;
		form.submit();
	}
}

//////////////////////
//  FUNCTION
//  GAsubmit_handler()
//
function GAsubmit_handler( ){
	if( submit_ready_flag ){ 		// Submit the form.  Make sure it is not submitted twice
		submit_ready_flag = false;
		GAsubmitForm( );
		return(true);
	}
	else{ 							// The form is not ready or has already been submitted
		return(false);
	}
}

//////////////////////
//  FUNCTION
//  gaGetTime()
//
//  Simple function to get the browser time 
// 	Should be ms since EPOCH
function gaGetTime(){
	var d = new Date();
	return d.getTime() ;
}

//////////////////////
//  FUNCTION
//  Client Cookie Functions
//
function GACreateClientCookie(name, days) {
	var date = new Date();
	var time = date.getTime();
	date.setTime(time + (days*24*60*60*1000));
	var expires = "; expires="+date.toGMTString();
	value = sha1(time + Math.random() + Math.random() );	
	document.cookie = name+"="+value+expires+"; path=/";
	return value;
}
function GAGetCookie(name) {
	var nameEQ = name + "=";
	var ca = document.cookie.split(';');
	for(var i=0;i < ca.length;i++) {
		var c = ca[i];
		while (c.charAt(0)==' ') c = c.substring(1,c.length);
		if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
	}
	return null;
}



///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
//
//	The following is code to generate a SHA-1 hash
//
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
function sha1(msg)
{
    // constants [4.2.1]
    var K = [0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6];

    // PREPROCESSING 
 
    msg += String.fromCharCode(0x80); // add trailing '1' bit to string [5.1.1]

    // convert string msg into 512-bit/16-integer blocks arrays of ints [5.2.1]
    var l = Math.ceil(msg.length/4) + 2;  // long enough to contain msg plus 2-word length
    var N = Math.ceil(l/16);              // in N 16-int blocks
    var M = new Array(N);
    for (var i=0; i<N; i++) {
        M[i] = new Array(16);
        for (var j=0; j<16; j++) {  // encode 4 chars per integer, big-endian encoding
            M[i][j] = (msg.charCodeAt(i*64+j*4)<<24) | (msg.charCodeAt(i*64+j*4+1)<<16) | 
                      (msg.charCodeAt(i*64+j*4+2)<<8) | (msg.charCodeAt(i*64+j*4+3));
        }
    }
    // add length (in bits) into final pair of 32-bit integers (big-endian) [5.1.1]
    M[N-1][14] = ((msg.length-1) >>> 30) * 8;
    M[N-1][15] = ((msg.length-1)*8) & 0xffffffff;

    // set initial hash value [5.3.1]
    var H0 = 0x67452301;
    var H1 = 0xefcdab89;
    var H2 = 0x98badcfe;
    var H3 = 0x10325476;
    var H4 = 0xc3d2e1f0;

    // HASH COMPUTATION [6.1.2]

    var W = new Array(80); var a, b, c, d, e;
    for (var i=0; i<N; i++) {

        // 1 - prepare message schedule 'W'
        for (var t=0;  t<16; t++) W[t] = M[i][t];
        for (var t=16; t<80; t++) W[t] = sha1_rotl(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16], 1);

        // 2 - initialise five working variables a, b, c, d, e with previous hash value
        a = H0; b = H1; c = H2; d = H3; e = H4;

        // 3 - main loop
        for (var t=0; t<80; t++) {
            var s = Math.floor(t/20); // seq for blocks of 'f' functions and 'K' constants
            var T = (sha1_rotl(a,5) + sha1_f(s,b,c,d) + e + K[s] + W[t]) & 0xffffffff;
            e = d;
            d = c;
            c = sha1_rotl(b, 30);
            b = a;
            a = T;
        }

        // 4 - compute the new intermediate hash value
        H0 = (H0+a) & 0xffffffff;  // note 'addition modulo 2^32'
        H1 = (H1+b) & 0xffffffff; 
        H2 = (H2+c) & 0xffffffff; 
        H3 = (H3+d) & 0xffffffff; 
        H4 = (H4+e) & 0xffffffff;
    }

    return H0.toHexStr() + H1.toHexStr() + H2.toHexStr() + H3.toHexStr() + H4.toHexStr();
}

//
// function 'f' [4.1.1]
//
function sha1_f(s, x, y, z) 
{
    switch (s) {
    case 0: return (x & y) ^ (~x & z);
    case 1: return x ^ y ^ z;
    case 2: return (x & y) ^ (x & z) ^ (y & z);
    case 3: return x ^ y ^ z;
    }
}

//
// rotate left (circular left shift) value x by n positions [3.2.5]
//
function sha1_rotl(x, n)
{
    return (x<<n) | (x>>>(32-n));
}

//
// extend Number class with a tailored hex-string method 
//   (note toString(16) is implementation-dependant, and 
//   in IE returns signed numbers when used on full words)
//
Number.prototype.toHexStr = function()
{
    var s="", v;
    for (var i=7; i>=0; i--) { v = (this>>>(i*4)) & 0xf; s += v.toString(16); }
    return s;
}

