//Decoders for each of the formats
function DataInput(formatname){
	//abstract class
	this.formatName=formatname;
}

DataInput.prototype.ignoreList=new Object();
DataInput.prototype.addIgnore=function(str,mode){this.ignoreList[str]=mode;}
DataInput.prototype.ignored=0; //how many lines have we ignored?
DataInput.prototype.currLine=""; //store the line we are working on
DataInput.prototype.currLineNum=0; //store the line number we are working on
DataInput.prototype.dupes=0;

DataInput.prototype.read=function(){
	//abstract method declaration
	//returns array of Rule objects

	var rawString=ui.getElement("sfrom").value; //do something here
	var chunks=this.split(rawString);
	ui.updateStatus('Reading '+this.formatName+' format. Found '+chunks.length+' entries');
	if(settings.listCleaning!=0){
		this.dupes=chunks.length;
		chunks=this.getUniqueArray(chunks);
		this.dupes-=chunks.length;
	}
	var rd=new Array();
	for(var j=0;j<chunks.length;j++){
		this.currLineNum=(j+1);
		this.currLine=chunks[j];
		if(this.currLineNum%10==0) ui.updateStatus("Reading "+this.formatName+" format ("+this.currLineNum+"/"+chunks.length+")");
		if(this.shouldIgnore(this.currLine)){
			this.ignored++;
			ui.debug("Ignore line "+j+": "+this.currLine);
		}else{
			//call the subclass to get the rule translated
			ui.debug("Processing line "+j+": "+this.currLine);
			var rr=this.getRule(this.currLine);
			//has an error occurred with the rule?
			if (rr){
				if(rr.error){
					//an error (which is fatal to the rule only) has occurred, so register a continuable exception
					this.ignored++;
					cnv.handleException('Source List line '+this.currLineNum+' : '+rr.error+' : '+this.currLine);
				}else{
					rd.push(rr);
				}
			}
		}
	}
	return rd;
}

DataInput.prototype.split=function(rawString){
	//default case which handles one line per rule (many formats)
	//this does not need to be overrridden, if it handles the default case
	var chunks=rawString.split("\r\n"); //crlf for ie/opera
	if(chunks.length==1) chunks=rawString.split("\n"); //linefeed only for ns/moz
	return chunks;
}

DataInput.prototype.shouldIgnore=function(str){
	//from the defined ignores (in the subclass constructor)
	//we work out if we should ignore the line
	if(str=="") return true; //always ignore blanks
	if (this.ignoreList){
		for(var k in this.ignoreList){
			switch(this.ignoreList[k]){
				case 1:{
					if(str==k) return true;
					break;
				} //exact
				case 2:{
					if(str.startsWith(k)) return true;
					break;
				} //startswith
				case 3:{
					if(str.endsWith(k)) return true;
					break;
				} //endswith
				case 4:{
					if(str.contains(k)) return true;
					break;
				} //contains
				default:{
					throw new CustomError("Invalid Value",1001,'DataInput::shouldIgnore - Invalid value for ignore condition ["'+k+'"]=='+this.ignoreList[k]);
					//throw('DataInput.shouldIgnore(): Invalid value for ignore condition ["'+k+'"]=='+this.ignoreList[k]);
					break;
				}
			}
		}
	}
	return false;
}

DataInput.prototype.getRule=function(rawRule){
	//return a Rule object
	//this must be overridden
	throw new CustomError("Method must be overridden",1002,"DataInput::getRule - This method must be overridden");
	//throw('DataInput.getRule(): This method must be overridden.');
	return false;
}

DataInput.prototype.getUniqueArray=function(arrIn){
	//simple function to get unique entries
	//removes EXACT duplicates only
	var unduped = new Object;
	var uniques = new Array;
	for (var l = 0; l < arrIn.length; l++) if(arrIn[l]!="") unduped[arrIn[l]] = arrIn[l];
	for (var k in unduped) uniques.push(k);
	return uniques;
}

DataInput.prototype.formatName="Unassigned"; //nice name for the format - default = Unassigned

DataInput.prototype.getXMLParm=function(rawRule,name){
	//return a given xml param
	var offset=rawRule.indexOf(name);
	if (offset==-1) return '';
	offset+=(name.length+2);
	var tmp=rawRule.substring(offset,rawRule.length-1);
	return tmp.substring(0,tmp.indexOf('"'));
}

