// This script is (c) copyright 2006 Jim Tucek under the
// GNU General Public License (http://www.gnu.org/licenses/gpl.html)
// For more information, visit www.jracademy.com/~jtucek/email/ 
// Leave the above comments alone!

var decryption_cache = new Array();

function decrypt_string(crypted_string,n,decryption_key,just_email_address) {
	var cache_index = "'"+crypted_string+","+just_email_address+"'";

	if(decryption_cache[cache_index])					// If this string has already been decrypted, just
		return decryption_cache[cache_index];				// return the cached version.

	if(addresses[crypted_string])						// Is crypted_string an index into the addresses array
		var crypted_string = addresses[crypted_string];			// or an actual string of numbers?

	if(!crypted_string.length)						// Make sure the string is actually a string
		return "Error, not a valid index.";

	if(n == 0 || decryption_key == 0) {					// If the decryption key and n are not passed to the
		var numbers = crypted_string.split(' ');			// function, assume they are stored as the first two
		n = numbers[0];	decryption_key = numbers[1];			// numbers in crypted string.
		numbers[0] = ""; numbers[1] = "";				// Remove them from the crypted string and continue
		crypted_string = numbers.join(" ").substr(2);
	}

	var decrypted_string = '';
	var crypted_characters = crypted_string.split(' ');

	for(var i in crypted_characters) {
		var current_character = crypted_characters[i];
		var decrypted_character = exponentialModulo(current_character,n,decryption_key);
		if(just_email_address && i < 7)				// Skip 'mailto:' part
			continue;
		if(just_email_address && decrypted_character == 63)	// Stop at '?subject=....'
			break;
		decrypted_string += String.fromCharCode(decrypted_character);
	}
	
	decryption_cache[cache_index] = decrypted_string;			// Cache this string for any future calls

	return decrypted_string;
}

function decrypt_and_email(crypted_string,n,decryption_key) {
	if(!n || !decryption_key) { n = 0; decryption_key = 0; }
	if(!crypted_string) crypted_string = 0;

	var decrypted_string = decrypt_string(crypted_string,n,decryption_key,false);
	parent.location = decrypted_string;
}

function decrypt_and_echo(crypted_string,n,decryption_key) {
	if(!n || !decryption_key) { n = 0; decryption_key = 0; }
	if(!crypted_string) crypted_string = 0;

	var decrypted_string = decrypt_string(crypted_string,n,decryption_key,true);
	document.write(decrypted_string);
	return true;
}

// Finds base^exponent % y for large values of (base^exponent)
function exponentialModulo(base,exponent,y) {
	if (y % 2 == 0) {
		answer = 1;
		for(var i = 1; i <= y/2; i++) {
			temp = (base*base) % exponent;
			answer = (temp*answer) % exponent;
		}
	} else {
		answer = base;
		for(var i = 1; i <= y/2; i++) {
			temp = (base*base) % exponent;
			answer = (temp*answer) % exponent;
		}
	}
	return answer;
}
// -->

function makeKey(message, subject, p, q) {

	var primes = new Array();
	var i = 0; primes[i++] = 2; primes[i++] = 3; primes[i++] = 5; primes[i++] = 7; 
	primes[i++] = 11; primes[i++] = 13; primes[i++] = 17; primes[i++] = 19; primes[i++] = 23; 
	primes[i++] = 29; primes[i++] = 31; primes[i++] = 37; primes[i++] = 41; primes[i++] = 43; 
	primes[i++] = 47; primes[i++] = 53; primes[i++] = 59; primes[i++] = 61; primes[i++] = 67; 
	primes[i++] = 71; primes[i++] = 73; primes[i++] = 79; primes[i++] = 83; primes[i++] = 89; 
	primes[i++] = 97; primes[i++] = 101; primes[i++] = 103; primes[i++] = 107; primes[i++] = 109; 
	primes[i++] = 113; primes[i++] = 127; primes[i++] = 131; primes[i++] = 137; primes[i++] = 139; 
	primes[i++] = 149; primes[i++] = 151; primes[i++] = 157; primes[i++] = 163; primes[i++] = 167; 
	primes[i++] = 173; primes[i++] = 179; primes[i++] = 181; primes[i++] = 191; primes[i++] = 193; 
	primes[i++] = 197; primes[i++] = 199; 

	addresses = new Array();

	if(p * q < 255)
		window.alert("P*Q must be greater than 255! P*Q = " + p*q);
	else if(p == q)
		window.alert("P cannot be equal to Q!");
	else if(message == "") 
		window.alert("You must enter an address to encrypt!");
	else {
		// Make the key
		var c = 0;
		var z = (p-1)*(q-1);
		var e = 0;
		var n = p*q;
		var d;

		do {
			e++;
			d = getKey(primes[e],z);
		} while(d==1);
		e = primes[e];
	
		// Split up each line of email addresses
		var lines = message.split('\n');

		// Turn each string into an array of numbers < 255
		for(lineNumber in lines) {
			if(!lines[lineNumber].length || lines[lineNumber].length < 2)
				continue;

			var m = "mailto:" + lines[lineNumber];
			var emailLength = m.length;
		
			if(subject != "")
				m = m + "?subject=" + subject;

			var length = m.length;
			theString = new Array(length);
			for(var i = 0; i < length; i++)
				theString[i] = m.charCodeAt(i);
	
			// Encrypt each of the numbers
			c = "";
			for(i = 0; i < length; i++) {
				if(i != 0)
					c+= " ";
				c += myMod(theString[i],e,n);
			}

			// Save this line
			addresses.push(c);
		}
		c = "";
		for(i in addresses) {
			c += addresses[i];
			if(i < addresses.length - 1)
				c += "\n";
		}

		document.write(n+' '+d+' '+c);
	}
}

// Euclid's Algorithm for finding a good key
function getKey(e,z) {
	A = 1;B = 0;C = z;F = 0;G = 1;bar = e;    
	while (bar != 0) {
	     foo = Math.floor(C/bar);
	     K = A - foo * F;
	     L = B - foo * G;
	     M = C - foo * bar;
	     A = F;B = G;C = bar;
	     F = K;G = L;bar = M;
	}
	if (B < 0)
		return (B + z);
	else
	      return (B);
}
// Finds x^e % y for large values of (x^e)
function myMod(x,e,y) {
	if (e % 2 == 0) {
		var answer = 1;
		for(var i = 1; i <= e/2; i++) {
			var temp = (x*x) % y;
			answer = (temp*answer) % y;
		}
	} else {
		var answer = x;
		for(var i = 1; i <= e/2; i++) {
			var temp = (x*x) % y;
			answer = (temp*answer) % y;
		}
	}
	return answer;
}

