//
// MD5 hash, as specified in RFC 1321
//
var md5_acc = new Array;
var md5_S11 = 7;
var md5_S12 = 12;
var md5_S13 = 17;
var md5_S14 = 22;
var md5_S21 = 5;
var md5_S22 = 9;
var md5_S23 = 14;
var md5_S24 = 20;
var md5_S31 = 4;
var md5_S32 = 11;
var md5_S33 = 16;
var md5_S34 = 23;
var md5_S41 = 6;
var md5_S42 = 10;
var md5_S43 = 15;
var md5_S44 = 21;
      	
function md5_init() {
	//
	// initialize accumulator ready for a message
	//
	md5_acc[0] = 0x67452301;
	md5_acc[1] = 0xefcdab89;
	md5_acc[2] = 0x98badcfe;
	md5_acc[3] = 0x10325476;
}
        	
function md5_F(x,y,z) {
	//
	// if b(X) then b(Y) else b(Z)
	//
	var r = (x & y) | ((~x) & z);
	return r;
}

function md5_G(x,y,z) {
	var r = (x & z) | (y & (~z));
	return r;
}

function md5_H(x,y,z) {
	var r = x ^ y ^ z;
	return r;
}

function md5_I(x,y,z) {
	var r = y ^ (x | (~z));
	return r;
}
        	
function md5_ROL(x,n) {
    var m = (1<<n)-1;
    var r = ((x<<n)&~m) | ((x >>(32-n))&m);
    return r;
}
        	
function md5_add4(a,b,c,d) {
	//
	// safe 32-bit mod 32 add (up to 4 numbers)
	//
	var ah = (a>>16) & 0xffff;
	var bh = (b>>16) & 0xffff;
	var ch = (c>>16) & 0xffff;
	var dh = (d>>16) & 0xffff;
	var h = ah+bh+ch+dh;
	var l = (a&0xffff)+(b&0xffff)+(c&0xffff)+(d&0xffff);
	h += (l>>16)&0xffff;
	var r = ((h & 0xffff)<<16)|(l & 0xffff);
	return r;
}
        	
function md5_add3(a,b,c) {
	return md5_add4(a,b,c,0);
}
        	
function md5_add2(a,b) {
	return md5_add4(a,b,0,0);
}
        	
function md5_FF(a,b,c,d,x,s,ac) {
	a = md5_add4(a,md5_F(b,c,d),x,ac);
	a = md5_ROL(a,s);
	a = md5_add2(a,b);
	return a;
}
        	
function md5_GG(a,b,c,d,x,s,ac) {
	a = md5_add4(a,md5_G(b,c,d),x,ac);
	a = md5_ROL(a,s);
	a = md5_add2(a,b);
	return a;
}
        	
function md5_HH(a,b,c,d,x,s,ac) {
	a = md5_add4(a,md5_H(b,c,d),x,ac);
	a = md5_ROL(a,s);
	a = md5_add2(a,b);
	return a;
}

function md5_II(a,b,c,d,x,s,ac) {
	a = md5_add4(a,md5_I(b,c,d),x,ac);
	a = md5_ROL(a,s);
	a = md5_add2(a,b);
	return a;
}
        	