DataInput.prototype.replace=function(string,text,by){
	// Replaces text with by in string
    var strLength = string.length, txtLength = text.length;
    if ((strLength == 0) || (txtLength == 0)) return string;

    var i = string.indexOf(text);
    if ((!i) && (text != string.substring(0,txtLength))) return string;
    if (i == -1) return string;

    var newstr = string.substring(0,i) + by;

    if (i+txtLength < strLength)
        newstr += this.replace(string.substring(i+txtLength,strLength),text,by);

    return newstr;
}

//////////////////////////////////////

function DataInput_arin() {
	this.DataInput = DataInput;
	this.DataInput("ARIN Raw");
}

DataInput_arin.prototype = new DataInput;

DataInput_arin.prototype.getRule=function(rawRule){
//split on single space - can have multiple spaces, so check for blank entries in the array
	var arr=rawRule.split(" ");
	var addr1=arr[0]; //always the first entry
	var addr2='';
	//accomodate multiple blank spaces
	for (var k=1;(addr2=='')&(k<arr.length);k++){
		ui.debug('DataOutput_arin.getRule: get range end - index '+k+': '+arr[k]);
		if (arr[k]!='') var addr2=arr[k];
	}
	ui.debug('DataOutput_arin.getRule: range end : '+addr2);
	var comment='';
	for(var j=k;j<arr.length;j++) comment+=((arr[j]=='')?'':arr[j]+(j<arr.length-1?' ':''));
	return new Rule(addr1,addr2,null,"deny",comment);
}

//////////////////////////////////////

function DataInput_bbl() {
	this.DataInput = DataInput;
	this.DataInput("Bob's Block List (BBL)");
	this.addIgnore(" ",2);
}

DataInput_bbl.prototype = new DataInput;

DataInput_bbl.prototype.getRule=function(rawRule){
	var offset=rawRule.indexOf(" ");
	var strange=rawRule.substring(0,offset);
	while(rawRule.substr(offset,1)==" ") offset++;
	var comment=rawRule.substring(offset,rawRule.length);
	if (strange.indexOf("/")==-1){
		//implicit /24 where address ends in .0
		if (strange.endsWith(".0")){
			return new Rule(strange,null,"255.255.255.0","deny",comment);
		}else{
			return new Rule(strange,strange,null,"deny",comment);
		}
	}else{
		var addrs=rawRule.split("/");
		//convert the netmaskbits to decimal netmask, and then to formatted netmask.
		//assumes netrange.js is loaded
		var netmask=decToIP(maxIP-Math.pow(2,(32-parseInt(addrs[1]))));
		return new Rule(addrs[0],null,netmask,"deny",comment);
	}
}

//////////////////////////////////////

function DataInput_bearshare() {
	this.DataInput = DataInput;
	this.DataInput("Bearshare");
}

DataInput_bearshare.prototype = new DataInput;

DataInput_bearshare.prototype.getRule=function(rawRule){
	//24.84.58.65
	//216.122.0.0/255.255.0.0
	var comment="From BearShare hostiles.txt"
	if (rawRule.indexOf("/")==-1){
		return new Rule(rawRule,rawRule,null,"deny",comment);
	}else{
		var addrs=rawRule.split("/");
		return new Rule(addrs[0],null,addrs[1],"deny",comment);
	}
}

//////////////////////////////////////

function DataInput_blackice() {
	this.DataInput = DataInput;
	this.DataInput("BlackICE");
	this.addIgnore(";",2);
}

DataInput_blackice.prototype = new DataInput;

DataInput_blackice.prototype.getRule=function(rawRule){
	//;action, IP/port, name, whenSet, whenExpire, precedence, whoSet
	//REJECT, 12.207.16.71,12.207.16.71, 2003-05-09 16:27:53, PERPETUAL, 4000, BIgui
	//REJECT, 80.62.54.62,0x503e363e.arcnxx8.adsl-dhcp.tele.dk, 2003-05-09 16:27:53, PERPETUAL, 4000, BIgui

	var arr=rawRule.split(",");
	var iptmp=arr[1];
	if (iptmp.indexOf(":")!=-1) iptmp=iptmp.split(":")[0];
	if (iptmp.indexOf(" - ")!=-1) var addrs=iptmp.split(" - ");
		else var addrs=new Array(iptmp,iptmp);
	var comment="From BlackICE Defender"+((arr[1]!=arr[2])?":"+arr[2]:"");
	var actn=((arr[0]=="REJECT")?"deny":"accept");
	return new Rule(addrs[0],addrs[1],null,actn,comment);
}

