//
// C++ Implementation: kmfiptdoc
//
// 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 "kmfiptdoc.h"

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

// StdLib includes
#include <iostream>
#include <unistd.h>
#include <sys/stat.h>

// QT includes
#include <tqtextstream.h>
#include <tqfile.h>
#include <tqxml.h>
#include <tqregexp.h>
#include <tqptrlist.h>
#include <tqmessagebox.h>
#include <tqstringlist.h>
#include <tqvbox.h>
#include <tqlabel.h>

// KDE includes
#include <kdebug.h>
#include <kstandarddirs.h>
#include <kfiledialog.h>
#include <kurl.h>
#include <tqfiledialog.h>
#include <kio/job.h>
#include <kio/netaccess.h>
#include <klocale.h>
#include <kprocess.h>
#include <kapplication.h>
#include <kconfig.h>
#include <kmessagebox.h>
#include <ktempfile.h>
#include <ktrader.h>
#include <klibloader.h>


// My includes
#include "../version.h"
#include "kmfcompilerinterface.h"
#include "kmfplugin.h"
#include "kmfdoc.h"
#include "iptchain.h"
#include "iptrule.h"
#include "iptable.h"
#include "netfilterobject.h"
#include "kmferror.h"
#include "kmferrorhandler.h"
#include "kmfpluginfactory.h"
#include "kmfconfig.h"
#include "kmftarget.h"
#include "kmfnetwork.h"
#include "kmfundoengine.h"
#include "xmlnames.h"

namespace KMF {

KMFIPTDoc::KMFIPTDoc( NetfilterObject *parent, const char *name, KMFTarget* target ) : KMFDoc( parent, name ), KMFRulesetDoc( target ) { 
	initDoc();
}


KMFIPTDoc::~KMFIPTDoc() {
	kdDebug() << "Callong KMFIPTDoc::~KMFIPTDoc()" << endl;
	m_ipt_filter->deleteLater();
	m_ipt_nat->deleteLater();;
	m_ipt_mangle->deleteLater();;

	// error handler
	delete m_err;
	m_err = 0;
}

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


void KMFIPTDoc::initDoc() {
	// 	kdDebug() << "\n\nvoid KMFIPTDoc::initKMFIPTDoc()" << endl;
	// registerRuleOptions();

	m_err_handler = new KMFErrorHandler( "KMFIPTDoc" );
	m_err = new KMFError();
	m_url.setFileName( i18n( "Untitled" ) );
	m_use_filter = true;
	m_use_nat = true;
	m_use_mangle = true;
	m_use_ipfwd = true;
	m_use_rp_filter = false;
	m_use_martians = false;
	m_use_syn_cookies = true;
	m_use_modules = true;
//	is_saved = false;

	m_ipt_filter = new IPTable( this, Constants::FilterTable_Name.latin1(),
                Constants::FilterTable_Name.latin1() );
	m_ipt_filter->settupDefaultChains();
	m_ipt_nat = new IPTable( this, Constants::NatTable_Name.latin1(), Constants::NatTable_Name );
	m_ipt_nat->settupDefaultChains();
	m_ipt_mangle = new IPTable( this, Constants::MangleTable_Name.latin1(), Constants::MangleTable_Name );
	
	m_ipt_mangle->settupDefaultChains();
}

void KMFIPTDoc::clear() {
	m_url.setFileName( i18n( "Untitled" ) );
	m_use_filter = true;
	m_use_nat = true;
	m_use_mangle = true;
	m_use_ipfwd = true;
	m_use_rp_filter = false;
	m_use_martians = false;
	m_use_syn_cookies = true;
	m_use_modules = true;
	// m_changed_objects.clear();
	// is_saved = false;

	m_ipt_filter->reset();
	m_ipt_nat->reset();
	m_ipt_mangle->reset();
	
	setName( i18n("Unamed Ruleset") );
	setDescription( i18n("No description available") );
	resetUrl();
}

const TQString& KMFIPTDoc::getFileDialogFilter() {
	return *( new TQString("*.kmfrs|KMyFirewall IPTables Ruleset(*.kmfrs)") );
}

void KMFIPTDoc::setUseFilter( bool on ) {
	if ( m_use_filter != on ) {
		m_use_filter = on;
		changed();
	}
}

void KMFIPTDoc::setUseNat( bool on ) {
	if ( m_use_nat != on ) {
		m_use_nat = on;
		changed();
	}
}

void KMFIPTDoc::setUseMangle( bool on ) {
	if ( m_use_mangle != on ) {
		m_use_mangle = on;
		changed();
	}
}

void KMFIPTDoc::setUseIPFwd( bool on ) {
	if ( m_use_ipfwd != on ) {
		m_use_ipfwd = on;
		changed();
	}
}

void KMFIPTDoc::setUseRPFilter( bool on ) {
	if ( m_use_rp_filter != on ) {
		m_use_rp_filter = on;
		changed();
	}
}
void KMFIPTDoc::setUseMartians( bool on ) {
	if ( m_use_martians != on ) {
		m_use_martians = on;
		changed();
	}
}
void KMFIPTDoc::setUseSynCookies( bool on ) {
	if ( m_use_syn_cookies != on ) {
		m_use_syn_cookies = on;
		changed();
	}
}
void KMFIPTDoc::setUseModules( bool on ) {
	if ( m_use_modules != on ) {
		m_use_modules = on;
		changed();
	}
}

bool KMFIPTDoc::isEmpty() {
	if ( m_ipt_filter->chains().isEmpty() && m_ipt_nat->chains().isEmpty() && m_ipt_mangle->chains().isEmpty() ) {
		return true;
	}	else {
	    return false;
	}
	return false;
}

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

