/***************************************************************************
 *   Copyright (C) 2004 by Christoph Thielecke                             *
 *   crissi99@gmx.de                                                       *
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
//BEGIN INCLUDES
#include "utils.h"
#include <qstring.h>
#include <qapplication.h>
#include <iostream>
#include <qfile.h>
#include <qprocess.h>
#include <qstring.h>
#include <klocale.h>
#include <ksimpleconfig.h>
#include <qdir.h>
#include <kstddirs.h>
#include <kdebug.h>
#include <kstandarddirs.h>
#include <qdns.h>
#include <qregexp.h>
//END INCLUDES

Utils::Utils(KVpncConfig* config,QObject *parent, const char *name)
		: QObject(parent, name)
{
	env = new QStringList();
	*env << "LC_ALL=C" << "LANG=C";
	this->config = config;
	retrieveValidNetworkdevice=false;
}

Utils::~Utils()
{
	// 	if(createProcess!=0)
	// 		delete createProcess;
	//
	// 	if (NetworkDeviceTestProcess!=0)
	// 		delete NetworkDeviceTestProcess;
}

bool Utils::isValidIPv4Address(QString Address)
{
	if ( Address.contains( '.' ) != 3 )
		return false;
	else
	{
		//std::cout << "test1 succeed.\n";
		QString addr = Address;
		int part0 = addr.section( '.', 0, 0 ).toInt();
		int part1 = addr.section( '.', 1, 1 ).toInt();
		int part2 = addr.section( '.', 2, 2 ).toInt();
		int part3 = addr.section( '.', 3, 3 ).toInt();

		//std::cout << "part0 " << part0 << ", part1 " << part1 <<  ", part2 " << part2 << ", part3 " << part3 << "\n";

		if ( ( part0 > 1 && part0 < 255 ) &&
		        ( part1 >= 0 && part1 < 255 ) &&
		        ( part2 >= 0 && part2 < 255 ) &&
		        ( part3 >= 0 && part3 < 255 ) )
			return true;
		else
			return false;
	}
}

bool Utils::isValidIPv4NetworkAddress(QString Address)
{
	if (isValidIPv4Address(Address))
	{
		if (Address.section('.',3,3).toInt() == 0)
			return true;
		else
			return false;
	}
	else
		return false;
}

bool Utils::isValidIPv4BroadcastAddress(QString Address)
{
	if (isValidIPv4Address(Address))
	{
		if (Address.section('.',3,3).toInt() == 255)
			return true;
		else
			return false;
	}
	else
		return false;
	return false;
}

bool Utils::tunDevExists()
{
	if (QFile ("/dev/net/tun").exists())
		return true;
	else
		return false;
}

bool Utils::createTunDev()
{
	createProcess = new QProcess(this);
	connect( createProcess, SIGNAL( readyReadStdout() ), this, SLOT( readStdOutCreateTunDev() ) );
	connect( createProcess, SIGNAL( readyReadStderr() ), this, SLOT( readStdErrCreateTunDev() ) );

	// step one: create directory
	if (!QDir ("/dev/net").exists())
	{
		createProcess->addArgument("mkdir");
		//createProcess->addArgument("-p");
		createProcess->addArgument("/dev/net");


		if ( !createProcess->start() )
		{
			kdError() << "Unable to create tunnel device file!" << endl;
			return false;
		}
		else
		{
			// 		while(createProcess->isRunning())
			// 		{ };
			sleep (2);
		}
	}

	// step two: create device node
	createProcess->clearArguments();
	createProcess->addArgument("/bin/mknod");
	createProcess->addArgument("/dev/net/tun");
	createProcess->addArgument("c");
	createProcess->addArgument("10");
	createProcess->addArgument("200");

	if ( !createProcess->start() )
	{
		kdError() << "Unable to create tunnel device file!" << endl;
		return false;
	}
	else
		return true;

	return false;
}

bool Utils::isValidNetworkDevice(QString NetworkDevice)
{
	if ( !NetworkDevice.isEmpty())
	{
		// /sbin/ifconfig | grep "eth0 " | wc -l
		validNetworkDevice=false;

		tmpfile = new KTempFile();
		QString tmpPath = locateLocal ( "data", "kvpnc/" );
		QString GetValidNetworkDeviceScript = tmpPath + "get_valid_network_device_script_"+NetworkDevice+".sh";

		QFile file ( GetValidNetworkDeviceScript );
		QTextStream stream( &file );
		if ( file.open( IO_WriteOnly ) )
		{
			stream << "# generated by kvpnc. Do not edit it." << "\n";
			stream << "\n";
			stream << config->pathToIfconfig +" | grep "+NetworkDevice+ " | wc -l > "+ tmpfile->name()+"\n";
			file.close();
		}

		NetworkDeviceTestProcess = new QProcess(this);
		NetworkDeviceTestProcess->addArgument( "/bin/sh" );
		NetworkDeviceTestProcess->addArgument( GetValidNetworkDeviceScript );

		// 		connect( NetworkDeviceTestProcess, SIGNAL( readyReadStdout() ), this, SLOT( readStdOutNetworkDeviceTest() ) );
		// 		connect( NetworkDeviceTestProcess, SIGNAL( readyReadStderr() ), this, SLOT( readStdErrNetworkDeviceTest() ) );
		connect( NetworkDeviceTestProcess, SIGNAL( processExited () ), this, SLOT( processHasFinished() ) );

		if ( !NetworkDeviceTestProcess->start(env) )
		{
			kdError() << "Unable to run network device test!" << endl;
			return false;
		}
		else
		{
			while ( retrieveValidNetworkdevice == true && NetworkDeviceTestProcess->isRunning() )
				config->appPointer->processEvents();


			// 		disconnect( NetworkDeviceTestProcess, SIGNAL( readyReadStdout() ), this, SLOT( readStdoutNetworkDeviceTest() ) );
			// 		disconnect( NetworkDeviceTestProcess, SIGNAL( readyReadStderr() ), this, SLOT( readStdErrNetworkDeviceTest() ) );
			disconnect( NetworkDeviceTestProcess, SIGNAL( processExited () ), this, SLOT( processHasFinished() ) );
			//delete NetworkDeviceTestProcess;

			;
			return validNetworkDevice;
		}
	}
	else
		return false;
}

bool Utils::loadKernelModule(QString Name,QApplication *app)
{
	if (!Name.isEmpty())
	{
		modprobeSuccess = true;
		ModprobeProcess = new QProcess(this);
		ModprobeProcess->addArgument( "/sbin/modprobe" );
		ModprobeProcess->addArgument( Name);

		connect( ModprobeProcess, SIGNAL( readyReadStdout() ), this, SLOT( readStdOutLoadKernelModule() ) );
		connect( ModprobeProcess, SIGNAL( readyReadStderr() ), this, SLOT( readStdErrLoadKernelModule() ) );
		if ( !ModprobeProcess->start(env) )
		{
			kdError() << "Unable to start kernel module loading process!" << endl;
			return false;
		}
		else
		{
			while(ModprobeProcess->isRunning())
				app->processEvents();
			return modprobeSuccess;
		}

	}
	else
		return false;
}

bool Utils::doChmod( QString file, QString mode)
{
	QProcess *chmodProcess= new QProcess(this);
	chmodProcess->addArgument("chmod");
	chmodProcess->addArgument(mode);
	chmodProcess->addArgument(file);

	if ( !chmodProcess->start() )
	{
// KMessageBox::sorry( this, i18n( "\"%1\" start failed!" ).arg( "PppdUpScript" ) );
		config->appendLogEntry( i18n( "Chmod of %1 failed!" ).arg(file),config->error);
		return false;
	}
	else
	{
		if (config->KvpncDebugLevel > 0)
			config->appendLogEntry ( i18n( "chmod of %1 (%2) started." ).arg(file).arg(mode) ,config->debug);
		while(chmodProcess->isRunning())
			config->appPointer->QApplication::processEvents();
		//GlobalConfig->appendLogEntry ( i18n( "\"%1\" finished." ).arg("chmod"),GlobalConfig->info );
		return true;
	}
}

QPtrList<ToolInfo>* Utils::getToolList()
{
	QPtrList<ToolInfo> *ToolList=new QPtrList<ToolInfo>();

	//TODO move to KVpncConfig
	QStringList *ToolNamesList = new QStringList();
	ToolNamesList->append("vpnc");
	ToolNamesList->append("racoon");
	ToolNamesList->append("ipsec"); // freeswan
	ToolNamesList->append("pppd");
	ToolNamesList->append("pptp");
	ToolNamesList->append("setkey");
	ToolNamesList->append("iptables");
	ToolNamesList->append("openssl");
	ToolNamesList->append("openvpn");
	ToolNamesList->append("kill");
	ToolNamesList->append("killall");
	ToolNamesList->append("ping");
	ToolNamesList->append("ip");
	ToolNamesList->append("ifconfig");
	ToolNamesList->append("route");

	ToolInfo *currentTool;
	for ( QStringList::Iterator it = ToolNamesList->begin(); it != ToolNamesList->end(); it++ )
	{
		//std::cout << "tool: " << *it << std::endl;

		currentTool = new ToolInfo(*it);
		ToolList->append(currentTool);
		//currentTool=0L;
		// 			std::cout << "tool: " << currentTool->Name << std::endl;
		// 			std::cout << "Version: " << currentTool->Version << std::endl;
		// 			std::cout << "Path: " << currentTool->PathToExec << std::endl << std::endl;
	}
	ToolList->sort();

	return ToolList;

}

ToolInfo* Utils::getToolInfo(KVpncConfig *GlobalConfig,QString name)
{
	ToolInfo *Tool=0;
	for (Tool=GlobalConfig->ToolList->first();Tool;Tool=GlobalConfig->ToolList->next())
	{
		if (Tool->Name == name)
			break;
	}
	return Tool;
}

QString Utils::resolveName(QString Name,QApplication *app)
{
	resolvedIP="";
	resolveFinished=false;

	for (int i=0;i<5;i++)
	{
		resolvedIP="";
		myDns = new QDns(Name);
		connect( myDns, SIGNAL(resultsReady()),this, SLOT(dnsLookupHelper()) );
		list = myDns->addresses();
		app->processEvents();
		while(!resolveFinished)
			app->processEvents();
		if (resolvedIP != "0.0.0.0")
			break;
	}
	disconnect( myDns, SIGNAL(resultsReady()),this, SLOT(dnsLookupHelper()) );
	delete myDns;
	return resolvedIP;
}

QString Utils::removeSpecialCharsForFilename(QString filename)
{
	filename.replace(QRegExp("[*]+"),"_");
	filename.replace(QRegExp("[+] +"),"_");
	filename.replace(QRegExp("[$]+"),"_");
	filename.replace(QRegExp(":+"),"_");
	filename.replace(QRegExp("ï¿œ"),"_");
	filename.replace(QRegExp("ï¿œ"),"_");
	filename.replace(QRegExp("+"),"_");
	filename.replace(QRegExp("ï¿œ"),"_");
	filename.replace(QRegExp("ï¿œ"),"_");
	filename.replace(QRegExp("ï¿œ"),"_");
	filename.replace(QRegExp("ï¿œ"),"_");
	filename.replace(QRegExp("\\+"),"_");
	filename.replace(QRegExp("/+"),"_");
	filename.replace(QRegExp(";+"),"_");
	filename.replace(QRegExp(" "),"_");
	filename.replace(QRegExp("_+"),"_");
	return filename;
}

QStringList Utils::getOpenvpnCiphers()
{
	OpenvpnCiphers.clear();
	retrieveOpenvpnCiphers=false;

	ToolInfo *OpenvpnInfo = getToolInfo(config,"openvpn");
	QString pathToOpenvpn = OpenvpnInfo->PathToExec;

	if (pathToOpenvpn.isEmpty())
		return OpenvpnCiphers;

	OpenvpnCiphersProcess = new QProcess(this);
	OpenvpnCiphersProcess->addArgument( pathToOpenvpn );
	OpenvpnCiphersProcess->addArgument( "--show-ciphers" );

	connect( OpenvpnCiphersProcess, SIGNAL( readyReadStdout() ), this, SLOT( readStdOutRetriveOpenvpnCiphers() ) );
	connect( OpenvpnCiphersProcess, SIGNAL( readyReadStderr() ), this, SLOT( readStdErrRetriveOpenvpnCiphers() ) );

	if ( !OpenvpnCiphersProcess->start(env) )
	{
		kdError() << "Unable to fetch openvpn ciphers!" << endl;
		return false;
	}
	else
	{
		while ( OpenvpnCiphersProcess->isRunning() )
			config->appPointer->processEvents();


		disconnect( OpenvpnCiphersProcess, SIGNAL( readyReadStdout() ), this, SLOT( readStdOutRetriveOpenvpnCiphers() ) );
		disconnect( OpenvpnCiphersProcess, SIGNAL( readyReadStderr() ), this, SLOT( readStdErrRetriveOpenvpnCiphers() ) );

	}
	return OpenvpnCiphers;
}

/* === Slots === */
void Utils::readStdOutCreateTunDev()
{
	QString msg = QString( createProcess->readStderr() );
	kdDebug() << "readStdOutCreateTunDev(): " << msg << endl;
}

