/*
 * This Javascript Template is inherited (and customized) from OWASP CSRF Guard implementation for Dynamic Token Injection
 * Reference : http://grepcode.com/file/repo1.maven.org/maven2/org.owasp/csrfguard/3.0.0/org/owasp/csrfguard/servlet/JavaScriptServlet.java?av=f
 */
(function() {
	/**
	 * Code to ensure our event always gets triggered when the DOM is updated.
	 * @param obj
	 * @param type
	 * @param fn
	 * @source http://www.dustindiaz.com/rock-solid-addevent/
	 */
	function addEvent( obj, type, fn ) {
	    if (obj.addEventListener) {
	        obj.addEventListener( type, fn, false );
	        EventCache.add(obj, type, fn);
	    }
	    else if (obj.attachEvent) {
	        obj["e"+type+fn] = fn;
	        obj[type+fn] = function() { obj["e"+type+fn]( window.event ); }
	        obj.attachEvent( "on"+type, obj[type+fn] );
	        EventCache.add(obj, type, fn);
	    }
	    else {
	        obj["on"+type] = obj["e"+type+fn];
	    }
	}
	
	var EventCache = function(){
	    var listEvents = [];
	    return {
	        listEvents : listEvents,
	        add : function(node, sEventName, fHandler){
	            listEvents.push(arguments);
	        },
	        flush : function(){
	            var i, item;
	            for(i = listEvents.length - 1; i >= 0; i = i - 1){
	                item = listEvents[i];
	                if(item[0].removeEventListener){
	                    item[0].removeEventListener(item[1], item[2], item[3]);
	                };
	                if(item[1].substring(0, 2) != "on"){
	                    item[1] = "on" + item[1];
	                };
	                if(item[0].detachEvent){
	                    item[0].detachEvent(item[1], item[2]);
	                };
	                item[0][item[1]] = null;
	            };
	        }
	    };
	}();
	
	/** string utility functions **/
	String.prototype.startsWith = function(prefix) {
		return this.indexOf(prefix) === 0;
	};

	String.prototype.endsWith = function(suffix) {
		return this.match(suffix+"$") == suffix;
	};

	/** hook using standards based prototype **/
	function hijackStandard() {
		XMLHttpRequest.prototype._open = XMLHttpRequest.prototype.open;
		XMLHttpRequest.prototype.open = function(method, url, async, user, pass) {
			this.url = url;
			
			this._open.apply(this, arguments);
		};
		
		XMLHttpRequest.prototype._send = XMLHttpRequest.prototype.send;
		XMLHttpRequest.prototype.send = function(data) {
			if(this.onsend != null) {
				this.onsend.apply(this, arguments);
			}
			
			this._send.apply(this, arguments);
		};
	}

	/** ie does not properly support prototype - wrap completely **/
	function hijackExplorer() {
		var _XMLHttpRequest = window.XMLHttpRequest;
		
		function alloc_XMLHttpRequest() {
			this.base = _XMLHttpRequest ? new _XMLHttpRequest : new window.ActiveXObject("Microsoft.XMLHTTP");
		}
		
		function init_XMLHttpRequest() {
			return new alloc_XMLHttpRequest;
		}
		
		init_XMLHttpRequest.prototype = alloc_XMLHttpRequest.prototype;
		
		/** constants **/
		init_XMLHttpRequest.UNSENT = 0;
		init_XMLHttpRequest.OPENED = 1;
		init_XMLHttpRequest.HEADERS_RECEIVED = 2;
		init_XMLHttpRequest.LOADING = 3;
		init_XMLHttpRequest.DONE = 4;
		
		/** properties **/
		init_XMLHttpRequest.prototype.status = 0;
		init_XMLHttpRequest.prototype.statusText = "";
		init_XMLHttpRequest.prototype.readyState = init_XMLHttpRequest.UNSENT;
		init_XMLHttpRequest.prototype.responseText = "";
		init_XMLHttpRequest.prototype.responseXML = null;
		init_XMLHttpRequest.prototype.onsend = null;
		
		init_XMLHttpRequest.url = null;
		init_XMLHttpRequest.onreadystatechange = null;

		/** methods **/
		init_XMLHttpRequest.prototype.open = function(method, url, async, user, pass) {
			var self = this;
			this.url = url;
			
			this.base.onreadystatechange = function() {
				try { self.status = self.base.status; } catch (e) { }
				try { self.statusText = self.base.statusText; } catch (e) { }
				try { self.readyState = self.base.readyState; } catch (e) { }
				try { self.responseText = self.base.responseText; } catch(e) { }
				try { self.responseXML = self.base.responseXML; } catch(e) { }
				
				if(self.onreadystatechange != null) {
					self.onreadystatechange.apply(this, arguments);
				}
			}
			
			this.base.open(method, url, async, user, pass);
		};
		
		init_XMLHttpRequest.prototype.send = function(data) {
			if(this.onsend != null) {
				this.onsend.apply(this, arguments);
			}
			
			this.base.send(data);
		};
		
		init_XMLHttpRequest.prototype.abort = function() {
			this.base.abort();
		};
		
		init_XMLHttpRequest.prototype.getAllResponseHeaders = function() {
			return this.base.getAllResponseHeaders();
		};
		
		init_XMLHttpRequest.prototype.getResponseHeader = function(name) {
			return this.base.getResponseHeader(name);
		};
		
		init_XMLHttpRequest.prototype.setRequestHeader = function(name, value) {
			return this.base.setRequestHeader(name, value);
		};
		
		/** hook **/
		window.XMLHttpRequest = init_XMLHttpRequest;
	}

	/** check if valid domain based on domainStrict **/
	function isValidDomain(currentDomain) {
		var result = false;
		
		/** check exact or subdomain match **/
		if(true == false) {
			result = true;
		} else if(currentDomain == "jobvite.com") {
			result = true;
		} else if(currentDomain.endsWith('.' + "jobvite.com")) {
			result = true;
		} 		
		return result;
	}

	/** determine if uri/url points to valid domain **/
	function isValidUrl(src) {
		var result = false;
		
		/** parse out domain to make sure it points to our own **/
		if(src.substring(0, 7) == "http://" || src.substring(0, 8) == "https://") {
			var token = "://";
			var index = src.indexOf(token);
			var part = src.substring(index + token.length);
			var domain = "";
			
			/** parse up to end, first slash, or anchor **/
			for(var i=0; i<part.length; i++) {
				var character = part.charAt(i);
				
				if(character == '/' || character == ':' || character == '#') {
					break;
				} else {
					domain += character;
				}
			}
			result = isValidDomain(domain);
			/** explicitly skip anchors **/
		} else if(src.charAt(0) == '#') {
			result = false;
			/** ensure it is a local resource without a protocol **/
		} else if(!src.startsWith("//") && (src.charAt(0) == '/' || src.indexOf(':') == -1)) {
			result = true;
		}
		
		return result;
	}

	/** parse uri from url **/
	function parseUri(url) {
		var uri = "";
		var token = "://";
		var index = url.indexOf(token);
		var part = "";
		
		/**
		 * ensure to skip protocol and prepend context path for non-qualified
		 * resources (ex: "protect.html" vs
		 * "/Owasp.CsrfGuard.Test/protect.html").
		 */
		if(index > 0) {
			part = url.substring(index + token.length);
		} else if(url.charAt(0) != '/') {
			part = "/admin/" + url;
		} else {
			part = url;
		}
		
		/** parse up to end or query string **/
		var uriContext = (index == -1);
		
		for(var i=0; i<part.length; i++) {
			var character = part.charAt(i);
			
			if(character == '/') {
				uriContext = true;
			} else if(uriContext == true && (character == '?' || character == '#')) {
				uriContext = false;
				break;
			}
			
			if(uriContext == true) {
				uri += character;
			}
		}
		
		return uri;
	}

	/** inject tokens as hidden fields into forms **/	
	function injectTokenForm(form, tokenName, tokenValue, pageTokens) {
		if(!form.elements.namedItem(tokenName)) {
			var hidden = document.createElement("input");
			
			hidden.setAttribute("type", "hidden");
			hidden.setAttribute("name", tokenName);
			hidden.setAttribute("value", tokenValue);
			
			form.appendChild(hidden);
		}
	}

	/** inject csrf prevention tokens throughout dom **/
	function injectTokens(tokenName, tokenValue) {
		/** obtain reference to page tokens if enabled **/
		var pageTokens = {};
		
		/** iterate over all elements and injection token **/
		var all = document.getElementsByTagName("form");
		var len = all.length;

		for(var i=0; i<len; i++) {
			var element = all[i];
			if(true == true) {
				injectTokenForm(element, tokenName, tokenValue, pageTokens);
			}
		}
	}
	
	/**
	 * Only inject the tokens if the JavaScript was referenced from HTML that
	 * was served by us. Otherwise, the code was referenced from malicious HTML
	 * which may be trying to steal tokens using JavaScript hijacking techniques.
	 */
	if(isValidDomain(document.domain)) {
		/** optionally include Ajax support **/
		if(true == true) {
			if((navigator.appName == "Microsoft Internet Explorer") && !(typeof window.XMLHttpRequest === 'function')) {
				// Do this for only IE9 or less
				hijackExplorer();
			} else {
				hijackStandard();
			}
			
			XMLHttpRequest.prototype.onsend = function(data) {
				if(isValidUrl(this.url)) {
					this.setRequestHeader("X-CSRF-TOKEN", "9febe1b9-d6f2-46fb-a096-122961a02673");
				}
			};
		}
		
		/** update nodes in DOM after load **/
		if(typeof $ != "undefined") {
			$(document).ready(function() {
				injectTokens("_csrf", "9febe1b9-d6f2-46fb-a096-122961a02673");
				if($(document).on) {
					$(document).on('submit','form',function(e){
						injectTokenForm(this, "_csrf", "9febe1b9-d6f2-46fb-a096-122961a02673", null);
						return true;
					}); 
				}
			});
		} else {
			addEvent(window,'unload',EventCache.flush);
			addEvent(window,'load', function() {
				injectTokens("_csrf", "9febe1b9-d6f2-46fb-a096-122961a02673");
			});		
		}
	} 
})();