	TQDomElement abstract = doc.createElement( XML::Abstract_Element );
	root.appendChild( abstract );
	if ( useFilter() ) {
		abstract.setAttribute( XML::UseFilter_Attribute, XML::Yes_Value );
	} else {
		abstract.setAttribute( XML::UseFilter_Attribute, XML::No_Value );
	}
	if ( useNat() ) {
		abstract.setAttribute( XML::Use_Nat_Attribute, XML::Yes_Value );
	} else {
		abstract.setAttribute( XML::Use_Nat_Attribute, XML::No_Value );
	}
	if ( useMangle() ) {
		abstract.setAttribute( XML::UseMangle_Attribute, XML::Yes_Value );
	} else {
		abstract.setAttribute( XML::UseMangle_Attribute, XML::No_Value );
	}
	if ( useModules() ) {
		abstract.setAttribute( XML::UseModules_Attribute, XML::Yes_Value );
	} else {
		abstract.setAttribute( XML::UseModules_Attribute, XML::No_Value );
	}
	if ( useRPFilter() ) {
		abstract.setAttribute( XML::UseRpFilter_Attribute, XML::Yes_Value );
	} else {
		abstract.setAttribute( XML::UseRpFilter_Attribute, XML::No_Value );
	}
	if ( useIPFwd() ) {
		abstract.setAttribute( XML::UseIPFwd_Attribute, XML::Yes_Value );
	} else {
		abstract.setAttribute( XML::UseIPFwd_Attribute, XML::No_Value );
	}
	if ( useSynCookies() ) {
		abstract.setAttribute( XML::UseSynCookies_Attribute, XML::Yes_Value );
	} else {
		abstract.setAttribute( XML::UseSynCookies_Attribute, XML::No_Value );
	}
	if ( useMartians() ) {
		abstract.setAttribute( XML::UseMartians_Attribute, XML::Yes_Value );
	} else {
		abstract.setAttribute( XML::UseMartians_Attribute, XML::No_Value );
	}
	abstract.setAttribute( XML::Description_Attribute, description() );
	abstract.setAttribute(  XML::Name_Attribute, name() );
	