//////////////////////////////////////

function DataInput_blockpost() {
	this.DataInput = DataInput;
	this.DataInput("Blockpost v1");
	this.addIgnore("#",2);
}

DataInput_blockpost.prototype = new DataInput;

DataInput_blockpost.prototype.getRule=function(rawRule){
	//63.236.32.50/255.255.255.255,Xupiter.com#P2P Dangerous IPs
	//65.160.0.0/255.255.128.0,OverPeer#P2P Dangerous IPs
	var arr=rawRule.split(",");
	var addrs=arr[0].split("/"); //ip then mask
	var comment=arr[1].substring(0,arr[1].indexOf("#")); //strip off the category
	return new Rule(addrs[0],null,addrs[1],"deny",comment);
}

//////////////////////////////////////

function DataInput_blockp2() {
	this.DataInput = DataInput;
	this.DataInput("Blockpost v2");
	this.addIgnore("#",2);
}

DataInput_blockp2.prototype = new DataInput;

DataInput_blockp2.prototype.getRule=function(rawRule){
	//#BLOCKPOST V2
	//1,0.0.0.0/255.0.0.0#LOW_ID
	//2,1.0.0.0-1.255.255.255#IANA
	//3,birdi.sharpnet.net,65.116.88.98#Accessing port 80
	var arr=rawRule.split(",");
	if (arr[0]=="3"){
		//single ip specified most likely by dns
		var arr2=arr[2].split("#");
		var comment=arr2[1];
		return new Rule(arr2[0],arr2[0],null,"deny",comment);
	}else{
		var arr2=arr[1].split("#");
		arr=new Array(arr[0],arr2[0],arr2[1]);
		var comment=arr[2];
		if(arr[0]=="1"){
			//it uses subnets (1)
			var addrs=arr[1].split("/");
			return new Rule(addrs[0],null,addrs[1],"deny",comment);
		}else{
			//uses ranges (2)
			var addrs=arr[1].split("-");
			return new Rule(addrs[0],addrs[1],null,"deny",comment);
		}

	}

}

//////////////////////////////////////

function DataInput_ciscoios() {
	this.DataInput = DataInput;
	this.DataInput("Cisco IOS");
	this.addIgnore("#",2);
}

DataInput_ciscoios.prototype = new DataInput;

DataInput_ciscoios.prototype.getRule=function(rawRule){
	//permit ip host 213.150.42.178 any
	//deny ip 66.111.55.152 0.0.0.7 any
	//inverse mask works as a range size, so use it as such
	var arr=rawRule.split(" ");
	if(arr[2]=="host"){
		return new Rule(arr[3],arr[3],null,arr[0],"From Cisco IOS");
	}else{
		var endAddr=decToIP(ipToDec(arr[2])+ipToDec(arr[3]));
		return new Rule(arr[2],endAddr,null,arr[0],"From Cisco IOS");
	}
}

//////////////////////////////////////

function DataInput_dshield() {
	this.DataInput = DataInput;
	this.DataInput("DShield");
	this.addIgnore("#",2);
	this.addIgnore("Start\t",2);

}

DataInput_dshield.prototype = new DataInput;

DataInput_dshield.prototype.getRule=function(rawRule){
	var arr=rawRule.split("\t");
	var comment="(DBL) "+((arr[4]=='')?'Unknown':arr[4]);
	return new Rule(arr[0],arr[1],null,"deny",comment);
}

//////////////////////////////////////

function DataInput_donk() {
	this.DataInput = DataInput;
	this.DataInput("eDonkey");
	this.addIgnore("#",2);
}

DataInput_donk.prototype = new DataInput;

DataInput_donk.prototype.getRule=function(rawRule){
	var arr=rawRule.split(" , ");
	var addrs=arr[0].split(" - ");
	var actn=((parseInt(arr[1])<127)?"deny":"accept");
	var comment=arr[2];
	return new Rule(addrs[0],addrs[1],null,actn,comment);
}

//////////////////////////////////////