void Utils::readStdErrCreateTunDev()
{
	QString msg = QString( createProcess->readStderr() );
	kdError() << "readStdErrCreateTunDev" << msg << endl;

}

void Utils::readStdOutNetworkDeviceTest()
{
	QString msg = QString( NetworkDeviceTestProcess->readStdout() );
	//std::cout << "readStdErrreadStdOutNetworkDeviceTest" << msg;
	if (msg.toInt() == 1)
		validNetworkDevice=true;
}

void Utils::readStdErrNetworkDeviceTest()
{
	QString msg = QString( NetworkDeviceTestProcess->readStderr() );
	kdError() << "readStdErrreadStdErrNetworkDeviceTest: " << msg << endl;
}

void Utils::readStdOutLoadKernelModule()
{
	QString msg = QString( ModprobeProcess->readStdout() );
	kdDebug() << "readStdErrreadStdOutLoadKernelModule" << msg << endl;
}

void Utils::readStdErrLoadKernelModule()
{
	QString msg = QString( ModprobeProcess->readStderr() );
// 	kdDebug() << "readStdErrreadStderrLoadKernelModule" << msg << endl;

	/* FATAL: Module <Name> not found. */
	if ( msg.find( "not found", 0, FALSE ) > -1 )
	{
		modprobeSuccess=false;
	}

	if ( msg.find( "could not find module", 0 , FALSE ) > -1 )
	{
		modprobeSuccess = false;
	}

	if ( msg.find( "not permitted", 0 , FALSE ) > -1 )
	{
		modprobeSuccess = false;
	}
}

void Utils::readStdOutToolsTest()
{}

void Utils::readStdErrToolsTest()
{}

void Utils::dnsLookupHelper()
{
	resolvedIP = list.first().toString();
	resolveFinished=true;
}

void Utils::processHasFinished()
{
	if (retrieveValidNetworkdevice )
	{
		if ( QString(tmpfile->file()->readAll()).toInt() >= 1 )
			validNetworkDevice=true;
		else
			validNetworkDevice=false;
		tmpfile->unlink();
		retrieveValidNetworkdevice=false;
	}

}

void Utils::readStdOutRetriveOpenvpnCiphers()
{
	while (OpenvpnCiphersProcess->canReadLineStdout() ) {
		QString msg = QString( OpenvpnCiphersProcess->readLineStdout() );
		if (msg.contains("default key"))
		{
			//std::cout << msg.ascii() << std::endl;
			OpenvpnCiphers.append(msg.section(' ',0,0));
		}
	}
}

void Utils::readStdErrRetriveOpenvpnCiphers()
{
	while (OpenvpnCiphersProcess->canReadLineStderr() ) {
		QString msg = QString( OpenvpnCiphersProcess->readLineStderr() );

	}
}

#include "utils.moc"