function md5_calc(data,len) {
	//
	// data is an array that has multiple of 16 words (512 bits)
	// len is actual length of the data (words)
	//
	var base;
	 
	for(base=0;base<len;base+=16) {
    	var a = md5_acc[0]; 
    	var b = md5_acc[1]; 
    	var c = md5_acc[2]; 
    	var d = md5_acc[3];
    	// Round 1
    	a = md5_FF(a, b, c, d, data[base+ 0], md5_S11, 0xd76aa478);
    	d = md5_FF(d, a, b, c, data[base+ 1], md5_S12, 0xe8c7b756);
    	c = md5_FF(c, d, a, b, data[base+ 2], md5_S13, 0x242070db);
    	b = md5_FF(b, c, d, a, data[base+ 3], md5_S14, 0xc1bdceee);
    	a = md5_FF(a, b, c, d, data[base+ 4], md5_S11, 0xf57c0faf);
    	d = md5_FF(d, a, b, c, data[base+ 5], md5_S12, 0x4787c62a);
    	c = md5_FF(c, d, a, b, data[base+ 6], md5_S13, 0xa8304613);
    	b = md5_FF(b, c, d, a, data[base+ 7], md5_S14, 0xfd469501);
    	a = md5_FF(a, b, c, d, data[base+ 8], md5_S11, 0x698098d8);
    	d = md5_FF(d, a, b, c, data[base+ 9], md5_S12, 0x8b44f7af);
    	c = md5_FF(c, d, a, b, data[base+10], md5_S13, 0xffff5bb1);
    	b = md5_FF(b, c, d, a, data[base+11], md5_S14, 0x895cd7be);
    	a = md5_FF(a, b, c, d, data[base+12], md5_S11, 0x6b901122);
    	d = md5_FF(d, a, b, c, data[base+13], md5_S12, 0xfd987193);
    	c = md5_FF(c, d, a, b, data[base+14], md5_S13, 0xa679438e);
    	b = md5_FF(b, c, d, a, data[base+15], md5_S14, 0x49b40821);
    	// Round 2
    	a = md5_GG(a, b, c, d, data[base+ 1], md5_S21, 0xf61e2562);
    	d = md5_GG(d, a, b, c, data[base+ 6], md5_S22, 0xc040b340);
    	c = md5_GG(c, d, a, b, data[base+11], md5_S23, 0x265e5a51);
    	b = md5_GG(b, c, d, a, data[base+ 0], md5_S24, 0xe9b6c7aa);
    	a = md5_GG(a, b, c, d, data[base+ 5], md5_S21, 0xd62f105d);
    	d = md5_GG(d, a, b, c, data[base+10], md5_S22, 0x02441453);
    	c = md5_GG(c, d, a, b, data[base+15], md5_S23, 0xd8a1e681);
    	b = md5_GG(b, c, d, a, data[base+ 4], md5_S24, 0xe7d3fbc8);
    	a = md5_GG(a, b, c, d, data[base+ 9], md5_S21, 0x21e1cde6);
    	d = md5_GG(d, a, b, c, data[base+14], md5_S22, 0xc33707d6);
    	c = md5_GG(c, d, a, b, data[base+ 3], md5_S23, 0xf4d50d87);
    	b = md5_GG(b, c, d, a, data[base+ 8], md5_S24, 0x455a14ed);
    	a = md5_GG(a, b, c, d, data[base+13], md5_S21, 0xa9e3e905);
    	d = md5_GG(d, a, b, c, data[base+ 2], md5_S22, 0xfcefa3f8);
    	c = md5_GG(c, d, a, b, data[base+ 7], md5_S23, 0x676f02d9);
    	b = md5_GG(b, c, d, a, data[base+12], md5_S24, 0x8d2a4c8a);
    	// Round 3
    	a = md5_HH(a, b, c, d, data[base+ 5], md5_S31, 0xfffa3942);
    	d = md5_HH(d, a, b, c, data[base+ 8], md5_S32, 0x8771f681);
    	c = md5_HH(c, d, a, b, data[base+11], md5_S33, 0x6d9d6122);
    	b = md5_HH(b, c, d, a, data[base+14], md5_S34, 0xfde5380c);
    	a = md5_HH(a, b, c, d, data[base+ 1], md5_S31, 0xa4beea44);
    	d = md5_HH(d, a, b, c, data[base+ 4], md5_S32, 0x4bdecfa9);
    	c = md5_HH(c, d, a, b, data[base+ 7], md5_S33, 0xf6bb4b60);
    	b = md5_HH(b, c, d, a, data[base+10], md5_S34, 0xbebfbc70);
    	a = md5_HH(a, b, c, d, data[base+13], md5_S31, 0x289b7ec6);
    	d = md5_HH(d, a, b, c, data[base+ 0], md5_S32, 0xeaa127fa);
    	c = md5_HH(c, d, a, b, data[base+ 3], md5_S33, 0xd4ef3085);
    	b = md5_HH(b, c, d, a, data[base+ 6], md5_S34, 0x04881d05);
    	a = md5_HH(a, b, c, d, data[base+ 9], md5_S31, 0xd9d4d039);
    	d = md5_HH(d, a, b, c, data[base+12], md5_S32, 0xe6db99e5);
    	c = md5_HH(c, d, a, b, data[base+15], md5_S33, 0x1fa27cf8);
    	b = md5_HH(b, c, d, a, data[base+ 2], md5_S34, 0xc4ac5665);
    	// Round 4
    	a = md5_II(a, b, c, d, data[base+ 0], md5_S41, 0xf4292244);
    	d = md5_II(d, a, b, c, data[base+ 7], md5_S42, 0x432aff97);
    	c = md5_II(c, d, a, b, data[base+14], md5_S43, 0xab9423a7);
    	b = md5_II(b, c, d, a, data[base+ 5], md5_S44, 0xfc93a039);
    	a = md5_II(a, b, c, d, data[base+12], md5_S41, 0x655b59c3);
    	d = md5_II(d, a, b, c, data[base+ 3], md5_S42, 0x8f0ccc92);
    	c = md5_II(c, d, a, b, data[base+10], md5_S43, 0xffeff47d);
    	b = md5_II(b, c, d, a, data[base+ 1], md5_S44, 0x85845dd1);
    	a = md5_II(a, b, c, d, data[base+ 8], md5_S41, 0x6fa87e4f);
    	d = md5_II(d, a, b, c, data[base+15], md5_S42, 0xfe2ce6e0);
    	c = md5_II(c, d, a, b, data[base+ 6], md5_S43, 0xa3014314);
    	b = md5_II(b, c, d, a, data[base+13], md5_S44, 0x4e0811a1);
    	a = md5_II(a, b, c, d, data[base+ 4], md5_S41, 0xf7537e82);
    	d = md5_II(d, a, b, c, data[base+11], md5_S42, 0xbd3af235);
    	c = md5_II(c, d, a, b, data[base+ 2], md5_S43, 0x2ad7d2bb);
    	b = md5_II(b, c, d, a, data[base+ 9], md5_S44, 0xeb86d391);
    	//
    	// end rounds
    	//
    	md5_acc[0] = md5_add2(md5_acc[0],a);
    	md5_acc[1] = md5_add2(md5_acc[1],b);
    	md5_acc[2] = md5_add2(md5_acc[2],c);
    	md5_acc[3] = md5_add2(md5_acc[3],d);
	}
}
        	
