//
// C++ Implementation: kmfpfcompiler
//
// Description:
//
//
// Author: Christian Hubinger <e9806056@student.tuwien.ac.at>, (C) 2004
//
// Copyright: See COPYING file that comes with this distribution
//
//
#include "kmfpfcompiler.h"

// QT includes
#include <tqptrlist.h>
#include <tqstringlist.h>
#include <tqmultilineedit.h>

// KDE includes
#include <kdebug.h>
#include <klocale.h>
#include <kaboutdata.h>
#include <klocale.h>

// Project includes
#include "../../core/kmfgenericdoc.h"
#include "../../core/kmfiptdoc.h"
#include "../../core/kmfnetzone.h"
#include "../../core/kmfnethost.h"
#include "../../core/kmfprotocol.h"
#include "../../core/kmferror.cpp"
#include "../../core/kmfprotocol.h"
#include "../../core/kmfprotocolusage.h"

#include "../../kmfwidgets/kmflistview.h"
namespace KMF {

KMFPFCompiler::KMFPFCompiler( TQObject* parent, const char* name ) : KMFPlugin( parent, name )  {
	m_osName = "openbsd";
	m_osGUIName = "OpenBSD";
	m_backendName = "pf";
	m_backendGUIName = "PF";

	
	
	if ( genericDoc() ) {
		new KAction( i18n( "Export as &PF (OpenBSD) Script" ),  "fileexport",
									0, this, TQT_SLOT( slotExportPF() ), actionCollection(), "compile_pf" );

		setXMLFile( "kmfpfcompiler.rc" );
		kdDebug() << "KMFPFCompiler: Finished initialisation." << endl;
	}
}


KMFPFCompiler::~KMFPFCompiler() {}

const TQString& KMFPFCompiler::osName(){
	return m_osName;
}
const TQString& KMFPFCompiler::osGUIName(){
	return m_osGUIName;
}
const TQString& KMFPFCompiler::backendName(){
	return m_backendName;
}
const TQString& KMFPFCompiler::backendGUIName(){
	return m_backendGUIName;
}


void KMFPFCompiler::compile() {
	kdDebug() << "void KMFPFCompiler::compile()" << endl;
	kdDebug() << "Doc XML:\n"  << m_doc->getXMLSniplet() << endl;
	
}

void KMFPFCompiler::slotExportPF() {
	kdDebug() << "KMFPFCompiler::slotExportIPT()" << endl;
	TQString s = compile( genericDoc() );
	kdDebug() << "Generated scrip: "  << s  << endl;
}

const TQString& KMFPFCompiler::compile( KMFGenericDoc* gendoc ) {
	kdDebug() << "const TQString& KMFPFCompiler::compile( KMFGenericDoc* doc )" << endl;
	if ( gendoc ) {
		m_compiledScript = "";
		
		setupNatRules( gendoc );
		
		setupLocalhostRules();
		
		setupInAndOutHosts( gendoc->trustedHostsZone(), "pass" );
		setupInAndOutHosts( gendoc->maliciousHostsZone(), "block" );
		setupForbiddenHosts( gendoc->badClientsHostsZone(), "in" );
		setupForbiddenHosts( gendoc->badServersHostsZone(), "out" );
		setupICMPRules( gendoc );
		
		createIncommingACCESSRules( 0, gendoc );
		createOutgoingACCESSRules( 0, gendoc );
		setupPolicies( gendoc );
		kdDebug() << "Return compiled pf:\n " << m_compiledScript << endl;
		return m_compiledScript;
	} else {
		return *( new TQString( "ERROR: Couldn't compile document - may be wrong type " ) );
	}
}

void KMFPFCompiler::setupInAndOutHosts( KMFNetZone* zone ,  const TQString& cmd ) {
	kdDebug() << "KMFPFCompiler::setupInAndOutHosts( KMFNetZone* )" << endl;
	if ( zone->hosts().count() == 0 ) {
		return;
	}
	
	TQPtrListIterator<KMFTarget> it ( zone->hosts() );
	if ( cmd == "pass" ) {
		m_compiledScript.append( "\n# Trusted Hosts\n" );
	} else {
		m_compiledScript.append( "\n# Maliciuous Hosts\n" );
	}
	
	while ( it.current() ) {
		KMFNetHost *host = dynamic_cast<KMFNetHost*> ( *it );
		++it;
		
		if ( cmd == "pass" ) {
			m_compiledScript.append( "pass in quick from " );
			m_compiledScript.append( host->address()->toString() );
			m_compiledScript.append( " to any keep state\n" );
		} else {
			m_compiledScript.append( "block in quick from " );
			m_compiledScript.append( host->address()->toString() );
			m_compiledScript.append( " to any\n" );
		}
	}
}

void KMFPFCompiler::setupForbiddenHosts( KMFNetZone* zone, const TQString& inOut ) {
	if ( zone->hosts().count() == 0 ) {
		return;
	}
	
	TQPtrListIterator<KMFTarget> it ( zone->hosts() );
	if ( inOut == "in" ) {
		m_compiledScript.append( "\n# Forbidden Clients\n" );
	} else {
		m_compiledScript.append( "\n# Forbidden Server\n" );
	}
	
	while ( it.current() ) {
		KMFNetHost *host = dynamic_cast<KMFNetHost*> ( *it );
		++it;
		if ( inOut == "in" ) {
			m_compiledScript.append( "block in quick from " );
			m_compiledScript.append( host->address()->toString() );
			m_compiledScript.append( " to any\n" );
		} else {
			m_compiledScript.append( "block out quick from any to " );
			m_compiledScript.append( host->address()->toString() );
			m_compiledScript.append( "\n" );
		}
	}
}

void KMFPFCompiler::setupICMPRules( KMFGenericDoc* doc ) {
	if ( ! doc->allowPingReply() ) {
		return;
	}
	m_compiledScript.append( "\n# Allow ping\n" );
	m_compiledScript.append( "pass in quick inet proto icmp all icmp-type { 0, 3, 11 } keep state\n" );
}

void KMFPFCompiler::setupLocalhostRules() {
	m_compiledScript.append( "\n# Allow loopback traffic\n" );
	m_compiledScript.append( "pass in quick on lo0 from 127.0.0.1 to 127.0.0.1 keep state\n" );
	m_compiledScript.append( "pass out quick on lo0 from 127.0.0.1 to 127.0.0.1 keep state\n" );
}

void KMFPFCompiler::createIncommingACCESSRules( KMFNetZone* zone, KMFGenericDoc* doc ) {
	kdDebug() << "void KMFPFCompiler::createIncommingACCESSRules( KMFNetZone* zone, KMFGenericDoc* doc )" << endl;
	if ( ! doc->allowIncomingConnections() ) {
		return;
	}
	if ( zone == 0 ) {
		m_compiledScript.append( TQString("\n# Settup Inncomming Connection access rules\n") );
		zone = doc->incomingZone();
	}
	
	TQPtrList<KMFNetZone>& children = zone->zones();
	TQPtrListIterator<KMFNetZone> it( children );
	while( it.current() ) {
		createIncommingACCESSRules(  it.current(), doc );
		++it;
	}
	
	TQPtrList<KMFTarget>& zoneHosts = zone->hosts();
	TQPtrListIterator<KMFTarget> itZoneHosts ( zoneHosts );
	while ( itZoneHosts.current() ) {
		KMFNetHost* host = dynamic_cast<KMFNetHost*> ( itZoneHosts.current() );
		++itZoneHosts;
		m_compiledScript.append( TQString("\n# Settup rules for host %1 [%2]\n").arg( host->guiName() ).arg( zone->address()->toString() ) );
		
		TQPtrList<KMFProtocolUsage>& hostProts = host->protocols();
		TQPtrListIterator<KMFProtocolUsage> itHostProtocols ( hostProts );
		while ( itHostProtocols.current() ) {
			KMFProtocolUsage* prot = itHostProtocols.current();
			++itHostProtocols;
			if ( ! host->protocolInherited( prot->protocol()->uuid() ) ) {
				m_compiledScript.append( TQString("# Settup rules for protocol %1\n").arg( prot->protocol()->name() ) );
				if ( prot->protocol()->tcpPorts().count() > 0 ) {
					m_compiledScript.append( "pass in " ); 
					if ( prot->logging() || host->logIncoming() ) {
						m_compiledScript.append( " log " );
					}
					m_compiledScript.append( " quick inet proto tcp from " );
					m_compiledScript.append( zone->address()->toString() );
					m_compiledScript.append( "/32 " );
					m_compiledScript.append( " to any " );
					m_compiledScript.append( " port " );
					m_compiledScript.append( getPortList( prot->protocol()->tcpPorts() ) );
					m_compiledScript.append( " synproxy state\n" );
				}
				if ( prot->protocol()->udpPorts().count() > 0 ) {
					m_compiledScript.append( "pass in " ); 
					if ( prot->logging() || host->logIncoming() ) {
						m_compiledScript.append( " log " );
					}
					m_compiledScript.append( " quick inet proto udp from " );
					m_compiledScript.append( zone->address()->toString() );
					m_compiledScript.append( "/32" );
					m_compiledScript.append( " to any " );
					m_compiledScript.append( " port " );
					m_compiledScript.append( getPortList( prot->protocol()->udpPorts() ) );
					m_compiledScript.append( " keep state\n" );
				}
			} else {
				kdDebug() << "Skipping inherited Portocol: " << prot->protocol()->name() << " in zone: " << zone->guiName() << endl;
			}
		}
	}
	
	m_compiledScript.append( TQString("\n# Settup rules for zone %1 [%2/%3]\n").arg( zone->guiName() ).arg( zone->address()->toString() ).arg( zone->maskLength() ) );
	TQPtrList<KMFProtocolUsage>& zoneProts = zone->protocols();
	TQPtrListIterator<KMFProtocolUsage> itZoneProtocols ( zoneProts );
	while ( itZoneProtocols.current() ) {
		KMFProtocolUsage* prot = itZoneProtocols.current();
		++itZoneProtocols;
		if ( ! zone->protocolInherited( prot->protocol()->uuid() ) ) {
			m_compiledScript.append( TQString("# Settup rules for protocol %1\n").arg( prot->protocol()->name() ) );
			if ( prot->protocol()->tcpPorts().count() > 0 ) {
				m_compiledScript.append( "pass in " ); 
				if ( prot->logging() ) {
					m_compiledScript.append( " log " );
				}
				m_compiledScript.append( " quick inet proto tcp from " );
				m_compiledScript.append( zone->address()->toString() );
				m_compiledScript.append( "/" );
				m_compiledScript.append( TQString::number( zone->maskLength() ) );
				m_compiledScript.append( " to any " );
				m_compiledScript.append( " port " );
				m_compiledScript.append( getPortList( prot->protocol()->tcpPorts() ) );
				m_compiledScript.append( " synproxy state\n" );
			}
			if ( prot->protocol()->udpPorts().count() > 0 ) {
				m_compiledScript.append( "pass in " ); 
				if ( prot->logging() ) {
					m_compiledScript.append( " log " );
				}
				m_compiledScript.append( " quick inet proto udp from " );
				m_compiledScript.append( zone->address()->toString() );
				m_compiledScript.append( "/" );
				m_compiledScript.append( TQString::number( zone->maskLength() ) );
				m_compiledScript.append( " to any " );
				m_compiledScript.append( " port " );
				m_compiledScript.append( getPortList( prot->protocol()->udpPorts() ) );
				m_compiledScript.append( " keep state\n" );
			}
		} else {
			kdDebug() << "Skipping inherited Portocol: " << prot->protocol()->name() << " in zone: " << zone->guiName() << endl;
		}
	}
}

void KMFPFCompiler::createOutgoingACCESSRules( KMFNetZone* zone, KMFGenericDoc* doc ) {
	kdDebug() << "void KMFPFCompiler::createOutgoingACCESSRules( KMFNetZone* zone, KMFGenericDoc* doc )" << endl;
	if ( ! doc->restrictOutgoingConnections() ) {
		return;
	}
	if ( zone == 0 ) {
		m_compiledScript.append( TQString("\n# Settup Outgoing Connection access rules\n") );
		zone = doc->outgoingZone();
	}
	
	TQPtrList<KMFNetZone>& children = zone->zones();
	TQPtrListIterator<KMFNetZone> it( children );
	while( it.current() ) {
		createIncommingACCESSRules(  it.current(), doc );
		++it;
	}
	
	TQPtrList<KMFTarget>& zoneHosts = zone->hosts();
	TQPtrListIterator<KMFTarget> itZoneHosts ( zoneHosts );
	while ( itZoneHosts.current() ) {
		KMFNetHost* host = dynamic_cast<KMFNetHost*> ( itZoneHosts.current() );
		++itZoneHosts;
		m_compiledScript.append( TQString("\n# Settup rules for host %1 [%2]\n").arg( host->guiName() ).arg( zone->address()->toString() ) );
		
		TQPtrList<KMFProtocolUsage>& hostProts = host->protocols();
		TQPtrListIterator<KMFProtocolUsage> itHostProtocols ( hostProts );
		while ( itHostProtocols.current() ) {
			KMFProtocolUsage* prot = itHostProtocols.current();
			++itHostProtocols;
			if ( ! host->protocolInherited( prot->protocol()->uuid() ) ) {
				m_compiledScript.append( TQString("# Settup rules for protocol %1\n").arg( prot->protocol()->name() ) );
				if ( prot->protocol()->tcpPorts().count() > 0 ) {
					m_compiledScript.append( "pass out " ); 
					if ( prot->logging() || host->logIncoming() ) {
						m_compiledScript.append( " log " );
					}
					m_compiledScript.append( " quick inet proto tcp from " );
					m_compiledScript.append( zone->address()->toString() );
					m_compiledScript.append( "/32 " );
					m_compiledScript.append( " to any " );
					m_compiledScript.append( " port " );
					m_compiledScript.append( getPortList( prot->protocol()->tcpPorts() ) );
					m_compiledScript.append( " synproxy state\n" );
				}
				if ( prot->protocol()->udpPorts().count() > 0 ) {
					m_compiledScript.append( "pass out " ); 
					if ( prot->logging() || host->logIncoming() ) {
						m_compiledScript.append( " log " );
					}
					m_compiledScript.append( " quick inet proto udp from " );
					m_compiledScript.append( zone->address()->toString() );
					m_compiledScript.append( "/32" );
					m_compiledScript.append( " to any " );
					m_compiledScript.append( " port " );
					m_compiledScript.append( getPortList( prot->protocol()->udpPorts() ) );
					m_compiledScript.append( " keep state\n" );
				}
			} else {
				kdDebug() << "Skipping inherited Portocol: " << prot->protocol()->name() << " in zone: " << zone->guiName() << endl;
			}
		}
	}
	
	m_compiledScript.append( TQString("\n# Settup rules for zone %1 [%2/%3]\n").arg( zone->guiName() ).arg( zone->address()->toString() ).arg( zone->maskLength() ) );
	TQPtrList<KMFProtocolUsage>& zoneProts = zone->protocols();
	TQPtrListIterator<KMFProtocolUsage> itZoneProtocols ( zoneProts );
	while ( itZoneProtocols.current() ) {
		KMFProtocolUsage* prot = itZoneProtocols.current();
		++itZoneProtocols;
		if ( ! zone->protocolInherited( prot->protocol()->uuid() ) ) {
			m_compiledScript.append( TQString("# Settup rules for protocol %1\n").arg( prot->protocol()->name() ) );
			if ( prot->protocol()->tcpPorts().count() > 0 ) {
				m_compiledScript.append( "pass out " ); 
				if ( prot->logging() ) {
					m_compiledScript.append( " log " );
				}
				m_compiledScript.append( " quick inet proto tcp from " );
				m_compiledScript.append( zone->address()->toString() );
				m_compiledScript.append( "/" );
				m_compiledScript.append( TQString::number( zone->maskLength() ) );
				m_compiledScript.append( " to any " );
				m_compiledScript.append( " port " );
				m_compiledScript.append( getPortList( prot->protocol()->tcpPorts() ) );
				m_compiledScript.append( " synproxy state\n" );
			}
			if ( prot->protocol()->udpPorts().count() > 0 ) {
				m_compiledScript.append( "pass out " ); 
				if ( prot->logging() ) {
					m_compiledScript.append( " log " );
				}
				m_compiledScript.append( " quick inet proto udp from " );
				m_compiledScript.append( zone->address()->toString() );
				m_compiledScript.append( "/" );
				m_compiledScript.append( TQString::number( zone->maskLength() ) );
				m_compiledScript.append( " to any " );
				m_compiledScript.append( " port " );
				m_compiledScript.append( getPortList( prot->protocol()->udpPorts() ) );
				m_compiledScript.append( " keep state\n" );
			}
		} else {
			kdDebug() << "Skipping inherited Portocol: " << prot->protocol()->name() << " in zone: " << zone->guiName() << endl;
		}
	}
}



TQString KMFPFCompiler::getPortList( TQValueList<int>& list ) {
	TQString ret = "{";
	TQValueList<int>::iterator ports;
	uint i = 0;
	for( ports = list.begin(); ports != list.end(); ++ports ) {
		ret.append( TQString::number( *ports ) );
		if ( i != list.count() - 1 ) {
			ret.append( ", " );
		}
		i++;
	}
	ret.append( "}" );
	return ret;
}



void KMFPFCompiler::setupPolicies( KMFGenericDoc* doc ) {
	m_compiledScript.append( "\n# Default action for unhandled packets\n" );
	m_compiledScript.append( "block in quick\n" );
	if ( doc->restrictOutgoingConnections() ) {
		m_compiledScript.append( "block out quick\n" );
	} else {
		m_compiledScript.append( "pass out quick all keep state\n" );
	}
}

void KMFPFCompiler::setupNatRules( KMFGenericDoc* doc ) {
	if ( ! doc->useNat() ) {
		return;
	}

	m_compiledScript.append( "\n# Setup NAT\n" );
	if ( doc->useMasquerade() ) {
		m_compiledScript.append( TQString("nat on %1 from any to any -> (%2)\n").arg( doc->outgoingInterface() ).arg( doc->outgoingInterface() ) );
	} else {
		m_compiledScript.append( TQString("nat on %1 from any to any -> %2\n").arg( doc->outgoingInterface() ).arg( doc->natAddress()->toString() ) );
	}
	

}

// KInstance* KMFPFCompilerFactory::s_instance = 0L;
// KAboutData* KMFPFCompilerFactory::s_about = 0L;

KMFPFCompilerFactory::KMFPFCompilerFactory( TQObject* parent, const char* name )
		: KLibFactory( parent, name ) {
// 	s_instance = new KInstance( "KMFPFCompilerFactory" );
}

TQObject* KMFPFCompilerFactory::createObject( TQObject* parent, const char* name,
        const char*, const TQStringList & ) {
	TQObject * obj = new KMFPFCompiler( parent, name );
	emit objectCreated( obj );
	return obj;
}


// KInstance* KMFPFCompilerFactory::instance() {
// 	if ( !s_instance ) {
// 		s_instance = new KInstance( "KMFPFCompilerFactory" );
// 	}
// 	return s_instance;
// }

extern "C" {
	void* init_libkmfcompiler_pf() {
		return new KMFPFCompilerFactory;
	}
}
}
#include "kmfpfcompiler.moc"