//eth ip filter insert forward drop -sa ADDRESS -sm 255.255.255.0
function DataInput_flowpoint() {
	this.DataInput = DataInput;
	this.DataInput("Flowpoint");
}

DataInput_flowpoint.prototype = new DataInput;

DataInput_flowpoint.prototype.getRule=function(rawRule){
	var arr=rawRule.split(" ");
	//var addrs=arr[0].split(" - ");
	if(arr.length<10){
		return new Rule(arr[7],arr[7],null,"deny","From Flowpoint Router");
	}else{
		return new Rule(arr[7],null,arr[9],"deny","From Flowpoint Router");
	}

}

//////////////////////////////////////

function DataInput_gnu() {
	this.DataInput = DataInput;
	this.DataInput("Gnucleus");
}

DataInput_gnu.prototype = new DataInput;

DataInput_gnu.prototype.getRule=function(rawRule){
	var arr=rawRule.split(":");
	var addrs=arr[0].split("-");
	var comment=arr[1];
	return new Rule(addrs[0],addrs[1],null,"deny",comment);
}

//////////////////////////////////////

function DataInput_morph() {
	this.DataInput = DataInput;
	this.DataInput("Morpheus");
}

DataInput_morph.prototype = new DataInput;

DataInput_morph.prototype.getRule=function(rawRule){
	var arr=rawRule.split(":");
	var addrs=arr[0].split("-");
	var comment=arr[1];
	return new Rule(addrs[0],addrs[1],null,"deny",comment);
}

//////////////////////////////////////

function DataInput_pg() {
	this.DataInput = DataInput;
	this.DataInput("PeerGuardian");
}

DataInput_pg.prototype = new DataInput;

DataInput_pg.prototype.getRule=function(rawRule){
	var arr=rawRule.split(":");
	var addrs=arr[arr.length-1].split("-");
	var addr1=addrs[0];
	var addr2=((addrs.length==1)?addr1:addrs[1]);
	var comment="";
	for(j=0;j<(arr.length-1);j++) comment+=arr[j]+(j<(arr.length-2)?":":"");
	return new Rule(addr1,addr2,null,"deny",comment);
}

//////////////////////////////////////

function DataInput_plainrange() {
	this.DataInput = DataInput;
	this.DataInput("Plain IP Ranges");
}

DataInput_plainrange.prototype = new DataInput;

DataInput_plainrange.prototype.getRule=function(rawRule){
	//192.168.2.1-192.168.2.1
	//192.168.1.0-192.168.1.255
	var addrs=rawRule.split("-");
	return new Rule(addrs[0],addrs[1],null,"deny","From Plain Ranges");
}

//////////////////////////////////////

function DataInput_raza() {
	this.DataInput = DataInput;
	this.DataInput("Shareaza xml");
	this.addIgnore('<?xml',4);
	this.addIgnore('<security xmlns',4);
	this.addIgnore('</security',4);
	this.addIgnore('content=',4);
}

DataInput_raza.prototype = new DataInput;

DataInput_raza.prototype.getRule=function(rawRule){

    var addr=this.getXMLParm(rawRule,"address");
    var comment=this.getXMLParm(rawRule,"comment");
    var actn=((rawRule.indexOf('action="deny"')==-1)?"accept":"deny");

    var offset=rawRule.indexOf('mask=');
    if (offset==-1){
         return new Rule(addr,addr,null,actn,comment);
    }else{
         tmp=rawRule.substr(offset+6,16);
         tmp=tmp.substring(0,tmp.indexOf('"'));;
         return new Rule(addr,null,tmp,actn,comment);
    }
}


//////////////////////////////////////

function DataInput_shorewall() {
	this.DataInput = DataInput;
	this.DataInput("Shorewall");
}

DataInput_shorewall.prototype = new DataInput;

DataInput_shorewall.prototype.getRule=function(rawRule){
			var comment="From Shorewall";
			var actn="deny";

			if (rawRule.indexOf("/")==-1){
				return new Rule(rawRule,rawRule,null,actn,comment);
			}else{
				var addrs=rawRule.split("/");
				//convert the netmaskbits to decimal netmask, and then to formatted netmask.
				//assumes netrange.js is loaded
				var netmask=decToIP(maxIP-Math.pow(2,(32-parseInt(addrs[1]))));
				return new Rule(addrs[0],null,netmask,"deny","From Shorewall");
			}

}

//////////////////////////////////////