	root.appendChild( m_ipt_filter->getDOMTree( ) );
	root.appendChild( m_ipt_nat->getDOMTree( ) );
	root.appendChild( m_ipt_mangle->getDOMTree( ) );
	doc.appendChild( root );
	return *( new TQDomDocument( doc ) );
}
void KMFIPTDoc::loadXML( const TQDomDocument& doc, TQStringList& errors ) {
	kdDebug() << "void KMFIPTDoc::loadXML( const TQDomDocument& )" << endl;
 	TQDomElement root = doc.documentElement();
 	if ( root.nodeName() != XML::IPTDoc_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 kmfrs").arg( root.nodeName() ) ) );
		return;
 	}
 	loadXML( root, errors );
}
void KMFIPTDoc::loadXML( TQDomNode root, TQStringList& errors ) {
	kdDebug() << "void KMFIPTDoc::loadXML( TQDomNode root, TQStringList& errors )" << endl;
	if ( root.nodeName() != XML::IPTDoc_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 kmfrs").arg( root.nodeName() ) ) );
		return;
 	}
	NetfilterObject::loadUuid( root, errors );
	TQDomNode curr = root.firstChild();
	while ( !curr.isNull() ) {
		kdDebug() << "Parsing Node: " << curr.nodeName() << endl;
		if ( curr.isElement() && curr.nodeName() == XML::Table_Element  ) {
			TQString name = curr.toElement().attribute(  XML::Name_Attribute );
			kdDebug() << "KMFIPTDoc: Start Parsing Table: " << name << endl;
			TQDomDocument table;
			table.appendChild( curr.cloneNode( true ) );
			if ( name == Constants::FilterTable_Name ) {
				m_ipt_filter->loadXML( table, errors );
			} else if ( name == Constants::NatTable_Name ) {
				m_ipt_nat->loadXML( table, errors );
			} else if ( name == Constants::MangleTable_Name ) {
				m_ipt_mangle->loadXML( table, errors );
			}
			kdDebug() << "KMFIPTDoc: Finished Parsing Table: " << name << endl;
		} else if ( curr.isElement() && curr.nodeName() == XML::Abstract_Element ) {
			kdDebug() << "KMFIPTDoc: Start Parsing Abstract" << endl;
			TQString filter;
			TQString nat;
			TQString mangle;
			TQString martians;
			TQString ipfwd;
			TQString syncookies;
			TQString rpfilter;
			TQString modules;
			TQString description = "";
			TQString name = "";
			filter = curr.toElement().attribute( XML::UseFilter_Attribute );
			nat = curr.toElement().attribute( XML::Use_Nat_Attribute );
			mangle = curr.toElement().attribute( XML::UseMangle_Attribute );
			martians = curr.toElement().attribute( XML::UseMartians_Attribute );
			ipfwd = curr.toElement().attribute( XML::UseIPFwd_Attribute );
			syncookies = curr.toElement().attribute( XML::UseSynCookies_Attribute );
			rpfilter = curr.toElement().attribute( XML::UseRpFilter_Attribute );
			modules = curr.toElement().attribute( XML::UseModules_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 ( filter == XML::Yes_Value )
				m_use_filter = true;
			else
				m_use_filter = false;

			if ( nat == XML::Yes_Value )
				m_use_nat = true;
			else
				m_use_nat = false;

			if ( mangle == XML::Yes_Value )
				m_use_mangle = true;
			else
				m_use_mangle = false;

			if ( martians == XML::Yes_Value )
				m_use_martians = true;
			else
				m_use_martians = false;

			if ( ipfwd == XML::Yes_Value )
				m_use_ipfwd = true;
			else
				m_use_ipfwd = false;

			if ( syncookies == XML::Yes_Value )
				m_use_syn_cookies = true;
			else
				m_use_syn_cookies = false;

			if ( rpfilter == XML::Yes_Value )
				m_use_rp_filter = true;
			else
				m_use_rp_filter = false;

			if ( modules == XML::Yes_Value )
				m_use_modules = true;
			else
				m_use_modules = false;

			
			kdDebug() << "KMFIPTDoc: Finished Parsing Abstract" << endl;
		}
		curr = curr.nextSibling();
	}
	changed();
}

IPTable* KMFIPTDoc::table( const TQString& table ) {
	if ( table == Constants::FilterTable_Name )
		return m_ipt_filter;
	if ( table == Constants::NatTable_Name )
		return m_ipt_nat;
	if ( table == Constants::MangleTable_Name )
		return m_ipt_mangle;
	return 0;
}

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

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

	if ( !xmlfile.isEmpty() ) {
		// 		kdDebug() << "Found xmlfile: " << xmlfile << endl;
		// delete old chainsets if there
		if ( !m_ipt_filter->chains().isEmpty() )
			m_ipt_filter->reset();
		if ( !m_ipt_nat->chains().isEmpty() )
			m_ipt_nat->reset();
		if ( !m_ipt_mangle->chains().isEmpty() )
			m_ipt_mangle->reset();
		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;

		// setUrl( url );
		m_url = url;
		emit documentChanged();
		KIO::NetAccess::removeTempFile( xmlfile );
		return;
	//	return this;
	}
	KIO::NetAccess::removeTempFile( xmlfile );
	// return this;
}

void KMFIPTDoc::registerRuleOptions() {
	kdDebug() << "KMFIPTDoc::registerRuleOptions()" << endl;

	KStandardDirs std_dir;
	TQStringList files = std_dir.findAllResources(
	                        "data", "kmyfirewall/ruleoptions/kmfruleoption*.xml", false, true );

	for ( TQStringList::Iterator it = files.begin(); it != files.end(); ++it ) {
		kdDebug() << "Found Option XML File: " << *it << endl;
		TQString xmlfile = *it;
		TQFile document( xmlfile );
		TQDomDocument domTree;
		if ( !document.open( IO_ReadOnly ) ) {
			kdDebug() << "ERROR: Can't read XML ruole option definition" << endl;
			return;
		}
		if ( !domTree.setContent( &document ) ) {
			kdDebug() << "ERROR: XML corrupted in file: " <<  xmlfile << endl;
			document.close();
			return;
		}
		document.close();

		kdDebug() << "############ Start Parsing ############" << endl;
		IPTRuleOption::readRuleOptionDefinition( domTree );
		kdDebug() << "########## Finished Parsing ###########" << endl;
	}
}

}
