//
// C++ Implementation: kmfiptdoc
//
// Description:
//
//
// Author: Christian Hubinger <chubinger@gmail.com>, (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 <qtextstream.h>
#include <qfile.h>
#include <qxml.h>
#include <qregexp.h>
#include <qptrlist.h>
#include <qmessagebox.h>
#include <qstringlist.h>
#include <qvbox.h>
#include <qlabel.h>

// KDE includes
#include <kdebug.h>
#include <kstandarddirs.h>
#include <kfiledialog.h>
#include <kurl.h>
#include <qfiledialog.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 "../interfaces/kmfcompilerinterface.h"
#include "../interfaces/kmfplugin.h"
// #include "../kmfxmlparser.h" // FIXME: REMOVE
#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"

KMFIPTDoc::KMFIPTDoc( QObject *parent, const char *name ) : KMFDoc( parent, name ) {
	initDoc();
}

KMFIPTDoc::~KMFIPTDoc() {
	kdDebug() << "Callong KMFIPTDoc::~KMFIPTDoc()" << endl;
	delete m_ipt_filter;
	delete m_ipt_nat;
	delete m_ipt_mangle;

	// error handler
	delete m_err_handler;
	delete m_err;
}

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, "filter" );
	m_ipt_filter->settupDefaultChains();
	m_ipt_nat = new IPTable( this, "nat" );
	m_ipt_nat->settupDefaultChains();
	m_ipt_mangle = new IPTable( this, "mangle" );
	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();
	m_name = i18n("Unamed Ruleset");
	m_description = i18n("No description available");
}


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 QDomDocument& KMFIPTDoc::getDOMTree() {
	// 	kdDebug() << "const QString& IPTChain::getIDOMree( )" << endl;
	QDomDocument doc( "kmyfirewall-ruleset" );
	QDomElement root = doc.createElement( "kmfrs" );

	QDomElement abstract = doc.createElement( "abstract" );
	root.appendChild( abstract );
	if ( useFilter() ) {
		abstract.setAttribute( "use_filter", "yes" );
	} else {
		abstract.setAttribute( "use_filter", "no" );
	}
	if ( useNat() ) {
		abstract.setAttribute( "use_nat", "yes" );
	} else {
		abstract.setAttribute( "use_nat", "no" );
	}
	if ( useMangle() ) {
		abstract.setAttribute( "use_mangle", "yes" );
	} else {
		abstract.setAttribute( "use_mangle", "no" );
	}
	if ( useModules() ) {
		abstract.setAttribute( "use_modules", "yes" );
	} else {
		abstract.setAttribute( "use_modules", "no" );
	}
	if ( useRPFilter() ) {
		abstract.setAttribute( "use_rp_filter", "yes" );
	} else {
		abstract.setAttribute( "use_rp_filter", "no" );
	}
	if ( useIPFwd() ) {
		abstract.setAttribute( "use_ipfwd", "yes" );
	} else {
		abstract.setAttribute( "use_ipfwd", "no" );
	}
	if ( useSynCookies() ) {
		abstract.setAttribute( "use_syn_cookies", "yes" );
	} else {
		abstract.setAttribute( "use_syn_cookies", "no" );
	}
	if ( useMartians() ) {
		abstract.setAttribute( "use_martians", "yes" );
	} else {
		abstract.setAttribute( "use_martians", "no" );
	}
	abstract.setAttribute( "description", description() );
	abstract.setAttribute( "name", name() );
	
	root.appendChild( m_ipt_filter->getDOMTree( ) );
	root.appendChild( m_ipt_nat->getDOMTree( ) );
	root.appendChild( m_ipt_mangle->getDOMTree( ) );
	doc.appendChild( root );
	return *( new QDomDocument( doc ) );
}