function md5_hashString(str) {
	//
	// add a string to the hash
	// assume string is unicode
	// note that although each individual string is MD5 hashed
	// the composite of adding two strings is not the same
	// as adding the combined string
	//
	var msgArray = new Array;
	var msgLen = 0;
	var index = 0;
	var sub = 0;
	var acc = 0;
	var mul;
	var gap;
	for(index = 0;index <= str.length ; index+=2) {
		gap = (str.length-index);
		if(gap>=2) {
			acc = (str.charCodeAt(index)&0xffff) |
				  ((str.charCodeAt(index+1)&0xffff) << 16);	        					
		} else if(gap==1) {
			acc = (str.charCodeAt(index)&0xffff) |
				  0x800000; // LSbyte of upper 16 bits has first bit set.	        					
		} else {
			acc = 0x80; // LSbyte of lower 16 bits has first bit set
		}
		msgArray[msgLen] = acc;
		msgLen++;
	}
	//
	// now padding
	//
	var padInc = 16-(msgLen % 16);
	if(padInc < 2) {
		padInc += 16;
	}
	padInc -= 2; // number of words to pad
	while(padInc > 0) {
		msgArray[msgLen] = 0;
		msgLen++;
		padInc--;
    }
    //
    // number of bits
    //
    msgArray[msgLen] = str.length*16;
    msgArray[msgLen+1] = 0;
    msgLen += 2;
    //
    // now go ahead and calc
    //
    md5_calc(msgArray,msgLen);
}
        	