function DataInput_sygate() {
	this.DataInput = DataInput;
	this.DataInput("Sygate Advanced Rules");
}

DataInput_sygate.prototype = new DataInput;

DataInput_sygate.prototype.getRule=function(rawRule){
			//66.111.55.150-66.111.55.159, 213.150.42.178-213.150.42.178,
			var addrs=rawRule.split("-");
			return new Rule(addrs[0],addrs[1],null,"deny","From Sygate Advanced Rules");
}

DataInput_sygate.prototype.split=function(rawString){
	return rawString.split(",");
}

//////////////////////////////////////

function DataInput_trusty() {
	this.DataInput = DataInput;
	this.DataInput("TrustyFiles");
}

DataInput_trusty.prototype = new DataInput;

DataInput_trusty.prototype.getRule=function(rawRule){
	var arr=rawRule.split(":");
	var addrs=arr[0].split("-");
	var comment=arr[1];
	return new Rule(addrs[0],addrs[1],null,"deny",comment);
}

//////////////////////////////////////

function DataInput_zonealarm() {
	this.DataInput = DataInput;
	this.DataInput("ZoneAlarm v4 xml");
	this.addIgnore('zones',4);
	this.addIgnore('/trusted',4);
	this.addIgnore('/restricted',4);
	this.addIgnore('host name',4);
}

DataInput_zonealarm.prototype = new DataInput;
DataInput_zonealarm.prototype.zonetype="unknown";

DataInput_zonealarm.prototype.getRule=function(rawRule){
	/*
	<zones>
	<trusted clearOldEntries="true" defaultNetworkStatus="ask" defaultAdapterMode="off">
	<ipaddr address="213.150.42.178" status="true" description="BAD HOST: Scan Error"/>
	</trusted>
	<restricted clearOldEntries="true" defaultNetworkStatus="ask" defaultAdapterMode="off">
	<iprange address="66.111.55.150" toAddress="66.111.55.159" status="true" description="BAD HOST: Fake Files Source"/>
	<ipsubnet address="10.0.0.5" mask="255.255.255.0" status="true" description="Sample IP Subnet in Blocked Zone"/>
	<host name="www.dell.com" status="true" description="Sample host in blocked zone."/>
	</restricted>
	</zones>
	*/

	if(rawRule.contains("trusted")){
		this.zonetype="permit";
		return false;
	}else if(rawRule.contains("restricted")){
		this.zonetype="deny";
		return false;
	}else if(rawRule.indexOf("address=")!=-1){
		var startAddr=this.getXMLParm(rawRule,"address");
		var desc=this.getXMLParm(rawRule,"description");
		if(rawRule.contains("ipaddr")){
			//ipaddr address="213.150.42.178" status="true" description="BAD HOST: Scan Error"/
			return new Rule(startAddr,startAddr,null,this.zonetype,desc);
		}else if(rawRule.contains("iprange")){
			//iprange address="66.111.55.150" toAddress="66.111.55.159" status="true" description="BAD HOST: Fake Files Source"/
			var endAddr=this.getXMLParm(rawRule,"toAddress");
			return new Rule(startAddr,endAddr,null,this.zonetype,desc);
		}else if(rawRule.contains("ipsubnet")){
			//ipsubnet address="10.0.0.5" mask="255.255.255.0" status="true" description="Sample IP Subnet in Blocked Zone"/
			var mask=this.getXMLParm(rawRule,"mask");
			return new Rule(startAddr,null,mask,this.zonetype,desc);
		}else{
			//unhandled ignore
			throw new CustomError("Unrecognised Syntax", 1003,'DataInput_zonealarm::getRule("'+rawRule+'"): Unrecognised Syntax')
			//throw('DataInput_zonealarm.getRule("'+rawRule+'"): Unknown Syntax');
		}
	}else{
		throw new CustomError("Unrecognised Syntax", 1003,'DataInput_zonealarm::getRule("'+rawRule+'"): Unrecognised Syntax')
		//throw('DataInput_zonealarm.getRule("'+rawRule+'"): Unknown Syntax');
	}
}

DataInput_zonealarm.prototype.split=function(rawString){
	//replace \r and \n with ""
	rawString=rawString.replace(new RegExp ('\r', 'gi'), "");
	rawString=rawString.replace(new RegExp ('\n', 'gi'), "");
	return rawString.split("><");
}
