/***************************************************************************
 *   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.             *
 ***************************************************************************/

#include "toolinfo.h"
#include <qfile.h>
#include <iostream>
#include <klocale.h>
#include <kstandarddirs.h>
#include <kmessagebox.h>
#include <qtimer.h>
#include <qstringlist.h>

#include "toolinfo.moc"

ToolInfo::ToolInfo( const QString &Name)
{
	this->Name = Name;
	this->programsInPath = programsInPath;
	this->found = false;
	TryPath_first="";
	/*
		if (!PathToExec.isEmpty())
			SearchPathList.append(PathToExec);*/
// 	if (config->KvpncDebugLevel > 2)
// 		std::cout << "Checking for: " << Name.ascii() << std::endl;

	Version=i18n("No info");

	env = new QStringList();
	*env << "LC_ALL=C" << "LANG=C" << "PATH=/bin:/usr/bin:/usr/sbin:/sbin";

	collectRunning=false;
	collectOpenvpnCapabilitiesRunning=false;
	collectToolInfo();
}

bool ToolInfo::collectToolInfo()
{
	if(programsInPath)
	{
		addSearchPath("/sbin");
		addSearchPath("/usr/local/sbin");
		addSearchPath("/usr/sbin");
		addSearchPath("/bin");
		addSearchPath("/usr/local/bin");
		addSearchPath("/usr/bin");
		addSearchPath(QString (QString(getenv("$HOME"))+"/bin"));
	}

	bool success=false;

	QStringList::Iterator it;
	if (QFile (TryPath_first).exists() && QFileInfo(TryPath_first).isFile() )
		{
// 			if (GlobalConfig->KvpncDebugLevel > 2)
// 				std::cout << Name.ascii() <<  "found at: " << TryPath_first.ascii() << ", was first try" << std::endl;
			found=true;
			PathToExec=TryPath_first;
	}
	else
	{
// 		if (GlobalConfig->KvpncDebugLevel > 2)
// 		{
// 			std::cout << QString(i18n("Trying first %1 at %2 has been failed, trying other paths").arg(Name).arg(TryPath_first)).ascii() << std::endl;
// 			std::cout << "searchpath list: " << SearchPathList.join(" ").ascii() << std::endl;
// 		}
		for (QStringList::Iterator it = SearchPathList.begin();it != SearchPathList.end();it++)
		{
			QString TryPath=QString(*it)+"/"+Name;
// 			if (GlobalConfig->KvpncDebugLevel > 2)
// 				std::cout << QString (i18n("Trying %1 at: %2").arg(Name).arg(TryPath)).ascii() << std::endl;
	
			if (!TryPath.isEmpty()  && QFile (TryPath).exists() && QFileInfo(TryPath).isFile() )
			{
// 				if (GlobalConfig->KvpncDebugLevel > 2)
// 					std::cout << QString(i18n("%1 found at: %2").arg(Name).arg(TryPath)).ascii() << std::endl;
				found=true;
				PathToExec=TryPath;
				break;
			}
		}
	}

	if ( found)
	{

		CollectToolInfoProcess = new QProcess(this);
		CollectToolInfoProcess->clearArguments();
		connect( CollectToolInfoProcess, SIGNAL( readyReadStdout() ), this, SLOT( readStdOutCollectToolInfo() ) );
		connect( CollectToolInfoProcess, SIGNAL( readyReadStderr() ), this, SLOT( readStdErrCollectToolInfo() ) );
		//connect( CollectToolInfoProcess, SIGNAL( processExited () ), this, SLOT(processFinished()  ) );
		bool run=true;

		//FIXME how it could be better?
		CollectToolInfoProcess->addArgument(PathToExec);
		if (Name == "vpnc")
			CollectToolInfoProcess->addArgument("--version");
		else if (Name == "vpnclient") { }
		else	if (Name == "ipsec")
			CollectToolInfoProcess->addArgument("--version");
		else	if (Name == "racoon")
		{
			// we need to do some stupid hack...
			// /usr/sbin/racoon -> /usr/sbin/setkey
			CollectToolInfoProcess->clearArguments();
			CollectToolInfoProcess->addArgument(QString(PathToExec.left(PathToExec.length()-6)+"setkey"));
			CollectToolInfoProcess->addArgument("-V");
		}
		else	if (Name == "setkey")
			CollectToolInfoProcess->addArgument("-V");
		else	if (Name == "openvpn")
			CollectToolInfoProcess->addArgument("--version");
		else	if (Name == "openssl")
			CollectToolInfoProcess->addArgument("version");
		else	if (Name == "pppd")
			CollectToolInfoProcess->addArgument("--version");
		else	if (Name == "iptables")
			CollectToolInfoProcess->addArgument("-V");
		else	if (Name == "kill")
		{
			// 			run=false;
			// 			success=true;
			CollectToolInfoProcess->addArgument("-V");
		}
		else	if (Name == "killall")
			CollectToolInfoProcess->addArgument("-V");
		else	if (Name == "ping")
			CollectToolInfoProcess->addArgument("-V");
		else	if (Name == "ip")
			CollectToolInfoProcess->addArgument("-V");
		else	if (Name == "ifconfig")
			CollectToolInfoProcess->addArgument("-V");
		else	if (Name == "route")
			CollectToolInfoProcess->addArgument("-V");
		else	if (Name == "pptp")
			;
		else	if (Name == "l2tpd")
			CollectToolInfoProcess->addArgument("-D");
		else	if (Name == "xl2tpd")
			CollectToolInfoProcess->addArgument("-v");
		else if (Name =="pkcs11-tool")
			CollectToolInfoProcess->addArgument("-I");
		else
		{
// 			if (GlobalConfig->KvpncDebugLevel > 2)
// 				std::cout << "Invalid tool (" << Name.ascii() <<")!" << std::endl;
			run=false;
			success=false;
		}

		collectRunning=false;

		if (run )
		{

			if ( !PathToExec.isEmpty() && !CollectToolInfoProcess->start(env) )
			{
				std::cerr << (i18n("Unable to start collectToolInfo process (%1)!").arg(Name)).ascii() << std::endl;
				//delete CollectToolInfoProcess;
				collectRunning=false;
				success=false;
			}
			else
			{
				collectRunning=true;

				while(CollectToolInfoProcess->isRunning() && collectRunning)
				{
					if (Name == "l2tpd" || Name == "pkcs11-tool") 
					{
						usleep(500);
						CollectToolInfoProcess->kill();
					}
				}

				//KMessageBox::information(0,QString("Name: "+Name+", Path: "+PathToExec+", Version: "+Version),"aaa");
				// 		disconnect( CollectToolInfoProcess, SIGNAL( readyReadStdout() ), this, SLOT( readStdOutCollectToolInfo() ) );
				// 		disconnect( CollectToolInfoProcess, SIGNAL( readyReadStderr() ), this, SLOT( readStdErrCollectToolInfo() ) );
				// 		delete CollectToolInfoProcess;
				success=true;
			}
		}

		disconnect( CollectToolInfoProcess, SIGNAL( readyReadStdout() ), this, SLOT( readStdOutCollectToolInfo() ) );
		disconnect( CollectToolInfoProcess, SIGNAL( readyReadStderr() ), this, SLOT( readStdErrCollectToolInfo() ) );


		//std::cout << "Name: " << Name << ", Path: " << PathToExec << ", Version: " << Version << std::endl;

		if (Name=="openvpn")
			getOpenvpnCapabilities();
	}
	else
	{
// 		if (GlobalConfig->KvpncDebugLevel > 2)
// 			std::cout << QString(i18n("%1 not found.").arg(Name)).ascii() << std::endl;
	}
	return success;
}

