//
// 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 "kmfprotocol.h"

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

// KDE includes
#include <kdebug.h>
#include <tdestandarddirs.h>
#include <tdelocale.h>
#include <tdeio/netaccess.h>
#include <tdeio/job.h>
#include <ktrader.h>
#include <klibloader.h>
#include <tdetempfile.h>
#include <tdefileitem.h>

// project includes
#include "../version.h"
#include "xmlnames.h"
#include "kmfundoengine.h"
#include "kmferror.h"
#include "kmferrorhandler.h"
#include "kmfgenericdoc.h"
#include "kmfnetzone.h"
#include "kmfnethost.h"
#include "kmfprotocolcategory.h"
#include "kmfprotocolusage.h"

namespace KMF {

KMFProtocol::KMFProtocol( KMFProtocolCategory* protCat, const char* name ) : NetfilterObject( protCat, name ) {
	// kdDebug() << "KMFProtocol::KMFProtocol( NetfilterObject* parent, const char* name )" << endl;
	m_category = protCat;
	m_customProtocol = true;
	m_tcpPorts.clear();
	m_udpPorts.clear();
/*	m_udpPorts = new TQValueList<int>;
	m_tcpPorts = new TQValueList<int>;*/
}


KMFProtocol::~KMFProtocol() {
	kdDebug() << "KMFProtocol::~KMFProtocol()" << endl;
	m_tcpPorts.clear();
	m_udpPorts.clear();
	
}

int KMFProtocol::type() {
	// kdDebug() << "KMFProtocol::type()" << endl;
	return NetfilterObject::PROTOCOL;
}

void KMFProtocol::clear() {
	m_tcpPorts.clear();
	m_udpPorts.clear();
}


void KMFProtocol::setCategory( KMFProtocolCategory* protCat ) {
	
	m_category = protCat;

}

const TQString& KMFProtocol::tcpPortsList() {
	kdDebug() << "void KMFProtocol::tcpPortsList()" << endl;
	kdDebug() << "Contains: " << tcpPorts().size() << " ports"  << endl;
	TQStringList *l = new TQStringList();
	TQValueList<int>::iterator it;
    for ( it = tcpPorts().begin(); it != tcpPorts().end(); ++it ) {
		TQString s = "";
		s.setNum( *it );
		*l << s;
	}
	
	return *(new TQString( l->join(",") ) );
}

const TQString& KMFProtocol::udpPortsList() {
	kdDebug() << "void KMFProtocol::udpPortsList()" << endl;
	kdDebug() << "Contains: " << m_udpPorts.size() << " ports"  << endl;
	TQStringList *l = new TQStringList();
	TQValueList<int>::iterator it;
     for ( it = m_udpPorts.begin(); it != m_udpPorts.end(); ++it ) {
		TQString s = "";
		s.setNum( *it );
		*l << s;
	}
	return *(new TQString( l->join(",") ) );
}

void KMFProtocol::addPort( const TQString& port, int protocol ) {
	// kdDebug() << "void KMFProtocol::addPort( const TQString& )" << endl;
	if ( protocol == UDP && udpPorts().contains( port.toInt() ) == 0 ) {
		kdDebug() << " + + + Register UDP Port:" <<  port << endl;
		udpPorts().append( port.toInt() );
		qHeapSort( udpPorts() );
		changed();
		return;
	} 
	if ( protocol == TCP && tcpPorts().contains( port.toInt() ) == 0 ) {
		kdDebug() << " + + + Register TCP Port:" <<  port << endl;
		tcpPorts().append( port.toInt() );
		qHeapSort( tcpPorts() );
		changed();
		return;
	}
	kdDebug() << "WARNING: ignoring duplicate port entry: " << port << " in protocol: " << name() << endl;
	
}

void KMFProtocol::delPort( const TQString& port, int protocol  ) {
	kdDebug() << "void KMFProtocol::delPort( const TQString& )" << endl;
	if ( protocol == UDP && udpPorts().contains( port.toInt() ) > 0  ) {
		kdDebug() << "KMFProtocol: " << name() << " Unregister UDP Port:" <<  port << endl;
		udpPorts().remove( udpPorts().find( port.toInt() ) );
		qHeapSort( udpPorts() );
		changed();
	} else if ( protocol == TCP && tcpPorts().contains( port.toInt() ) > 0 ) {
		kdDebug() << "KMFProtocol: " << name() << " Unregister TCP Port:" <<  port << endl;
		tcpPorts().remove( tcpPorts().find( port.toInt() ) );
		qHeapSort( tcpPorts() );
		changed();
	}  else {
		kdDebug() << "WARNING: no entry found to remove port: " << port  << " from protocol: " << name() << endl;
	}
}

void KMFProtocol::setCustomProtocol( bool onoff ){
// 	kdDebug() << "void KMFProtocol::setCustomProtocol( bool )" << endl;
	if( onoff != m_customProtocol ) {
		m_customProtocol = onoff;
		changed();
	}
}

bool KMFProtocol::isEquivalent( KMFProtocol *other ){
	kdDebug() << "void KMFProtocol::isEquivalent( KMFProtocol& other )" << endl;
	kdDebug() << "Comparing: "  << name() << " with: " << other->name() << endl;
	if ( tcpPorts().size() != other->tcpPorts().size() ) {
		kdDebug() << "Have different TCP port count." << endl;
		return false;
	}
	
	TQValueList<int>::iterator itTcp;
	for( itTcp = tcpPorts().begin(); itTcp != tcpPorts().end(); ++itTcp ) {
		if ( other->tcpPorts().contains( *itTcp ) == 0 ) {
			kdDebug() << "TCP port " << *itTcp << " not found in other protocol." << endl;
			return false;
		}
	}
	
	if ( udpPorts().size() != other->udpPorts().size() ) {
		kdDebug() << "Have different UDP port count." << endl;
		return false;
	}
	TQValueList<int>::iterator itUdp;
	for( itUdp = udpPorts().begin(); itUdp != udpPorts().end(); ++itUdp ) {
		if ( other->udpPorts().contains( *itUdp ) == 0 ) {
			kdDebug() << "UDP port " << *itUdp << " not found in other protocol." << endl;
			return false;
		}
	}
	kdDebug() << "Protocol: "  << name() << " is Equivalent to protocol: " << other->name() << endl;
	return true;
}

bool KMFProtocol::replaceTCPPort( int oldPort, int newPort ){
	kdDebug() << "void KMFProtocol::replaceTCPPort( int " << oldPort  << ", int " << newPort << " )" << endl;
	
	if ( tcpPorts().contains( newPort ) > 0 ) {
		kdDebug() << "WARNING: ignoring duplicate port entry: " << newPort << " in protocol: " << name() << endl;
		return false;
	}
	
	int index =  tcpPorts().findIndex( oldPort );
	kdDebug() << "Found Port at: " << index << endl;
	if ( index == -1 ) {
		kdDebug() << "WARNING: port entry: " << oldPort << "not found  in protocol: " << name() << endl;
		return false;
	} 

	*tcpPorts().at( index ) = newPort;
	qHeapSort( tcpPorts() );
	changed();
	return true;
}
bool KMFProtocol::replaceUDPPort( int oldPort, int newPort ){
	kdDebug() << "void KMFProtocol::replaceUDPPort( int " << oldPort  << ", int " << newPort << " )" << endl;
	
	if ( m_udpPorts.contains( newPort ) > 0 ) {
		kdDebug() << "WARNING: ignoring duplicate port entry: " << newPort << " in protocol: " << name() << endl;
		return false;
	}
	
	int index =  udpPorts().findIndex( oldPort );
	kdDebug() << "Found Port at: " << index << endl;
	if ( index == -1 ) {
		kdDebug() << "WARNING: port entry: " << oldPort << "not found  in protocol: " << name() << endl;
		return false;
	} 
	
	*udpPorts().at( index ) = newPort;
	qHeapSort( m_udpPorts );
	changed();
	return true;
}

KMFProtocolUsage *KMFProtocol::createUsage() {
	KMFProtocolUsage *use = new KMFProtocolUsage( this, "KMFProtocolUsage" );
	use->setProtocol( this );
	m_usages.append( use );
	return use;
}

const TQDomDocument& KMFProtocol::getDOMTree() {
// 	kdDebug() << "const TQDomDocument& KMFProtocol::getDOMTree()" << endl;
	TQDomDocument doc;
	TQDomElement root = doc.createElement( XML::Protocol_Element );
	NetfilterObject::saveUuid( root );
	
	root.setAttribute(  XML::Name_Attribute, name() );
	root.setAttribute( XML::Description_Attribute, description() );
	TQValueList<int>::iterator it;
	kdDebug() << "Serializte ports: " << udpPortsList() << endl;
	for ( it = udpPorts().begin(); it != udpPorts().end(); ++it ) {
		TQDomElement port = doc.createElement( XML::Port_Element );
		root.appendChild( port );
		port.setAttribute( XML::Num_Attribute,*it);
		port.setAttribute( XML::Protocol_Attribute , XML::UDP_Value );
	}
	kdDebug() << "Serializte ports: " << tcpPortsList() << endl;
	for (  it = tcpPorts().begin(); it != tcpPorts().end(); ++it ) {
		TQDomElement port = doc.createElement( XML::Port_Element );
		root.appendChild( port );
		port.setAttribute( XML::Num_Attribute,*it);
		port.setAttribute( XML::Protocol_Attribute ,XML::TCP_Value );
	}

	doc.appendChild( root );
	return *( new TQDomDocument( doc ) );
}

void KMFProtocol::loadXML( const TQDomDocument& doc, TQStringList& errors ) {
	// kdDebug() << "void KMFProtocol::loadXML( const TQDomDocument& )" << endl;
 	TQDomElement root = doc.documentElement();
 	loadXML( root, errors );
}
void KMFProtocol::loadXML( TQDomNode root, TQStringList& errors ) {
//	kdDebug() << "void KMFProtocol::loadXML( TQDomNode root )" << endl;

	// Protocols use Fixed Guids
	NetfilterObject::loadUuid( root, errors );
	
	TQString name = "";
	TQString logging = "";
	TQString desc = "";
	TQString limit = "";
	TQString io = "";
	
	name = root.toElement().attribute( XML::Name_Attribute );
	desc = root.toElement().attribute( XML::Description_Attribute );
	
	setDescription( *( new TQString( desc ) ) );
	setName( *(new TQString( name ) ) );
	
	TQDomNode curr = root.firstChild();
	while ( !curr.isNull() ) {
		if ( curr.isElement() && curr.nodeName() == XML::Port_Element ) {
			TQString port = curr.toElement().attribute( XML::Num_Attribute );
			TQString protocol = curr.toElement().attribute( XML::Protocol_Attribute );
			if ( protocol == XML::UDP_Value ) {
				addPort( port, UDP );
			}
			if ( protocol == XML::TCP_Value ) {
				addPort( port, TCP );
			}
		}
		curr = curr.nextSibling();
	}
	changed();
}

}
