// ==UserScript==
// @name          KeyGenius
// @namespace     http://kg.yubico.com
// @description   Enables the use of a YubiKey to log in to any web site, by storing passwords on a secure server.
// @version 0.8.6
// @include       *
// ==/UserScript==

/**
 * KeyGenius http://kg.yubico.com Dain Nilsson 2009-08-07
 * 
 * Released under the GPL license http://www.gnu.org/copyleft/gpl.html
 */
document.keygenius_login = {};
(function() {
	/* CONFIGURABLE VARIABLES */
	var server = 'kg.yubico.com/';
	var default_len = 16;
	var interval = 500;

	var submit = false;
	var inputs = [];
	var clones = [];
	var forms = [];
	var i = 0;
	var clone = false;
	var secure = (window.location.protocol == 'https:');
	var icon = 'url(http' + (secure ? 's://' : '://') + server
			+ 'keygenius_icon.png)';
	var custom = '';
	function listener(e) {
		e = e || window.event;
		var input = e.currentTarget || e.srcElement;
		if (e.keyCode == 13) {
			if (e.preventDefault)
				e.preventDefault();
			else
				e.returnValue = false;
			if (parse_input(input))
				submit_form(input.form);
			return false;
		}
	}
	function init() {
		inputs = document.getElementsByTagName('input');
		for ( var j = inputs.length - 1; j >= 0; j--) {
			if (inputs[j].type == 'password'
					&& inputs[j].className.indexOf('no_kg') == -1) {
				inputs[j].className += ' no_kg';
				if (inputs[j].id == '') {
					inputs[j].id = 'in_' + j;
				}
				if (inputs[j].offsetWidth) {
					clone = true;
					clones[i] = inputs[j].cloneNode(true);
					clones[i].removeAttribute('maxLength');
					clones[i].removeAttribute('name');
					clones[i].id = 'kg_' + clones[i].id;
					inputs[j].style.display = 'none';
					inputs[j].parentNode.insertBefore(clones[i], inputs[j]);
				} else {
					clones[i] = inputs[j];
					clone = false;
				}
				mark_input(clones[i]);
				add_listener(clones[i], 'keypress', listener);
				if (clone)
					add_listener(
							clones[i++],
							'keyup',
							function(e) {
								e = e || window.event;
								var input = e.currentTarget || e.srcElement;
								document.getElementById(input.id.substr(3)).value = input.value;
							});
				var f = forms.length;
				while (f--) {
					if (forms[f] == inputs[j].form)
						break;
				}
				if (f == -1) {
					forms[forms.length - 1] = inputs[j].form;
					add_listener(inputs[j].form, 'submit', submit_form);
				}
			}
		}
		setTimeout(init, interval);
	}
	init();
	function restore_all() {
		i--;
		for (; i >= 0; i--) {
			restore_orig(clones[i]);
		}
	}
	function parse_input(input) {
		var val = input.value;
		var prefix = val.substring(0, val.length - 44);
		var yubi = val.substring(val.length - 44);
		if (yubi.match('[cbdefghijklnrtuv]{44}') == null)
			return true;
		var url = 'https://' + server + '?token=' + yubi + '&url='
				+ encodeURIComponent(window.location);
		if (prefix == '=') {
			write(input, yubi);
			return false;
		} else if (prefix == '') {
			submit = true;
		} else if (prefix.charAt(0) == '#') {
			submit = true;
			custom = prefix.substr(1);
		} else if (prefix.charAt(0) == '+') {
			if (prefix == '+') {
				var pw = prompt('KeyGenius - Enter password to store.', '');
				if (pw === null) {
					input.value = '';
					return false;
				}
				url += '&store=' + encodeURIComponent(pw);
			} else {
				var len = prefix.substr(1);
				if (len == '++')
					len = default_len;
				url += '&gen=' + len;
			}
		}
		input.disabled = true;
		input.style.backgroundImage = 'url(data:image/gif;base64,R0lGODlhEAAQAPIAAP///wAAAMLCwkJCQgAAAGJiYoKCgpKSkiH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAADMwi63P4wyklrE2MIOggZnAdOmGYJRbExwroUmcG2LmDEwnHQLVsYOd2mBzkYDAdKa+dIAAAh+QQJCgAAACwAAAAAEAAQAAADNAi63P5OjCEgG4QMu7DmikRxQlFUYDEZIGBMRVsaqHwctXXf7WEYB4Ag1xjihkMZsiUkKhIAIfkECQoAAAAsAAAAABAAEAAAAzYIujIjK8pByJDMlFYvBoVjHA70GU7xSUJhmKtwHPAKzLO9HMaoKwJZ7Rf8AYPDDzKpZBqfvwQAIfkECQoAAAAsAAAAABAAEAAAAzMIumIlK8oyhpHsnFZfhYumCYUhDAQxRIdhHBGqRoKw0R8DYlJd8z0fMDgsGo/IpHI5TAAAIfkECQoAAAAsAAAAABAAEAAAAzIIunInK0rnZBTwGPNMgQwmdsNgXGJUlIWEuR5oWUIpz8pAEAMe6TwfwyYsGo/IpFKSAAAh+QQJCgAAACwAAAAAEAAQAAADMwi6IMKQORfjdOe82p4wGccc4CEuQradylesojEMBgsUc2G7sDX3lQGBMLAJibufbSlKAAAh+QQJCgAAACwAAAAAEAAQAAADMgi63P7wCRHZnFVdmgHu2nFwlWCI3WGc3TSWhUFGxTAUkGCbtgENBMJAEJsxgMLWzpEAACH5BAkKAAAALAAAAAAQABAAAAMyCLrc/jDKSatlQtScKdceCAjDII7HcQ4EMTCpyrCuUBjCYRgHVtqlAiB1YhiCnlsRkAAAOwAAAAAAAAAAAA%3D%3D)';
		if (typeof GM_xmlhttpRequest != 'undefined'
				&& typeof chromium != 'object') {
			GM_xmlhttpRequest( {
				method : 'GET',
				url : url,
				onload : function(data) {
					var form = input.form;
					if (data.status == 200) {
						write(input, data.responseText);
					}
					if (submit)
						submit_form(form);
				}
			});
		} else {
			var sec = Math.random();
			document.keygenius_login[sec] = function(pw) {
				var form = input.form;
				if (pw !== null)
					input.value = pw;
				write(input, input.value);
				if (submit)
					submit_form(form);
			}
			var s = document.createElement('script');
			url += '&callback=document.keygenius_login[' + sec + ']';
			s.setAttribute('src', url);
			document.getElementsByTagName('body')[0].appendChild(s);
		}
		return false;
	}
	function write(input, val) {
		input.disabled = false;
		input.style.backgroundImage = icon;
		val = custom + val;
		input.value = val;
		if (input.id.substr(0, 3) == 'kg_') {
			document.getElementById(input.id.substr(3)).value = val;
		}
		restore_orig(input);
	}
	function add_listener(elm, e, cb) {
		if (elm.addEventListener)
			elm.addEventListener(e, cb, false);
		else if (elm.attachEvent)
			elm.attachEvent('on' + e, cb);
		else
			eval('elm.on' + e + '=callable;');
	}
	function restore_orig(input) {
		if (input.id.substr(0, 3) != 'kg_' || input.parentNode == null)
			return;
		var o = document.getElementById(input.id.substr(3));
		input.parentNode.removeChild(input);
		if (o.style.removeProperty)
			o.style.removeProperty('display');
		else
			o.style.display = 'inline-block';
	}
	var submitting = false;
	function submit_form(form) {
		if (submitting) {
			return;
		}
		submitting = true;
		var submitters = document.getElementsByTagName('input');
		var i = submitters.length;
		while (i--) {
			if (submitters[i].form == form && submitters[i].type == 'submit') {
				if (submitters[i].name != '') {
					var field = document.createElement('input');
					field.name = submitters[i].name;
					field.type = 'hidden';
					field.value = submitters[i].value;
					form.appendChild(field);
				}
				break;
			}
		}
		restore_all();
		if (form.type != 'submit') {
			if (document.createEvent) {
				var e = document.createEvent('HTMLEvents');
				e.initEvent('submit', true, true);
				form.dispatchEvent(e);
				form.submit();
			} else if (document.createEventObject) {
				var e = document.createEventObject();
				form.fireEvent('onsubmit', e);
				form.submit();
			}
		}
	}
	function mark_input(e) {
		var s = e.style;
		s.border = '1px solid #77af03';
		s.backgroundImage = icon;
		s.backgroundPosition = 'center left';
		s.backgroundRepeat = 'no-repeat';
	}
}());