void ToolInfo::readStdOutCollectToolInfo()
{
	while (CollectToolInfoProcess->canReadLineStdout())
	{
		QString msg = QString( CollectToolInfoProcess->readLineStdout() );
		//	QString msg = QString( CollectToolInfoProcess->readStdout() );
		// 		std::cout << "Checking for: " << Name.ascii() << std::endl;
		// 		std::cout << "stdout collectToolInfo: " << msg.ascii() <<std::endl;

		//FIXME how it could be better?
		if (Name == "vpnc")
		{
			if ( msg.find( "version", 0, FALSE ) > -1 )
			{
// 												std::cout << "stdout collectToolInfo: " << msg.ascii() << std::endl;
// 												std::cout << "stdout collectToolInfo: 2,2 " << msg.simplifyWhiteSpace().section(' ',2,2).ascii() << std::endl;
				Version = msg.simplifyWhiteSpace().section(' ',2,2);
			}
		}
		else if (Name == "vpnclient")
		{
			if ( msg.find( "Cisco Systems VPN Client", 0, FALSE ) > -1 )
				Version = msg.section(' ',5,6); // "Cisco Systems VPN Client Version 4.8.00 (0490)"
		}
		else	if (Name == "ipsec")
		{
			if ( msg.find( "wan", 0, FALSE ) > -1 )
			{
				// 				std::cout << "stdout collectToolInfo: " << msg << std::endl;
// 				if ( msg.find( "openswan", 0, FALSE ) > -1 )
// 					Version = msg.section(' ',2,2);
// 				else
					Version = msg.section(' ',1,2); // "Openswan Ux.x.x/kx.x.x"
					if (msg.section(' ',1,2).contains('/'))
						Version = msg.section(' ',1,2).section('/',0,0); // "Openswan Ux.x.x"
					else
						Version = msg.section(' ',1,2);
			}
		}
		else	if (Name == "racoon")
		{

			// we need to do some stupid hack...
			// /usr/sbin/racoon -> /usr/sbin/setkey
			CollectToolInfoProcess->addArgument(QString(PathToExec.left(PathToExec.length()-6)+"setkey"));
			if ( msg.find( "ipsec-tools", 0, FALSE ) > -1 )
			{
				//std::cout << "stdout collectToolInfo: " << msg << std::endl;
				Version = msg.section(' ',3,3);
			}
		}
		else	if (Name == "setkey")
		{
			if ( msg.find( "ipsec-tools", 0, FALSE ) > -1 )
			{
				//std::cout << "stdout collectToolInfo: " << msg << std::endl;
				Version = msg.section(' ',3,3);
			}
		}
		else	if (Name == "openvpn")
		{
			if ( msg.find( "built", 0, FALSE ) > -1 )
			{
				//std::cout << "stdout collectToolInfo: " << msg << std::endl;
				Version = msg.section(' ',1,1);
			}
		}
		else	if (Name == "openssl")
		{
			if ( msg.find( "OpenSSL", 0, FALSE ) > -1 )
			{
				//std::cout << "stdout collectToolInfo: " << msg << std::endl;
				Version = msg.section(' ',1,1);
			}
		}
		else	if (Name == "pppd")
		{
			if ( msg.find( "version", 0, FALSE ) > -1 )
			{
				//std::cout << "stdout collectToolInfo: " << msg << std::endl;
				Version = msg.section(' ',2,2);
			}
		}
		else	if (Name == "iptables")
		{
			if ( msg.find( "iptables", 0, FALSE ) > -1 )
			{
				//std::cout << "stdout collectToolInfo: " << msg << std::endl;
				Version = msg.section(' ',1,1);
			}
		}
		else	if (Name == "ping")
		{
			if ( msg.find( "ping", 0, FALSE ) > -1 )
			{
				//std::cout << "stdout collectToolInfo: " << msg << std::endl;
				Version = msg.section(' ',2,2);
			}
		}
		else	if (Name == "ip")
		{
			if ( msg.find( "ip", 0, FALSE ) > -1 )
			{
				//std::cout << "stdout collectToolInfo: " << msg << std::endl;
				Version = msg.section(' ',2,2);
			}
		}
		else	if (Name == "kill")
		{
			if ( msg.find( "kill", 0, FALSE ) > -1 )
			{
				//std::cout << "stdout collectToolInfo: " << msg << std::endl;
				Version = msg.section(' ',3,3).section(')',0,0);
			}
		}
		else	if (Name == "pkcs11-tool")
		{
			if ( msg.find( "version", 0, FALSE ) > -1 )
			{
				//std::cout << "stdout collectToolInfo: " << msg << std::endl;
				Version = msg.section(' ',2,2);
			}
			else if ( msg.find( "Error: can't open /var/run/openct/status", 0, FALSE ) > -1 )
			{
				std::cerr << "stdout collectToolInfo (pkcs11): " << msg.ascii() << std::endl;
				CollectToolInfoProcess->kill();
			}
		}
		else	if (Name == "xl2tpd")
		{
			if ( msg.find( "version:", 0, FALSE ) > -1 )
			{
// 				std::cout << "stdout collectToolInfo: " << msg << std::endl;
				Version = msg.section(' ',3,3).stripWhiteSpace();
			}
		}
		else
		{
			KMessageBox::information(0,QString("Name: "+Name+", Path: "+PathToExec+", Version: "+Version+", err: "+msg),"aaa");
		}
	}
	collectRunning=false;
}