void KMFIPTDoc::loadXML( const QDomDocument& doc ) {
	// 	kdDebug() << "void KMFIPTDoc::loadXML( const QDomDocument& doc )" << endl;
	// 	kdDebug() << "Parsing XML:\n" << doc.toString() << endl;
	QDomElement root = doc.documentElement();
	QDomNode curr = root.firstChild();
	while ( !curr.isNull() ) {
		// 		kdDebug() << "Parsing Node: " << curr.nodeName() << endl;
		if ( curr.isElement() && curr.nodeName() == "table" ) {
			QString name = curr.toElement().attribute( "name" );
			// 			kdDebug() << "KMFIPTDoc: Start Parsing Table: " << name << endl;
			QDomDocument table;
			table.appendChild( curr.cloneNode( true ) );
			if ( name == "filter" ) {
				m_ipt_filter->loadXML( table );
			} else if ( name == "nat" ) {
				m_ipt_nat->loadXML( table );
			} else if ( name == "mangle" ) {
				m_ipt_mangle->loadXML( table );
			}
			// 			kdDebug() << "KMFIPTDoc: Finished Parsing Table: " << name << endl;
		} else if ( curr.isElement() && curr.nodeName() == "abstract" ) {
			// 			kdDebug() << "KMFIPTDoc: Start Parsing Abstract" << endl;
			QString filter;
			QString nat;
			QString mangle;
			QString martians;
			QString ipfwd;
			QString syncookies;
			QString rpfilter;
			QString modules;
			QString description = "";
			QString name = "";
			filter = curr.toElement().attribute( "use_filter" );
			nat = curr.toElement().attribute( "use_nat" );
			mangle = curr.toElement().attribute( "use_mangle" );
			martians = curr.toElement().attribute( "use_martians" );
			ipfwd = curr.toElement().attribute( "use_ipfwd" );
			syncookies = curr.toElement().attribute( "use_syn_cookies" );
			rpfilter = curr.toElement().attribute( "use_rp_filter" );
			modules = curr.toElement().attribute( "use_modules" );
			
			description += curr.toElement().attribute( "description" );
			if ( ! description.isNull() )
				setDescription( *( new QString( description ) ) );
			
			name += curr.toElement().attribute( "name" );
			if ( ! name.isNull() )
				setName( *( new QString( name ) ) );
			
			if ( filter == "yes" )
				m_use_filter = true;
			else
				m_use_filter = false;

			if ( nat == "yes" )
				m_use_nat = true;
			else
				m_use_nat = false;

			if ( mangle == "yes" )
				m_use_mangle = true;
			else
				m_use_mangle = false;

			if ( martians == "yes" )
				m_use_martians = true;
			else
				m_use_martians = false;

			if ( ipfwd == "yes" )
				m_use_ipfwd = true;
			else
				m_use_ipfwd = false;

			if ( syncookies == "yes" )
				m_use_syn_cookies = true;
			else
				m_use_syn_cookies = false;

			if ( rpfilter == "yes" )
				m_use_rp_filter = true;
			else
				m_use_rp_filter = false;

			if ( modules == "yes" )
				m_use_modules = true;
			else
				m_use_modules = false;

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

IPTable* KMFIPTDoc::table( const QString& table ) {
	if ( table == "filter" )
		return m_ipt_filter;
	if ( table == "nat" )
		return m_ipt_nat;
	if ( table == "mangle" )
		return m_ipt_mangle;
	return 0;
}

const QString& KMFIPTDoc::compile() {
	KMFCompilerInterface* compiler = KMFPluginFactory::KMFCompiler( "linux", "iptables", parent() );
	if ( ! compiler ) {
		return *( new QString("ERROR") );
	}
	return compiler->compile( this );
}

KMFError* KMFIPTDoc::createFirewallScript( const QString& fi ) {
	// 	kdDebug() << "void KMFIPTDoc::createFirewallScript(const QString& fi)" << endl;
	if ( KMFConfig::firstRun() ) {
		m_err->setErrType( KMFError::NORMAL );
		const QString& msg = i18n( "<qt>You didn't configure KMyFirewall yet.<br>"
		                           "Click <b>Settings-&gt;Configure KMyFirewall...</b> to setup a valid configuration.</qt>" );
		m_err->setErrMsg( msg );
		return m_err;
	}

	QString file;
	if ( fi.isEmpty() ) {
		KURL url ( KGlobal::dirs() ->findResource( "appdata", "scripts/" ));
		file = url.path();
		// 		kdDebug() << "Scriptdir: " << file << endl;
		file = file + "kmyfirewall.sh";
		kdDebug() << "Scriptfile: " << file << endl;
	} else {
		file = fi;
	}
	if ( file.isEmpty() ) {
		kdDebug() << "Cant open output file" << endl;
		m_err->setErrType( KMFError::NORMAL );
		const QString& msg = i18n( "Opening file for writing failed.\n"
		                           "Please make sure that you are logged in as root." );
		m_err->setErrMsg( msg );
		return m_err;

	}

	QFile f( file );
	f.remove();
	bool gotit = f.open( IO_ReadWrite );
	if ( ! gotit ) {
		m_err->setErrType( KMFError::NORMAL );
		const QString& msg = i18n( "Opening file for writing failed.\n"
		                           "Please make sure that you are logged in as root." );
		m_err->setErrMsg( msg );
		return m_err;
	}

	QTextStream ts( &f );
	QString s;
	kdDebug() << "########### start compiling ###########" << endl;
	ts << compile() << endl;
	kdDebug() << "########### Finished compiling ###########" << endl;
	f.flush();
	f.close();
	m_err->setErrType( KMFError::OK );
	const QString& msg = "";
	m_err->setErrMsg( msg );
	return m_err;
}

KMFIPTDoc* KMFIPTDoc::parseXMLRuleset( const KURL& url ) {
	// 	kdDebug() << "KMFIPTDoc::parseXMLRuleset()" << endl;
	QString xmlfile;
	if ( ! KIO::NetAccess::download( url, xmlfile, 0 ) ) {
		clear();
		m_url.setFileName( i18n( "Untitled" ) );
		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();
		QFile kmfrsFile( xmlfile );
		QDomDocument domTree;
		if ( !kmfrsFile.open( IO_ReadOnly ) ) {
			return 0;
		}
		if ( !domTree.setContent( &kmfrsFile ) ) {
			kmfrsFile.close();
			return 0;
		}
		kmfrsFile.close();

		kdDebug() << "############ Start Parsing ############" << endl;
		loadXML( domTree );
		kdDebug() << "########## Finished Parsing ###########" << endl;

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

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

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

	for ( QStringList::Iterator it = files.begin(); it != files.end(); ++it ) {
		kdDebug() << "Found Option XML File: " << *it << endl;
		QString xmlfile = *it;
		QFile document( xmlfile );
		QDomDocument 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;
	}
}