function md5_getHexPartDigit(val) {
	if((val>=0) && (val<=9)) {
		return String.fromCharCode(val+48);
	} else {
		return String.fromCharCode(val-10+65);
	}
}
        	
function md5_getHexPartByte(val) {
	return md5_getHexPartDigit((val>>4)&0xf)+md5_getHexPartDigit(val&0xf);
}
        	
function md5_getHexPartWord(val) {
	return md5_getHexPartByte(val&0xff)+
			md5_getHexPartByte((val>>8)&0xff)+
			md5_getHexPartByte((val>>16)&0xff)+
			md5_getHexPartByte((val>>24)&0xff);        				
}
        	
function md5_getHex() {
	//
	// get the accumulated md5 data as hex
	//
	return md5_getHexPartWord(md5_acc[0])+
			md5_getHexPartWord(md5_acc[1])+
			md5_getHexPartWord(md5_acc[2])+
			md5_getHexPartWord(md5_acc[3]);
}

function md5_initHexPartWord(part,str) {
	//
	// abcdefgh
	// -> ghefcdab
	//
	// mul = 0, nibble = 4, shift = 4
	// mul = 4, nibble = -4, shift = 0
	// mul = 8, nibble = 4, shift = 12
	// mul = 12, nubble = -4, shift = 8
	// etc
	//
	var index;
	var acc = 0;
	var mul = 0;
	var nibble = 4;
	for(index = 0;index<8;index++) {
		var c = str.charCodeAt(index);
		var cc;
		if((c>=48) && (c<=57)) {
			cc = (c-48);
		} else if((c>=65) && (c<=70)) {
			cc = (c-65+10);
		} else {
			return false;
		}
		acc |= (cc << (mul+nibble));
		nibble = -nibble;
		mul+=4;
	}
	md5_acc[part] = acc;
	return true;
}

function md5_initHex(str) {
	//
	// init accumulator from jex
	//
	if(str.length != 32) {
		md5_init();
		return false;
	}
	if(md5_initHexPartWord(0,str.substr(0,8)) &&
		md5_initHexPartWord(1,str.substr(8,8)) &&
		md5_initHexPartWord(2,str.substr(16,8)) &&
		md5_initHexPartWord(3,str.substr(24,8))) {
		return true;
	} else {
		md5_init();
		return false;
	}
}

function HexCharVal(a,i) {
	var c = a.charCodeAt(i);
	var cc;
	if((c>=48) && (c<=57)) {
		cc = (c-48);
	} else if((c>=65) && (c<=70)) {
		cc = (c-65+10);
	} else {
		cc = 0;
	}
	return cc;
}

function HexToChar(val) {
	if((val>=0) && (val<=9)) {
		return String.fromCharCode(val+48);
	} else {
		return String.fromCharCode(val-10+65);
	}
}        				

function XorStrings2(a,b) {
	var i;	
	var r = "";											
	for(i=0;i<a.length;i++) {
		var cc = (HexCharVal(a,i)^HexCharVal(b,i))&0x0f;
		r += HexToChar(cc);
	}
	return r;
}

function EncodePassword(pass,cookie) {
    //
    // what is sent is a hash based on password and cookie
    // knowing password and cookie gets same hash
    //
    md5_init();
    md5_hashString(pass);   // stored password hash
    md5_hashString(cookie); // encode it for transfer
    return md5_getHex(); // what we need to send
}

function EncodeNewPassword(pass,cookie,newpass) {
    //
    // what is sent is a hash1 xor'd with hash2
    // hash2 is derivable from passhash & cookie
    // but not from encoded password
    //
    md5_init();
    md5_hashString(pass);   // stored password hash
    md5_hashString(cookie); // encode it for transfer
    md5_hashString("*SetPassword*"); // special
    var xorenc = md5_getHex(); // our mangler
    md5_init();
    md5_hashString(newpass);   // stored password hash
    var newenc = md5_getHex(); // what we need to send
    return XorStrings2(xorenc,newenc); // mangled version that we send
}


//
// this is a sanity check
//
var password_js_loaded = true;