void ToolInfo::readStdErrCollectToolInfo()
{
	while (CollectToolInfoProcess->canReadLineStderr())
	{
		QString msg = QString( CollectToolInfoProcess->readLineStderr() );
// 		if (GlobalConfig->KvpncDebugLevel > 2)
// 		{
// 			std::cout << "Checking for: " << Name.ascii() << std::endl;
// 			std::cout << "stderr collectToolInfo: " << msg.ascii() <<std::endl;
// 		}

		if (Name == "pppd")
		{
			if ( msg.find( "version", 0, FALSE ) > -1 )
			{
				//std::cout << "stdout collectToolInfo: " << msg << std::endl;
				Version = msg.section(' ',2,2);
			}
		}
		else	if (Name == "killall")
		{
			if ( msg.find( "killall", 0, FALSE ) > -1 )
			{
				//std::cout << "stdout collectToolInfo: " << msg << std::endl;
				Version = msg.section(' ',2,2);
			}
		}
		else	if (Name == "ifconfig")
		{
			//KMessageBox::information(0,QString("Name: "+Name+", Path: "+PathToExec+", Version: "+Version),"aaa");
			if ( msg.find( "ifconfig", 0, FALSE ) > -1 )
			{
				//std::cout << "stdout collectToolInfo: " << msg << std::endl;
				Version = msg.section(' ',1,1);
			}
		}
		else	if (Name == "route")
		{
			if ( msg.find( "route", 0, FALSE ) > -1 )
			{
				//std::cout << "stdout collectToolInfo: " << msg << std::endl;
				Version = msg.section(' ',1,1);
			}
		}
		else	if (Name == "pptp")
		{
			if ( msg.find( "pptp version", 0, FALSE ) > -1 )
			{
				//				std::cout << "stdout collectToolInfo: " << msg.ascii() << std::endl;
				Version = msg.section(' ',2,2).section(')',0,0);
			}
		}
		else	if (Name == "l2tpd")
		{
			if ( msg.find( "l2tpd", 0, FALSE ) > -1 )
			{
				//std::cout << "stdout collectToolInfo: " << msg << std::endl;
				Version = msg.section(' ',2,2).section(')',0,0);
			}
		}
		else	if (Name == "pkcs11-tool")
		{
			if ( msg.find( "version", 0, FALSE ) > -1 )
			{
// 				std::cout << "stdout collectToolInfo (pkcs11): " << msg.ascii() << std::endl;
				Version = msg.section(' ',2,2);
			}
			else if ( msg.find( "Error: can't open /var/run/openct/status", 0, FALSE ) > -1 )
			{
// 				std::cout << "stdout collectToolInfo (pkcs11): " << msg.ascii() << std::endl;
				CollectToolInfoProcess->kill();
			}
		}
		else
		{
// 			KMessageBox::information(0,QString("Name: "+Name+", Path: "+PathToExec+", Version: "+Version+", err: "+msg),"aaa");
		}
	}
}

