"use strict";

function URLValidator()
{
	throw new Error("Static class.");
}


URLValidator.isValid=function(strURL, strValidationType, bAllowNonIANATLD)
{
	var mxResult;
	if (strValidationType===URLValidator.VALIDATION_TYPE_URL) 
		mxResult=URLValidator.assertValid(strURL, bAllowNonIANATLD);
	if (typeof mxResult==="boolean")
		return mxResult;
	return false;
}

/**
* Validates using RFC 3986.
* Only allows for HTTP and HTTPS URI schemes.
*
*     foo://example.com:8042/over/there?name=ferret#nose
*     \_/   \______________/\_________/ \_________/ \__/
*      |           |            |            |        |
*   scheme     authority       path        query   fragment
*      |   _____________________|__
*     / \ /                        \
*     urn:example:animal:ferret:nose
*
* @param string $strURL 
* @return bool.
* @throws DomainNameValidatorException, URLValidatorException.
*/


URLValidator.assertValid=function(strURL, bAllowNonIANATLD, bAllowHash, bAllowIPs)
{
	//It does not matter for validation, and simplifies lookups.
	strURL=strURL.toLowerCase();
	
	if(strURL.length>URLValidator.URL_MAX_LENGTH)
		return new URLValidatorException("URL cannot be longer than "+URLValidator.URL_MAX_LENGTH+" characters.", URLValidatorException.URL_LONGER_THAN_MAXIMUM_ALLOWED);
	
	var arrAllowedURLSchemes=["http", "https"];
	
	var arrSchemeAndRest=strURL.split(":");
	var strScheme=arrSchemeAndRest[0].toLowerCase();
	var strRemainingURL;
	
	if(arrSchemeAndRest.length>=2)
	{
		for(var i=1; i<arrSchemeAndRest.length; i++)
		{
			if(i==1)
				strRemainingURL=arrSchemeAndRest[i];
			else
				strRemainingURL=strRemainingURL+":"+arrSchemeAndRest[i];
		}
	}
	else
		return new URLValidatorException("Missing scheme separator ':'.", URLValidatorException.MISSING_SCHEME_SEPARATOR);
		
	if(arrAllowedURLSchemes.indexOf(strScheme)==-1)
		return new URLValidatorException("URL scheme not supported.", URLValidatorException.SCHEME_NOT_ALLOWED);
	
	if(strRemainingURL.substr(0, 2)!="//")
		return new URLValidatorException("Missing authority path-abempty (missing '//' after URI scheme and ':').", URLValidatorException.MISSING_AUTHORITY_PATH_ABEMPTY);
	strRemainingURL=strRemainingURL.substr(2);
	
	
	var arrHierarchyPathAndRest=strRemainingURL.split("/");
	var strUserPasswordAndDomainName=arrHierarchyPathAndRest[0];
	var strPathQueryAndFragment;
	
	if(arrHierarchyPathAndRest.length>=2)
	{
		for(var i=1; i<arrHierarchyPathAndRest.length; i++)
		{
			if(i==1)
				strPathQueryAndFragment=arrHierarchyPathAndRest[i];
			else 
				strPathQueryAndFragment=strPathQueryAndFragment+"/"+arrHierarchyPathAndRest[i];
		}
	}
	else
		strPathQueryAndFragment="";
		
		
	/**
	* RFC 3986. 
	* 3.2.1.  User Information.
	* Use of the format "user:password" in the userinfo field is
	* deprecated.  Applications should not render as clear text any data
	* after the first colon (":") character found within a userinfo
	* subcomponent unless the data after the colon is the empty string
	* (indicating no password).  Applications may choose to ignore or
	* reject such data when it is received as part of a reference and
	* should reject the storage of such data in unencrypted form.  The
	* passing of authentication information in clear text has proven to be
	* a security risk in almost every case where it has been used.
	*/
	
	var arrUserInfoAndHostPortParts=strUserPasswordAndDomainName.split("@");
	var arrUserAndPassword=[];
	
	var myArray;
	
	if(arrUserInfoAndHostPortParts.length>=2)
	{
		arrUserAndPassword=arrUserInfoAndHostPortParts.shift();
		arrUserAndPassword=arrUserAndPassword.split(":");
		if(arrUserAndPassword.length==2)
		{
			if(arrUserAndPassword[1]!="")
				return new URLValidatorException("RFC 3986 deprecates storing password information on URLs as it is sure to be a security issue.", URLValidatorException.USER_INFO_PASSWORD_NOT_ALLOWED);
		}
		var strUserName=arrUserAndPassword[0];
		
		/**
		* RFC 3986.
		* 3.2.1.  User Information.
		* userinfo = *( unreserved / pct-encoded / sub-delims / ":" ).
		* 
		* pct-encoded   = "%" HEXDIG HEXDIG
		* unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
		* sub-delims    = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
		*/
		
		var myRegex=/%[0-9a-f]{2,2}/gi;
		strUserName=strUserName.replace(myRegex, "");
		
		// myArray=["!", "\$", "&", "'", "(", ")", "*", "+", ",", ";", "="];
		myArray=[/!/g, /\$/g, /&/g, /'/g, /\(/g, /\)/g, /\*/g, /\+/g, /,/g, /;/g, /=/g];
		
		for(var key in myArray)
		{
			strUserName=strUserName.replace(myArray[key], "");
		}
		
		myRegex=/[0-9a-z]/gi;
		strUserName=strUserName.replace(myRegex, "");
		
		if(strUserName.length)
			return new URLValidatorException("Found invalid characters in username: "+strUserName, URLValidatorException.USERNAME_INVALID);
	}
	
	
	var arrDomainNameAndPort=arrUserInfoAndHostPortParts[0].split(":");
	var strPort;
	
	if(arrDomainNameAndPort.length>=2)
	{
		for(var i=1; i<arrDomainNameAndPort.length; i++)
		{
			if(i==1)
				strPort=arrDomainNameAndPort[i];
			else 
				strPort=strPort+":"+arrDomainNameAndPort[i];
		}
		
		if(isNaN(strPort) || strPort.substr(0, 1)=="0" || Number(strPort)<=0 || Number(strPort)>65535)
			return new URLValidatorException("Invalid TCP port: "+strPort, URLValidatorException.INVALID_PORT);
	}
	
	var strDomainName=arrDomainNameAndPort[0];
	var objDomainNameException=DomainNameValidator.assertValid(strDomainName, bAllowNonIANATLD, bAllowIPs);
	
	
	if(objDomainNameException==true)
	{
		var arrPathAndRest=strPathQueryAndFragment.split("?");
		
		var strPath=arrPathAndRest[0];
		var strQueryAndFragment;
		
		if(arrPathAndRest.length>=2)
		{
			for(var i=1; i<arrPathAndRest.length; i++)
			{
				if(i==1)
					strQueryAndFragment=arrPathAndRest[i];
				else 
					strQueryAndFragment=strQueryAndFragment+"?"+arrPathAndRest[i];
			}
		}
		else
			strQueryAndFragment="";
		
		
		/**
		* Path types:
		*
		* path-abempty: begins with "/" or is empty
		* path-absolute: begins with "/" but not "//"
		* path-noscheme: begins with a non-colon segment
		* path-rootless: begins with a segment
		* path-empty: zero characters
		*
		* path-abempty  = *( "/" segment )
		* path-absolute = "/" [ segment-nz *( "/" segment ) ]
		* path-noscheme = segment-nz-nc *( "/" segment )
		* path-rootless = segment-nz *( "/" segment )
		* path-empty    = 0<pchar>
		*
		* segment       = *pchar
		* segment-nz    = 1*pchar
		* segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" )
		* 			; non-zero-length segment without any colon ":"
		* 
		* pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
		*/
		
		
		myArray=[/:/g, /@/g, /\//g];
		for(var key in myArray)
		{
			strPath=strPath.replace(myArray[key], "");
		}
		
		strPath=URLValidator.replace_pchar(strPath);
		
		if(strPath.length)
			return new URLValidatorException("Found invalid characters in path: "+strPath, URLValidatorException.PATH_INVALID);
			
		var arrQueryAndFragment=strQueryAndFragment.split("#");
		var strQuery=arrQueryAndFragment[0];
		var strFragment;
		
		if(arrQueryAndFragment.length>=2)
		{
			for(var i=1; i<arrQueryAndFragment.length; i++)
			{
				if(i==1)
					strFragment=arrQueryAndFragment[i];
				else
					strFragment=strFragment+"#"+arrQueryAndFragment[i];
			}
		}
		else
			strFragment="";
		
		/**
		*  query       = *( pchar / "/" / "?" )
		*/
		
		strQuery=strQuery.replace(/\//g, "");
		strQuery=strQuery.replace(/\?/g, "");
		
		strQuery=URLValidator.replace_pchar(strQuery);
		
		if(strQuery.length)
			return new URLValidatorException("Found invalid characters in query: "+strQuery, URLValidatorException.QUERY_INVALID);
		
		/**
		*  fragment    = *( pchar / "/" / "?" )
		*/
		
		strFragment=strFragment.replace(/\//g, "");
		strFragment=strFragment.replace(/\?/g, "");
		
		strFragment=URLValidator.replace_pchar(strFragment);
		
		if(strFragment.length)
			return new URLValidatorException("Found invalid characters in fragment: "+strFragment, URLValidatorException.FRAGMENT_INVALID);
	}
	
	return objDomainNameException;
	return true;
}


URLValidator.replace_pchar=function(strURL)
{
	/**
	* pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
	* unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
	* sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
	* 				/ "*" / "+" / "," / ";" / "="
	* pct-encoded   = "%" HEXDIG HEXDIG
	*/
	
	var myRegex=/[0-9a-z\.~_-]/gi;
	strURL=strURL.replace(myRegex, "");

	var myArray=[/!/g, /\$/g, /&/g, /'/g, /\(/g, /\)/g, /\*/g, /\+/g, /,/g, /;/g, /=/g];
	for(var key in myArray)
		strURL=strURL.replace(myArray[key], "");
		
	myRegex=/%[0-9a-f]{2,2}/gi;
	strURL=strURL.replace(myRegex, "");
	
	
	strURL=strURL.replace(/:/g, "");
	strURL=strURL.replace(/@/g, "");
	
	return strURL;
}
 
URLValidator.VALIDATION_TYPE_URL="url";

URLValidator.URL_MAX_LENGTH=4096;

