// Copyright © 2001-2003 Ubicom Inc. <www.ubicom.com>.  All rights reserved.
// Author: Russell Smith.

// NOTES:
//   * don't use array.push(), it is not supported in Explorer v5.0.
//     use array.concat() instead.

// base 64 encoding is used, except for the following changes:
//   * the symbols . and _ are used instead of + and /
//   * the binary byte string is assumed to be a multiple of 3 bytes, so that
//     the base-64 string is a multiple of 4 characters. if it is not, it is
//     padded with 0's. no '=' symbols are used to mark the end of the encoded
//     string.

// convert an array of 8-bit bytes into a base-64 encoded string

function convertToBase64 (a)
{
	while (a.length % 3) a = a.concat (0);	// make 'a' a multiple of 3
	var b64 = ".ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_";
	var b = "";
	for (var i=0; i < a.length; i += 3) {
		var x = (a[i] << 16) | (a[i+1] << 8) | a[i+2];
		b += b64.charAt((x >> 18) & 0x3f);
		b += b64.charAt((x >> 12) & 0x3f);
		b += b64.charAt((x >> 6) & 0x3f);
		b += b64.charAt(x & 0x3f);
	}
	return b;
}


function charToSixBits (c)
{
	if (c=='.') return 0;
	if (c >= 'A' && c <= 'Z')
		return c.charCodeAt(0) + 1 - "A".charCodeAt(0);
	if (c >= 'a' && c <= 'z')
		return c.charCodeAt(0) + 27 - "a".charCodeAt(0);
	if (c >= '0' && c <= '9')
		return c.charCodeAt(0) + 53 - "0".charCodeAt(0);
	return 63;
}


// read base-64 encoded binary data from the string `buf', and return
// the resulting array of 8-bit numbers. invalid characters in the string will
// be interpreted as 'A'.

function convertFromBase64 (buf)
{
	while (buf.length & 3) buf += 'A';	// make 'buf' a multiple of 4
	var dst = new Array;

	for (var i = 0; i < buf.length; i += 4) {
		var cc = (charToSixBits (buf.charAt(i)) << 18) |
			 (charToSixBits (buf.charAt(i+1)) << 12) |
			 (charToSixBits (buf.charAt(i+2)) << 6) |
			 (charToSixBits (buf.charAt(i+3)));
		dst = dst.concat ((cc >> 16) & 0xff);
		dst = dst.concat ((cc >> 8) & 0xff);
		dst = dst.concat ((cc) & 0xff);
	}
	return dst;
}


// convert a string to a byte array

function stringToByteArray (s,len)
{
	var a = new Array;
	var l = s.length;
	if (l > len) l = len;
	for (var i=0; i < l; i++) {
		a = a.concat (s.charCodeAt (i));
	}
	for (var i=l; i < len; i++) {
		a = a.concat (0);
	}
	a = a.concat (0);
	return a;
}


// convert a byte array to a string.

function byteArrayToString (b)
{
	var s = "";
	for (var i=0; i < b.length; i++) {
		if (b[i]) s += String.fromCharCode (b[i]);
		else break;
	}
	return s;
}


// convert a hex string of 'len' bytes into a byte array of 'len2' bytes.
// ignore leading and trailing whitespace. if there are any conversion errors
// or the string is the wrong length then return 0, else return the
// byte array. if the string is empty or all whitespace, return an array of
// all zeros.

function convertHexString (s,len,len2)
{
	if (s.match (/^\s*$/)) {
		var a = new Array;
		for (var i=0; i<len2; i++) a = a.concat (0);
		return a;
	}
	var got = s.match (/^\s*([0-9a-fA-F]+)\s*$/);
	if (got) {
		if (got[1].length != len*2) return 0;
		var a = new Array;
		var i;
		for (i=0; i < len; i++) {
			a = a.concat (parseInt(got[1].substr(i*2,2),16));
		}
		for (; i < len2; i++) {
			a = a.concat (0);
		}
		return a;
	}
	else return 0;
}