void ToolInfo::readStdOutCollectOpenvpnCapabilies()
{
	while (CollectOpenvpnCapabilitiesProcess->canReadLineStdout())
	{
		QString msg = QString( CollectOpenvpnCapabilitiesProcess->readLineStdout() );
		//	QString msg = QString( CollectOpenvpnCapabilitiesProcess->readStdout() );
		// 		std::cout << "stdout collectOpenvpnCapabilities: " << msg.ascii() <<std::endl;
		if ( msg.find( "PKCS#11 Options:", 0, FALSE ) > -1 )
			Capabilities+=i18n("pkcs11 support")+";";

	}
	collectOpenvpnCapabilitiesRunning=false;
}

void ToolInfo::readStdErrCollectOpenvpnCapabilies()
{
	while (CollectOpenvpnCapabilitiesProcess->canReadLineStderr())
	{
		QString msg = QString( CollectOpenvpnCapabilitiesProcess->readLineStderr() );
		// 		std::cout << "stderr collectOpenvpnCapabilities: " << msg.ascii() <<std::endl;

	}
}

void ToolInfo::processFinished()
{
	collectRunning=false;
	collectOpenvpnCapabilitiesRunning=false;
}

void ToolInfo::addSearchPath(const QString& path )
{

	// 	std::cout << "Path added: " << path << std::endl;
	if (!path.isEmpty())
		SearchPathList.insert(SearchPathList.begin(), QString(path));
	else

		return;
}

