/***************************************************************************
 begin                : Tue Aug 6 2002
 copyright            : (C) 2002 by Christian Hubinger
 email                : chubinger@irrsinnig.org
***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 "kmfcheckinput.h"
// My includes
#include "kmferror.h"

// QT includes
#include <tqstring.h>
#include <tqregexp.h>
// KDE includes
#include <kdebug.h>
#include <klocale.h>

namespace KMF {

KMFCheckInput::KMFCheckInput() {
	generateMsgDict();
}

KMFCheckInput::~KMFCheckInput() {}

void KMFCheckInput::checkInput( const TQString& inp, const TQString& inp_type, KMFError* err ) {
// 	kdDebug() << "KMFCheckInput::checkInput(TQString& inp, const TQString& inp_type)" << endl;
// 	KMFError *err = new KMFError();
	TQString str_input = inp;
	if ( str_input.isEmpty() ) {
		const TQString msg = "String is Empty.";
		err->setErrMsg( msg );
		err->setErrType( KMFError::FATAL );
		return;
	}

	if ( inp_type == "IP/NETWORK/FTQHN" ) {
// 		kdDebug() << "Check for IP/NETWORK/FTQHN" << endl;
		bool isIP = checkIP( str_input );
		//		bool isFTQHN = checkFTQHN( str_input );
		bool isNETWORK = checkNetWork( str_input );
		if ( !isIP && !isNETWORK ) {
			const TQString msg = *m_msg_dict.find( "IP/NETWORK/FTQHN" );
			err->setErrMsg( msg );
			err->setErrType( KMFError::HINT );
			return;
		} else {
			const TQString msg = "";
			err->setErrMsg( msg );
			err->setErrType( KMFError::OK );
			return;
		}
	}

	if ( inp_type == "PORT" ) {
		bool isPORT = checkPORT( str_input );
		if ( !isPORT ) {
			const TQString msg = *m_msg_dict.find( "PORT" );
			err->setErrMsg( msg );
			err->setErrType( KMFError::NORMAL );
			return;
		} else {
			const TQString msg = "";
			err->setErrMsg( msg );
			err->setErrType( KMFError::OK );
			return;
		}
	}

	if ( inp_type == "MULTIPORT" ) {
		bool isMULTIPORT = checkMULTIPORT( str_input );
		if ( !isMULTIPORT ) {
			const TQString msg = *m_msg_dict.find( "MULTIPORT" );
			err->setErrMsg( msg );
			err->setErrType( KMFError::NORMAL );
			return;
		} else {
			const TQString msg = "";
			err->setErrMsg( msg );
			err->setErrType( KMFError::OK );
			return;
		}
	}

	if ( inp_type == "PORT/PORTRANGE" ) {
		bool isPORT = checkPORT( str_input );
		bool isPORTRANGE = checkPORTRANGE( str_input );
		if ( !isPORT && !isPORTRANGE ) {
			const TQString msg = *m_msg_dict.find( "PORT" );
			err->setErrMsg( msg );
			err->setErrType( KMFError::NORMAL );
			return;
		} else {
			const TQString msg = "";
			err->setErrMsg( msg );
			err->setErrType( KMFError::OK );
			return;
		}
	}

	if ( inp_type == "FTQHN" ) {
		bool isFTQHN = checkFTQHN( str_input );
		if ( !isFTQHN ) {
			const TQString msg = *m_msg_dict.find( "FTQHN" );
			err->setErrMsg( msg );
			err->setErrType( KMFError::NORMAL );
			return;
		} else {
			const TQString msg = "";
			err->setErrMsg( msg );
			err->setErrType(KMFError::OK );
			return;
		}
	}

	if ( inp_type == "IP" ) {
		bool isIP = checkIP( str_input );
		if ( !isIP ) {
			const TQString msg = *m_msg_dict.find( "IP" );
			err->setErrMsg( msg );
			err->setErrType( KMFError::NORMAL );
			return;
		} else {
			const TQString msg = "";
			err->setErrMsg( msg );
			err->setErrType( KMFError::OK );
			return;
		}
	}


	if ( inp_type == "CHAINNAME" ) {
		bool isValid = checkChainName( str_input );
		if ( !isValid ) {
			const TQString msg = *m_msg_dict.find( "CHAINNAME" );
			err->setErrMsg( msg );
			err->setErrType( KMFError::NORMAL );
			return;
		} else {
			const TQString msg = "";
			err->setErrMsg( msg );
			err->setErrType( KMFError::OK );
			return;
		}
	}

	if ( inp_type == "RULENAME" ) {
		bool isValid = checkRuleName( str_input );
		if ( !isValid ) {
			const TQString msg = *m_msg_dict.find( "RULENAME" );
			err->setErrMsg( msg );
			err->setErrType( KMFError::NORMAL );
			return;
		} else {
			const TQString msg = "";
			err->setErrMsg( msg );
			err->setErrType( KMFError::OK );
			return;
		}
	}

	if ( inp_type == "MAC" ) {
		bool isValid = checkMAC( str_input );
		if ( !isValid ) {
			const TQString msg = *m_msg_dict.find( "MAC" );
			err->setErrMsg( msg );
			err->setErrType( KMFError::NORMAL );
			return;
		} else {
			const TQString msg = "";
			err->setErrMsg( msg );
			err->setErrType( KMFError::OK );
			return;
		}
	}

	const TQString msg = "Misuse of this function.";
	err->setErrMsg( msg );
	err->setErrType( KMFError::FATAL );
	return;
}

bool KMFCheckInput::checkIP( TQString inp ) {
	TQRegExp exp( "^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}$" );
//	kdDebug() << "RegEx isValid: " << exp.isValid() << endl;
	if ( inp.contains( exp ) ) {
		int pos;
		TQString str_num;
		bool valid = true;
		while ( !inp.isEmpty() ) {
			pos = inp.find( "." );
			if ( pos > -1 ) {
				str_num = inp.left( pos );
				//				kdDebug() << "IP Num Part: " << str_num << endl;
				inp = inp.right( inp.length() - ( pos + 1 ) );
				//				kdDebug() << "Rest: " << inp << endl;
				int val = str_num.toInt();
				//				kdDebug() << "Val: " << val << endl;
				if ( val < 0 || val > 255 ) {
					valid = false;
				}
			} else {
				str_num = inp;
				//				kdDebug() << "IP Num Part: " << str_num << endl;
				inp = "";
				//				kdDebug() << "Rest: " << inp << endl;
				int val = str_num.toInt();
				//				kdDebug() << "Val: " << val << endl;
				if ( val < 0 || val > 255 ) {
					valid = false;
				}
			}
		}

		return valid;
	} else
		return false;
}

bool KMFCheckInput::checkNetMask ( TQString inp ) {
	TQRegExp exp( "^[0-9]{1,2}$" );
//	kdDebug() << "RegEx isValid: " << exp.isValid() << endl;
	if ( inp.contains( exp ) ) {
		int val = inp.toInt();
		if ( val < 0 || val > 24 ) {
			kdDebug() << "Mask must not be > 24" << endl;
			return false;
		} else {
			return true;
		}
	} else {
		return false;
	}
}

bool KMFCheckInput::checkPORT ( TQString inp ) {
	TQRegExp exp( "^[0-9]{1,5}$" );
//	kdDebug() << "RegEx isValid: " << exp.isValid() << endl;
	if ( inp.contains( exp ) ) {
		int val = inp.toInt();
		if ( val < 0 || val > 65535 ) {
			kdDebug() << "Port must not be > 65535" << endl;
			return false;
		} else {
			return true;
		}
	} else {
		return false;
	}
}

bool KMFCheckInput::checkMULTIPORT ( TQString inp ) {
	TQString tmp = inp;
	while ( !tmp.isEmpty() ) {
		TQString port = "";
		int pos = tmp.find( "," );
		if ( pos == -1 ) {
			port = tmp;
			port = port.stripWhiteSpace();
			tmp = "";
			if ( !checkPORT( port ) ) {
				return false;
			}
		} else {
			port = tmp.left( pos );
			port = port.stripWhiteSpace();
			kdDebug() << "Found for port: " << port << endl;
			tmp = tmp.right( tmp.length() - ( pos + 1 ) );
			kdDebug() << "Rest: " << tmp << endl;
			if ( !checkPORT( port ) ) {
				return false;
			}
		}
	}
	return true;
}

bool KMFCheckInput::checkPORTRANGE ( TQString inp ) {
	bool valid_1 = false;
	bool valid_2 = false;
	kdDebug() << "Checking for PORTRANGE: " << endl;
	int delimiter = inp.find( ":" );
	if ( delimiter == -1 ) {
		kdDebug() << "This is no port range" << endl;
		return false;
	} else {
		TQString port1 = inp.left( delimiter );
		TQString port2 = inp.right( inp.length() - ( delimiter + 1 ) );
		valid_1 = checkPORT ( port1 );
		valid_2 = checkPORT ( port2 );
		if ( valid_1 && valid_2 ) {
			return true;
		} else {
			return false;
		}
	}
	return false;
}

bool KMFCheckInput::checkNetWork ( TQString inp ) {
	bool valid_address = false;
	bool valid_mask = false;
	kdDebug() << "Checking for NETWORK: " << endl;
	int delimiter = inp.find( "/" );
	if ( delimiter == -1 ) {
		kdDebug() << "This is no network" << endl;
		return false;
	} else {
		TQString addr = inp.left( delimiter );
		TQString mask = inp.right( inp.length() - ( delimiter + 1 ) );
		kdDebug() << "Found address: " << addr << endl;
		kdDebug() << "Found mask: " << mask << endl;
		if ( mask.isEmpty() ) {
// 			kdDebug() << "Mask is Empty\n" << endl;
			return false;
		} else {
			valid_address = checkIP( addr );
			valid_mask = checkIP( mask );
// 			kdDebug() << "Mask address valid: " << valid_mask << endl;
			if ( !valid_mask ) {
				valid_mask = checkNetMask ( mask );
// 				kdDebug() << "Mask number valid: " << valid_mask << endl;
			}
			if ( !valid_address || !valid_mask ) {
// 				kdDebug() << "This is no valid Network" << endl;
				return false;
			} else {
// 				kdDebug() << "This is a valid Network" << endl;
				return true;
			}
		}
	}
}

bool KMFCheckInput::checkFTQHN( TQString inp ) {
	bool valid = TRUE;
	//	TQRegExp exp( "^(\\w{1,256})\\.([a-z]{2,6})$", false );
	TQRegExp exp( "^[0-9a-zA-Z_-\\.]{3,256}$", false );
	if ( !inp.contains( exp ) )
		valid = false;
	return valid;
}

bool KMFCheckInput::checkMAC( TQString inp ) {
	bool valid = TRUE;
	TQRegExp exp( "^[0-9a-fA-F]{2,2}\\:[0-9a-fA-F]{2,2}\\:[0-9a-fA-F]{2,2}\\:[0-9a-fA-F]{2,2}\\:[0-9a-fA-F]{2,2}\\:[0-9a-fA-F]{2,2}$", false );
	if ( !inp.contains( exp ) )
		valid = false;
	return valid;
}

bool KMFCheckInput::checkChainName( TQString inp ) {
	bool valid = TRUE;
	TQRegExp exp( "^[a-zA-Z0-9_]{1,29}$", false );
	if ( !inp.contains( exp ) )
		valid = false;
	return valid;
}

bool KMFCheckInput::checkRuleName( TQString inp ) {
	bool valid = TRUE;
	TQRegExp exp( "^[a-zA-Z0-9_-]{1,20}$", false );
	if ( !inp.contains( exp ) )
		valid = false;
	return valid;
}

void KMFCheckInput::generateMsgDict() {
	TQString key = "IP/NETWORK/FTQHN";
	const TQString *msg = new TQString( i18n( "<p><b>This is not a numerical IP or NETWORK therefore this can only be a HOSTNAME;</b><br>"
	                                        "there is nothing wrong with that except that you will need to "
	                                        "have a working name resolution (e.g. DNS) at firewall startup<br>"
	                                        "and hence the network needs to be up before the firewall can be started. "
	                                        "Therefore, it is highly recommended that you only use numerical IP/NETWORK addresses, "
	                                        "but it will also work that way.<br>"
	                                        "<b>Please note that it is not possible to guarantee that the hostname is valid - it is "
	                                        "your job to make sure that the hostname is right.</b>"
	                                        "<p><ul><li>An IP address has the format: [0-255].[0-255].[0-255].[0-255]</li>"
	                                        "<li>A network may look like 123.123.123.0/255.255.255.0 or 123.123.123.0/24 (these two are identical.)</li>"
	                                        "<li>A hostname looks like www.debian.org or my-host.the-net.org</li></ul>" ) );
	m_msg_dict.insert( key, msg );

	key = "IP";
	const TQString *msg2 = new TQString( i18n( "<p>This is not a valid IP address or hostname."
	                                   "<p>An IP address has the format: [0-255].[0-255].[0-255].[0-255]" ) );
	m_msg_dict.insert( key, msg2 );

	key = "FTQHN";
	const TQString *msg3 = new TQString( i18n( "<p>This is not a valid IP address or hostname."
	                                   "<p>A hostname looks like www.suse.com" ) );
	m_msg_dict.insert( key, msg3 );

	key = "CHAINNAME";
	const TQString *msg4 = new TQString( i18n( "<p>This is not a valid chain name."
	                                   "<p>Chain names <b>must not</b> contain special characters (like whitespace, ?, &, %, etc.) "
	                                   "and <b>must not</b> be longer then 29 characters" ) );
	m_msg_dict.insert( key, msg4 );

	key = "MAC";
	const TQString *msg5 = new TQString( i18n( "<p>This is not a valid MAC address."
	                                   "<p>A MAC address has six hex digits from 00-FF<br>A valid address could be: <b>02:E4:5A:90:1B:3C</b>" ) );
	m_msg_dict.insert( key, msg5 );

	key = "PORT";
	const TQString *msg6 = new TQString( i18n( "<p><b>This is not a valid numeric PORT number.</b><br>"
	                                   "Port numbers are all numbers from 1 to 65535.<br> "
	                                   "If you specified a service name (e.g. ssh or www) like they are listed "
	                                   "in <i>/etc/services</i> the rule will work, but it is highly recommended "
	                                   "that you only use numerical port numbers to avoid problems when having a broken (faked) "
	                                   "/etc/services file.<br>"
	                                   "<b>Please think twice about before you use service names, port numbers are much safer.</b>" ) );
	m_msg_dict.insert( key, msg6 );

	key = "MULTIPORT";
	const TQString *msg7 = new TQString( i18n( "<p>This is not a valid MULTIPORT string.<br>"
	                                   "A MULTIPORT strings is a comma separated list of PORT numbers.<br>"
	                                   "Please not that service names are not supported within MULTIPORT strings." ) );
	m_msg_dict.insert( key, msg7 );

	key = "RULENAME";
	const TQString *msg8 = new TQString( i18n( "<p>This is not a valid rule name."
	                                   "<p>Rule names <b>must not</b> contain special characters (like whitespace, ?, &, %, etc.) "
	                                   "and <b>must not</b> be longer then 20 characters" ) );
	m_msg_dict.insert( key, msg8 );

}

}