function convertIPAddress (s)
{
	var got = s.match (/^\s*(\d+)\s*[.]\s*(\d+)\s*[.]\s*(\d+)\s*[.]\s*(\d+)\s*$/);
	if (got) {
		var a = new Array;
		for (var i=1; i <= 4; i++) {
			var q = parseInt(got[i]);
			if (q < 0 || q > 255) return 0;
			a = a.concat (q);
		}
		return a;
	}
	else return 0;
}


function byteArrayToHexString (a)
{
	var s = "";
	for (var i=0; i < a.length; i++) {
		var s2 = a[i].toString(16);
		if (s2.length < 2) s2 = '0' + s2;
		s += s2;
	}
	return s.toUpperCase();
}


// same as byteArrayToHexString() but an all-zero array will be converted
// into a blank string.

function byteArrayToHexString2 (a)
{
	for (var i=0; i < a.length; i++) {
		if (a[i] != 0) return byteArrayToHexString (a);
	}
	return "";
}


function byteArrayToIPAddress (a)
{
	var s = "";
	for (var i=0; i < 4; i++) {
		s += a[i].toString(10);
		if (i < 3) s += ".";
	}
	return s;
}

function byteArrayToMACAddress (a)
{
	var s = "";
	for (var i=0; i < 6; i++) {
		var s2 = a[i].toString(16);
		if (s2.length < 2) s2 = '0' + s2;
		s += s2;
		// if (i < 5) s += ":";
	}
	return s.toUpperCase();
}

// a is the byte array, b is the number of digits
function byteArrayToNumber (a,b)
{
	var num = 0;
	for (var i=0; i < b; i++) {
		num = (num << 8);
		num += a[i];
	}
	return num;
}

function byteArrayToVersion(a)
{
	var s = "";
	s += a[0];
	s += ".";
	s += a[1];
	s += ".";
	s += byteArrayToString(a.slice(2,3));
	s += a[3];
	s += "."
	s += byteArrayToU32(a.slice(4, 8));

	return s;
}

function byteArrayToU8(a)
{
	var x = parseInt(a[0]);
	return (isNaN(x)?-1:x);
}

function byteArrayToU16(a)
{
	var x = (parseInt(a[0])*256) + parseInt(a[1]);
	return (isNaN(x)?-1:x);
}

function byteArrayToU32(a)
{
	var x = (parseInt(a[0])*16777216) + (parseInt(a[1])*65536) + (parseInt(a[2])*256) + parseInt(a[3]);
	return (isNaN(x)?-1:x);
}

function U32toByteArray(n, size)
{
	var a = new Array();

	var j;
	for (j=0; j<size; j++) {
		a = a.concat ((n >> ((size-1-j) * 8)) & 0xff);
	}
	return a;
}

// Returns decimal equivalent for input IP address array
function ConvertIPAddressArrayToInt(a)
{
	//Convert address to hex string
	var s = "";
	for (var i = 0; i < a.length ; i++)
	{
		var t = a[i].toString(16);
		if ((t.length < 0) || (t.length > 2))
		{
			return 0;
		}
		
		// Aline address to 1 whole byte
		if (t.length == 1)
		{
			t = "0" + a[i].toString(16);
		}
		s = s + t
	} 
	
	// Return decimal equivalent
	return(parseInt(s, 16));	
}

//***************************************************************************
// data setter/getter functions.
// these assume that the globals 'a' and 'i' are defined.
// parameter conventions:
//	item - the number of the 'Q' form element to set/get.
//	size - the size of the config block integer to set/get.
//	list - an array of numerical values corresponding selectedIndex.

// utility function to get an integer from the config block

function getInt (size)
{
	var n=0;
	var j;
	for (j=0; j<size; j++) {
		n = (n<<8) + a[i];
		i++;
	}
	return n;
}


// utility function to append an integer to the config block

function setInt (n,size)
{
	var j;
	for (j=0; j<size; j++) {
		a = a.concat ((n >> ((size-1-j)*8)) & 0xff);
	}
}


// set string

function sS (item,len)
{
	var j = byteArrayToString (a.slice (i,i+len));
	i += len+1;
	eval('document.myform.Q'+item+'.value = j;');
}