void ToolInfo::removeSearchPath(const QString& path )
{

	// 	std::cout << "Path removed: " << path << std::endl;
	if (!path.isEmpty())
		SearchPathList.remove(QString(path));
	else

		return;
}

void ToolInfo::getOpenvpnCapabilities()
{

	if (Name=="openvpn" && !PathToExec.isEmpty())
	{

		CollectOpenvpnCapabilitiesProcess = new QProcess(this);
		CollectOpenvpnCapabilitiesProcess->clearArguments();
		connect( CollectOpenvpnCapabilitiesProcess, SIGNAL( readyReadStdout() ), this, SLOT( readStdOutCollectOpenvpnCapabilies() ) );
		connect( CollectOpenvpnCapabilitiesProcess, SIGNAL( readyReadStderr() ), this, SLOT( readStdErrCollectOpenvpnCapabilies() ) );
		connect( CollectOpenvpnCapabilitiesProcess, SIGNAL( processExited () ), this, SLOT(processFinished()  ) );

		//FIXME how it could be better?
		CollectOpenvpnCapabilitiesProcess->addArgument(PathToExec);
		if (Name == "vpnc")
			CollectOpenvpnCapabilitiesProcess->addArgument("--help");

		collectOpenvpnCapabilitiesRunning=false;

		if ( !PathToExec.isEmpty() && !CollectOpenvpnCapabilitiesProcess->start(env) )
		{
			std::cerr << i18n("Unable to start collectToolInfo process (%1)!").arg(Name).ascii() << std::endl;

			//delete CollectToolInfoProcess;
			collectOpenvpnCapabilitiesRunning=false;
		}
		else
		{
			collectOpenvpnCapabilitiesRunning=true;
			while(CollectOpenvpnCapabilitiesProcess->isRunning() && collectOpenvpnCapabilitiesRunning)
			{
				// wait...
			}

			//KMessageBox::information(0,QString("Name: "+Name+", Path: "+PathToExec+", Version: "+Version),"aaa");
			// 		disconnect( CollectOpenvpnCapabilitiesProcess, SIGNAL( readyReadStdout() ), this, SLOT( readStdOutCollectOpenvpnCapabilies() ) );
			// 		disconnect( CollectOpenvpnCapabilitiesProcess, SIGNAL( readyReadStderr() ), this, SLOT( readStdErrCollectOpenvpnCapabilies() ) );
			// 		delete CollectOpenvpnCapabilitiesProcess;
		}

		disconnect( CollectOpenvpnCapabilitiesProcess, SIGNAL( readyReadStdout() ), this, SLOT( readStdOutCollectOpenvpnCapabilies() ) );
		disconnect( CollectOpenvpnCapabilitiesProcess, SIGNAL( readyReadStderr() ), this, SLOT( readStdErrCollectOpenvpnCapabilies() ) );


		//std::cout << "Name: " << Name << ", Path: " << PathToExec << ", Version: " << Version << std::endl;
	}
}
