//
//
// C++ Implementation: $MODULE$
//
// Description:
//
//
// Author: Christian Hubinger <chubinger@irrsinnig.org>, (C) 2003
//
// Copyright: See COPYING file that comes with this distribution
//
//
/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/


#include "kmfgenericdoc.h"


// TQt includes
#include <tqfile.h>
#include <tqdom.h>
#include <tqstring.h>
#include <tqstringlist.h>

// KDE includes
#include <tdeapplication.h>
#include <kdebug.h>
#include <kstandarddirs.h>
#include <tdelocale.h>
#include <tdeio/netaccess.h>
#include <tdeio/job.h>
#include <ktrader.h>
#include <klibloader.h>

// Project includes
#include "../version.h"
#include "kmfcompilerinterface.h"
#include "kmfplugin.h"
#include "kmfpluginfactory.h"
#include "kmferror.h"
#include "kmfprotocol.h"
#include "kmfnetzone.h"
#include "kmfnetwork.h"
#include "kmfconfig.h"
#include "ipaddress.h"
#include "kmftarget.h"
#include "xmlnames.h"

namespace KMF {

KMFGenericDoc::KMFGenericDoc( NetfilterObject* parent, const char* name, KMFTarget* target ) : KMFDoc( parent, name ), KMFRulesetDoc( target ) {
	kdDebug() << "KMFGenericDoc::KMFGenericDoc( TQObject *parent, const char *name ) : KMFDoc( parent, name )" << endl;
	m_zones.setAutoDelete( false );
	m_zone_incoming = 0;
	m_zone_outgoing = 0;
	m_zone_trusted= 0 ;
	m_zone_malicious= 0 ;
	m_zone_badClients= 0 ;
	m_zone_badServers= 0 ;
	m_allowIncomingConnections  = false;
	m_restrictOutgoingConnections = false;
	m_allowPingReply = true;
	m_limitPingReply = true;
	m_useNat = false;
	m_useMasquerade = false;
	m_logDropped  = true;
	m_limitLog  = true;
	m_logPrefix = "KMF: ";
	m_natAddress = new IPAddress(0,0,0,0);
	m_outgoingInterface = XML::BoolOff_Value;
	initDoc();
}

int KMFGenericDoc::type() {
// 	kdDebug() << "IPTRule::type()" << endl;
	return NetfilterObject::GENERIC_RULESET;
}


KMFGenericDoc::~KMFGenericDoc() {}

void KMFGenericDoc::clear() {
	kdDebug() << "void KMFGenericDoc::clear()" << endl;
	m_zone_incoming->clear();
	m_zone_outgoing->clear();
	m_zone_trusted->clear();
	m_zone_malicious->clear();
	m_zone_badClients->clear();
	m_zone_badServers->clear();
	m_allowIncomingConnections  = false;
	m_restrictOutgoingConnections = false;
	m_allowPingReply = true;
	m_limitPingReply = true;
	m_useNat = false;
	m_useMasquerade = false;
	m_logDropped  = true;
	m_limitLog  = true;
	m_logPrefix = "KMF: ";
	m_natAddress->setAddress( 0, 0, 0, 0 );
	m_outgoingInterface = XML::BoolOff_Value;
	resetUrl();
	setDescription( i18n("No description available") );
}

void KMFGenericDoc::initDoc() {
	kdDebug() << "void KMFGenericDoc::initDoc()" << endl;
	m_err = new KMFError;
	m_url.setFileName( i18n( "Untitled" ) );

	m_zone_incoming = new KMFNetZone( this,  "incoming_world",  "incoming_world" );
	m_zone_outgoing = new KMFNetZone( this,  "outgoing_world",  "outgoing_world" );
	m_zone_trusted= new KMFNetZone( this,  "trusted_hosts",  "trusted_hosts" );
	m_zone_malicious= new KMFNetZone( this,  "malicious_hosts",  "malicious_hosts" );
	m_zone_badClients= new KMFNetZone( this,  "badClients_hosts",  "badClients_hosts" );
	m_zone_badServers= new KMFNetZone( this,  "badServers_hosts",  "badServers_hosts" );


	m_zone_incoming->setGuiName( i18n( "Incoming Connections" ) );
	m_zone_outgoing->setGuiName( i18n( "Outgoing Connections" )  );
	m_zone_trusted->setGuiName( i18n( "Trusted Hosts" )  );
	m_zone_malicious->setGuiName( i18n( "Malicious Hosts" )  );
	m_zone_badClients->setGuiName( i18n( "Forbidden Clients" )  );
	m_zone_badServers->setGuiName( i18n( "Forbidden Servers" )  );

	m_zone_incoming->setZone( IPAddress( 0, 0, 0, 0 ), 0 );
	m_zone_outgoing->setZone( IPAddress( 0, 0, 0, 0 ), 0 );
	m_zone_trusted->setZone( IPAddress( 0, 0, 0, 0 ), 0 );
	m_zone_malicious->setZone( IPAddress( 0, 0, 0, 0 ), 0 );
	m_zone_badClients->setZone( IPAddress( 0, 0, 0, 0 ),0 );
	m_zone_badServers->setZone( IPAddress( 0, 0, 0, 0 ), 0 );

	m_zone_incoming->setDescription( i18n( "This is the global zone that contains\n"
																"all valid IP addresses." ) );
	m_zone_outgoing->setDescription( i18n( "This is the global zone that contains\n"
																"all valid IP addresses." ) );
	m_zone_trusted->setDescription( i18n(   "Traffic coming from and going to hosts\n"
																"will  be accepted always.\n"
																"Only add really trusted Hosts to this Zone" ) );
	m_zone_malicious->setDescription( i18n("Traffic coming from and going to hosts\n"
																"will  be dropped always." ) );
	m_zone_badClients->setDescription( i18n( 	"Hosts in this zone will not be able\n"
																	"to use services your computer provides." ) );
	m_zone_badServers->setDescription( i18n( "You will not be able to use the services\n"
																	"of the hosts in that list." ) );
}

const TQString& KMFGenericDoc::getFileDialogFilter() {
	return *( new TQString("*.kmfgrs|KMyFirewall Generic Ruleset(*.kmfgrs)") );
}


void KMFGenericDoc::setRestrictOutgoingConnections( bool onoff ) {
	m_restrictOutgoingConnections = onoff;
}

void KMFGenericDoc::setAllowIncomingConnections( bool onoff ) {
	m_allowIncomingConnections = onoff;
}

void KMFGenericDoc::setLogDropped( bool onoff ) {
	m_logDropped = onoff;
}

void KMFGenericDoc::setLimitLog( bool onoff ) {
	m_limitLog = onoff;
}

void KMFGenericDoc::setLogPrefix( const TQString& pre ) {
	if ( ! pre.isNull() )
		m_logPrefix = pre;
	else
		m_logPrefix = "";
}



bool KMFGenericDoc::isEmpty() {
// 	kdDebug() << "bool KMFGenericDoc::isEmpty()" << endl;
	return false;
}

void KMFGenericDoc::setAllowPingReply( bool onoff ) {
	m_allowPingReply = onoff;
}
void KMFGenericDoc::setLimitPingReply( bool onoff ){
	m_limitPingReply = onoff;
}

void KMFGenericDoc::setUseNat( bool onoff ) {
	m_useNat = onoff;
}

void KMFGenericDoc::setUseMasquerade( bool onoff ) {
	m_useMasquerade = onoff;
}

void KMFGenericDoc::setNatAddress( const TQString& addr ) {
	m_natAddress->setAddress( addr );
}

void KMFGenericDoc::setOutgoingInterface( const TQString& str ) {
	m_outgoingInterface = str;
}


TQPtrList<KMFNetZone>& KMFGenericDoc::zones() const {
	TQPtrList<KMFNetZone>* ret_val = new TQPtrList<KMFNetZone>;
	*ret_val = m_zones;
	return *ret_val;
}

KMFNetZone* KMFGenericDoc::addZone( const TQString& name, KMFError* err ) {
// 	kdDebug() << "KMFNetZone* addZone( const TQString& name, KMFError* err )" << endl;
	bool found = false;
	TQPtrListIterator<KMFNetZone> it ( m_zones );
	while ( it.current() && ! found ) {
		KMFNetZone * zone = it.current();
		++it;
		if ( zone->name() == name )
			found = true;
	}
	if ( found ) {
		err->setErrType( KMFError::NORMAL );
		err->setErrMsg( i18n( "<qt>Sorry, cannot create Zone with name '<b>%1</b>':<br>"
		                        "there already exists a zone with that name. Please try again"
		                        " with another name that is unique within your configuration.</qt>" ).arg(name) );
		return 0;
	}

	KMFNetZone * new_zone = new KMFNetZone( this, name.latin1(), name );
	m_zones.append( new_zone );
	err->setErrType( KMFError::OK );
	return new_zone;
}

KMFError* KMFGenericDoc::delZone( KMFNetZone* zone ) {
// 	kdDebug() << "KMFError* delZone( const TQString& name )" << endl;
	bool found = false;
	TQPtrListIterator<KMFNetZone> it ( m_zones );
	while ( it.current() && ! found ) {
		KMFNetZone * z = it.current();
		++it;
		if ( zone == z ) {
			found = true;
			m_zones.remove( zone );
			m_err->setErrType( KMFError::OK );
			changed();
		}
	}
	if ( ! found ) {
		m_err->setErrType( KMFError::NORMAL );
		m_err->setErrMsg( i18n( "<qt>Sorry, cannot create Zone with name: <b>%1</b>.<br>"
		                        "There already exists a zone with that name; please try again"
		                        " with another name that is unique within your configuration.</qt>" ).arg( zone->name() ) );
	}
	return m_err;
}

KMFNetZone* KMFGenericDoc::findZone( const TQString& name ) const {
// 	kdDebug() << "KMFNetZone* KMFGenericDoc::findZone( const TQString& name ) const" << endl;
	TQPtrListIterator<KMFNetZone> it ( m_zones );
	while ( it.current() ) {
		KMFNetZone *z = it.current();
		++it;
		if ( z->name() == name )
			return z;
	}
	return 0;
}

const TQString& KMFGenericDoc::compile() {
// 	kdDebug() << "const TQString& KMFGenericDoc::compile()" << endl;
	KMFCompilerInterface* compiler = target()->compiler();
	if ( ! compiler ) {
		return *( new TQString("ERROR") );
	}
	return compiler->compile( this );
}

void KMFGenericDoc::parseDocument( const KURL& url, TQStringList& errors ) {
// 	kdDebug() << "KMFDoc* KMFGenericDoc::parseDocument( const KURL& )" << endl;
	TQString xmlfile;
	if ( ! TDEIO::NetAccess::download( url, xmlfile, TDEApplication::kApplication()->mainWidget() ) ) {
		clear();
		m_url.setFileName( i18n( "Untitled" ) );
		return;
		// return this;
	}

	if ( !xmlfile.isEmpty() ) {
// 		kdDebug() << "Found xmlfile: " << xmlfile << endl;
		// delete old chainsets if there
		clear();
		TQFile kmfrsFile( xmlfile );
		TQDomDocument domTree;
		if ( !kmfrsFile.open( IO_ReadOnly ) ) {
			return;
			// return 0;
		}
		if ( !domTree.setContent( &kmfrsFile ) ) {
			kmfrsFile.close();
			return;
			// return 0;
		}
		kmfrsFile.close();

		kdDebug() << "############ Start Parsing ############" << endl;
		loadXML( domTree, errors );
		kdDebug() << "########## Finished Parsing ###########" << endl;
			
		m_url = url;
		// setUrl( url );
		emit documentChanged();
		TDEIO::NetAccess::removeTempFile( xmlfile );
		return;
	//	return this;
	}
	TDEIO::NetAccess::removeTempFile( xmlfile );
//	return this;
}

const TQDomDocument& KMFGenericDoc::getDOMTree() {
	// kdDebug() << "const TQDomDocument& KMFGenericDoc::getDOMTree()" << endl;
	TQDomDocument doc( "kmyfirewall-ruleset" );
	TQDomElement root = doc.createElement( XML::GenericDoc_DocumentElement );
	NetfilterObject::saveUuid( root );
	root.setAttribute( XML::Version_Attribute, KMYFIREWALL_VERSION );
	root.setAttribute( XML::MinVersion_Attribute, "1.0.0" );
	root.setAttribute( XML::MaxVersion_Attribute, "~" );

	root.appendChild( m_zone_incoming->getDOMTree() );
	root.appendChild( m_zone_outgoing->getDOMTree() );
	root.appendChild( m_zone_badClients->getDOMTree() );
	root.appendChild( m_zone_badServers->getDOMTree() );
	root.appendChild( m_zone_malicious->getDOMTree() );
	root.appendChild( m_zone_trusted->getDOMTree() );
	TQDomElement abstract = doc.createElement( XML::Abstract_Element );
	if ( restrictOutgoingConnections() )
		abstract.setAttribute( XML::RestrictOutgoingConnections_Attribute ,XML::BoolOn_Value );
	else
		abstract.setAttribute( XML::RestrictOutgoingConnections_Attribute ,XML::BoolOff_Value );
	if ( allowIncomingConnections() )
		abstract.setAttribute( XML::AllowIncomingConnections_Attribute ,XML::BoolOn_Value );
	else
		abstract.setAttribute( XML::AllowIncomingConnections_Attribute ,XML::BoolOff_Value );
	
	abstract.setAttribute( XML::Description_Attribute, description() );
	abstract.setAttribute(  XML::Name_Attribute, name() );
	
	root.appendChild( abstract );

	TQDomElement logging = doc.createElement( XML::Logging_Element );
	if ( logDropped() )
		logging.setAttribute( XML::LogDropped_Attribute, XML::BoolOn_Value );
	else
		logging.setAttribute( XML::LogDropped_Attribute, XML::BoolOff_Value );

	if ( limitLog() )
		logging.setAttribute( XML::LimitLog_Attribute, XML::BoolOn_Value );
	else
		logging.setAttribute( XML::LimitLog_Attribute, XML::BoolOff_Value );

	logging.setAttribute( XML::LogPrefix_Attribute, logPrefix() );
	root.appendChild( logging );

	TQDomElement icmp = doc.createElement( XML::ICMP_Element );
	if ( allowPingReply() ) {
		icmp.setAttribute( XML::AllowPingReply_Attribute, XML::BoolOn_Value );
	} else {
		icmp.setAttribute( XML::AllowPingReply_Attribute, XML::BoolOff_Value );
	}
	
	if ( limitPingReply() )
		icmp.setAttribute( XML::LimitPingReply_Attribute, XML::BoolOn_Value );
	else
		icmp.setAttribute( XML::LimitPingReply_Attribute, XML::BoolOff_Value );
	root.appendChild( icmp );

	TQDomElement nat = doc.createElement( XML::NAT_Element );
	if ( useNat() )
		nat.setAttribute( XML::UseNat_Attribute, XML::BoolOn_Value );
	else
		nat.setAttribute( XML::UseNat_Attribute, XML::BoolOff_Value );

	if ( useMasquerade() )
		nat.setAttribute( XML::UseMasquerade_Attribute, XML::BoolOn_Value );
	else
		nat.setAttribute( XML::UseMasquerade_Attribute, XML::BoolOff_Value );


	nat.setAttribute( XML::NatAddress_Attribute, m_natAddress->toString() );
	TQString allowedIn = "";

	nat.setAttribute( XML::OutgoingInterface_Attribute, m_outgoingInterface );

	root.appendChild( nat );

	doc.appendChild( root );
	return *( new TQDomDocument( doc ) );
}
void KMFGenericDoc::loadXML( const TQDomDocument& doc, TQStringList& errors ) {
	kdDebug() << "void KMFGenericDoc::loadXML( const TQDomDocument& )" << endl;
	TQDomElement root = doc.documentElement();
	if ( root.nodeName() != XML::GenericDoc_DocumentElement ) {
		kdDebug() << "!!! ERROR: Wrong XML format " << root.nodeName() << " found !!!" << endl;
		errors.append( KMFError::getAsString( KMFError::FATAL, i18n("Wrong XML format <b>%1</b> found! Expected kmfgrs").arg( root.nodeName() ) ) );
		return;
 	}
 	loadXML( root, errors );
}

void KMFGenericDoc::loadXML( TQDomNode root, TQStringList& errors ) {
// void KMFGenericDoc::loadXML( const TQDomDocument& doc ) {
// 	kdDebug() << "void KMFGenericDoc::loadXML( const TQDomDocument& )" << endl;
//	TQDomElement root = doc.documentElement();
	NetfilterObject::loadUuid ( root, errors );
	TQDomNode curr = root.firstChild();
	while ( !curr.isNull() ) {
		kdDebug() << "Parsing Node: " << curr.nodeName() << endl;
		if ( curr.isElement() && curr.nodeName() == XML::NetZone_Element ) {
			TQString name = "";
			name = curr.toElement().attribute(  XML::Name_Attribute );
			TQDomDocument zone_doc;
			zone_doc.appendChild( curr.cloneNode(true) );
			if ( name == "incoming_world" ) {
// 				kdDebug() << "\nKMFIPTDoc: Start Parsing INCOMMING ZONES" << endl;
				m_zone_incoming->loadXML( zone_doc, errors );
// 				kdDebug() << "KMFIPTDoc: Finished Parsing INCOMMING ZONES" << endl;
			}
			if ( name == "outgoing_world" ) {
// 				kdDebug() << "\nKMFIPTDoc: Start Parsing OUTGOING ZONES" << endl;
				m_zone_outgoing->loadXML( zone_doc, errors );
// 				kdDebug() << "KMFIPTDoc: Finished Parsing OUTGOING ZONES" << endl;
			}
			if ( name == "trusted_hosts" ) {
// 				kdDebug() << "\nKMFIPTDoc: Start Parsing TRUSTED HOSTS" << endl;
				m_zone_trusted->loadXML( zone_doc, errors );
// 				kdDebug() << "KMFIPTDoc: Finished Parsing TRUSTED HOSTS" << endl;
			}
			if ( name == "malicious_hosts" ) {
// 				kdDebug() << "\nKMFIPTDoc: Start Parsing MALICIOUS HOSTS" << endl;
				m_zone_malicious->loadXML( zone_doc, errors );
// 				kdDebug() << "KMFIPTDoc: Finished Parsing MALICIOUS HOSTS" << endl;
			}
			if ( name == "badClients_hosts" ) {
// 				kdDebug() << "\nKMFIPTDoc: Start Parsing BADCLIENTS HOSTS" << endl;
				m_zone_badClients->loadXML( zone_doc, errors );
// 				kdDebug() << "KMFIPTDoc: Finished Parsing BADCLIENTS HOSTS" << endl;
			}
			if ( name == "badServers_hosts" ) {
// 				kdDebug() << "\nKMFIPTDoc: Start Parsing BADSERVERS HOSTS" << endl;
				m_zone_badServers->loadXML( zone_doc, errors );
// 				kdDebug() << "KMFIPTDoc: Finished Parsing BADSERVERS HOSTS" << endl;
			}
		} else if ( curr.isElement() && curr.nodeName() == XML::Logging_Element ) {
// 			kdDebug() << "\nKMFIPTDoc: Start Parsing Logging" << endl;
			TQString logDropped = "";
			TQString limitLog = "";
			TQString logPrefix = "";

			logDropped = curr.toElement().attribute( XML::LogDropped_Attribute );
			limitLog = curr.toElement().attribute( XML::LimitLog_Attribute );
			logPrefix = curr.toElement().attribute( XML::LogPrefix_Attribute );

			if ( logDropped == XML::BoolOn_Value )
				setLogDropped( true );
			else
				setLogDropped( false );

			if ( limitLog == XML::BoolOn_Value )
				setLimitLog( true );
			else
				setLimitLog( false );

			setLogPrefix( logPrefix );
// 			kdDebug() << "KMFIPTDoc: Finished Parsing Logging" << endl;
		} else if ( curr.isElement() && curr.nodeName() == XML::ICMP_Element ) {
// 			kdDebug() << "\nKMFIPTDoc: Start Parsing ICMP" << endl;
			TQString allowPing = "";
			TQString limitPing = "";
			allowPing = curr.toElement().attribute( XML::AllowPingReply_Attribute );
			limitPing = curr.toElement().attribute( XML::LimitPingReply_Attribute );

			if ( allowPing == XML::BoolOn_Value)
				setAllowPingReply( true );
			else
				setAllowPingReply( false );

			if ( limitPing == XML::BoolOn_Value )
				setLimitPingReply( true );
			else
				setLimitPingReply( false );

// 			kdDebug() << "KMFIPTDoc: Finished Parsing ICMP" << endl;
		} else if ( curr.isElement() && curr.nodeName() == XML::NAT_Element ) {
// 			kdDebug() << "\nKMFIPTDoc: Start Parsing NAT" << endl;
			TQString useNat = "";
			TQString useMasquerade = "";
			TQString natAddress = "";
			TQString outgoingInterface = "";
			useNat = curr.toElement().attribute( XML::UseNat_Attribute );
			useMasquerade = curr.toElement().attribute( XML::UseMasquerade_Attribute );
			natAddress = curr.toElement().attribute( XML::NatAddress_Attribute );
			outgoingInterface = curr.toElement().attribute( XML::OutgoingInterface_Attribute );



			if ( useNat == XML::BoolOn_Value)
				setUseNat( true );
			else
				setUseNat( false );

			if ( useMasquerade == XML::BoolOn_Value)
				setUseMasquerade( true );
			else
				setUseMasquerade( false );

			m_outgoingInterface = outgoingInterface;
			m_natAddress->setAddress( natAddress );
// 			kdDebug() << "Stored NAT address: " << m_natAddress->toString() << endl;
// 			kdDebug() << "KMFIPTDoc: Finished Parsing NAT" << endl;
		}else if ( curr.isElement() && curr.nodeName() == XML::Abstract_Element ) {
			kdDebug() << "KMFIPTDoc: Start Parsing Abstract" << endl;
			TQString allowIncomingConnections = "";
			TQString restrictOutgoingConnections = "";
			TQString description = "";
			TQString name = "";
			allowIncomingConnections =curr.toElement().attribute( XML::AllowIncomingConnections_Attribute );
			restrictOutgoingConnections =curr.toElement().attribute( XML::RestrictOutgoingConnections_Attribute );
			
			description += curr.toElement().attribute( XML::Description_Attribute );
			if ( ! description.isNull() )
				setDescription( *( new TQString( description ) ) );
			
			name += curr.toElement().attribute(  XML::Name_Attribute );
			if ( ! name.isNull() )
				setName( *( new TQString( name ) ) );
			
				
			if ( allowIncomingConnections == XML::BoolOn_Value )
				setAllowIncomingConnections( true );
			else
				setAllowIncomingConnections( false );

			if ( restrictOutgoingConnections == XML::BoolOn_Value )
				setRestrictOutgoingConnections( true );
			else
				setRestrictOutgoingConnections( false );
 			kdDebug() << "KMFIPTDoc: Finished Parsing Abstract" << endl;
		}
		curr = curr.nextSibling();
	}
}

}