// get string

function gS(item,len)
{
	eval('a = a.concat (stringToByteArray (document.myform.Q'+item+'.value,'+len+'));');
}


// set password string

function sP (item,len)
{
	var j = byteArrayToString (a.slice (i,i+len));
	i += len+1;
	eval('document.myform.Q'+item+'.value = j;');
	eval('document.myform.Q'+item+'b.value = j;');
}


// get password string

function gP(item,len)
{
	var test = eval('document.myform.Q'+item+'.value != document.myform.Q'+item+'b.value');
	if (test) {
		alert ("The two passwords do not match,\nyou must enter the same password twice.");
		return 0;
	}
	eval('a = a.concat (stringToByteArray (document.myform.Q'+item+'.value,'+len+'));');
	return 1
}


// set selection (multiple choice)

function sM(item,size,list)
{
	var k;
	var n = getInt (size);
	var j = -1;
	for (k=0; k<list.length; k++) {
		if (n == list[k]) {
			j = k;
			break;
		}
	}
	eval ('document.myform.Q'+item+'.selectedIndex = j;');
}


// get selection (multiple choice)

function gM(item,size,list)
{
	var n;
	eval ('n = document.myform.Q'+item+'.selectedIndex;');
	if (n >= 0 && n < list.length) setInt (list[n],size); else setInt (0,size);
}


// set linear selection

function sL(item,size,list0)
{
	eval ('document.myform.Q'+item+'.selectedIndex = getInt(size)-list0;');
}


// get linear selection

function gL(item,size,list0)
{
	var n;
	eval ('n = document.myform.Q'+item+'.selectedIndex;');
	setInt (n+list0,size);
}


// set radio button selection

function sR(item,size,list)
{
	var n = getInt (size);
	var k;
	for (k=0; k<list.length; k++) {
		eval ('document.myform.Q'+item+'[k].checked = (n == list[k]);');
	}
}


// get radio button selection

function gR(item,size,list)
{
	var k,n=0;
	for (k=0; k<list.length; k++) {
		if (eval ('document.myform.Q'+item+'[k].checked')) n = list[k];
	}
	setInt (n,size);
}


// set integer

function sI(item,size)
{
	eval ('document.myform.Q'+item+'.value = getInt(size);');
}


// get integer

function gI(item,size)
{
	var n = eval ('document.myform.Q'+item+'.value');
	setInt (n,size);
	return n;
}


// set boolean (checkbox)

function sB(item)
{
	eval ('document.myform.Q'+item+'.checked = getInt(1);');
}


// get boolean (checkbox)

function gB(item)
{
	eval ('setInt (Number(document.myform.Q'+item+'.checked),1);');
}


// set hex string

function sH (item,len)
{
	var j = byteArrayToHexString (a.slice (i,i+len));
	i += len;
	eval('document.myform.Q'+item+'.value = j;');
}


// set hex string 2

function sH2 (item,len)
{
	var j = byteArrayToHexString2 (a.slice (i,i+len));
	i += len;
	eval('document.myform.Q'+item+'.value = j;');
}


// get hex string

function gH (item,len,len2)
{
	var hs = eval('convertHexString (document.myform.Q'+item+'.value,len,len2);');
	if (!hs) {
		alert ("A hexadecimal string is not valid");
		return 0;
	}
	a = a.concat (hs);
	return 1;
}


// post process a hex string (truncate its length to len)

function pH (item,len)
{
	eval ('document.myform.Q'+item+'.value = document.myform.Q'+item+'.value.substr (0,2*len);');
}


// set IP address

function sX(item)
{
	eval ('document.myform.Q'+item+'.value = byteArrayToIPAddress (a.slice (i,i+4));');
	i+=4;
}


// get IP address

function gX(item)
{
	var ip;
	eval ('ip = convertIPAddress (document.myform.Q'+item+'.value);');
	if (ip == 0) {
		alert ("An IP address is not valid");
		return 0;
	}
	a = a.concat (ip);
	return 1;
}
