/***************************************************************************
*   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 "networkinterface.h"
#include <iostream>
#include <qfile.h>
#include <qtextstream.h>
#include <kmessagebox.h>
#include <klocale.h>
#include <kglobal.h>
#include <kstddirs.h>
#include <qnetwork.h>
#include <qurloperator.h>
#include <kmessagebox.h>
#include <arpa/inet.h>
#include <linux/sockios.h>
#include <linux/if.h>
#include <sys/ioctl.h>
//END INCLUDES

NetworkInterface::NetworkInterface( KVpncConfig* GlobalConfig,QApplication *app, QObject *parent, const char *name ) : QObject( parent, name )
{

	this->app = app;
	interfaceTest = false;
	retrieveInterfaceAddress = false;
	QPtrList<QString>*InterfaceList = new QPtrList<QString>();
	InterfaceList->setAutoDelete( TRUE ); // the list owns the objects
	QString InterfaceIP = "";
	QString InterfaceAddress = "";
	IPforInterface = "";
	tmpInterface = "";
	interfaceExists = false;
	defaultinterface="default";
	readOutput=false;
	env = new QStringList();
	*env << "LC_ALL=C" << "LANG=C" << "PATH=/bin:/usr/bin:/usr/sbin:/sbin";
	this->GlobalConfig = GlobalConfig;
}

NetworkInterface::~NetworkInterface()
{
	//delete proc;
	if (defaultinterface == "default")
		defaultinterface == "";
}

bool NetworkInterface::interfaceExist( QString Interface )
{
	if ( !Interface.isEmpty() )
	{
		QFile NetdevFile( "/proc/net/dev" );
		QTextStream stream ( &NetdevFile );
		if ( NetdevFile.open( IO_ReadOnly ) ) {
			QString line = "";
			while ( !stream.atEnd() ) {
				line = stream.readLine(); // line of text excluding '\n' and replace all white chars with one blank
				if ( line.find( ':' ) > -1 ) {
					QString tmpdev = line.section( ':', 0, 0 ).stripWhiteSpace();
					if ( tmpdev == Interface ) {
						NetdevFile.close();
						return true;
					}
				}
			}
		}
		NetdevFile.close();
	}
	return false;
}

QStringList NetworkInterface::getAllNetworkInterfaces()
{
	QFile NetdevFile( "/proc/net/dev" );
	QTextStream stream ( &NetdevFile );
	if ( NetdevFile.open( IO_ReadOnly ) ) {
		QString line = "";
		while ( !stream.atEnd() ) {
			line = stream.readLine(); // line of text excluding '\n' and replace all white chars with one blank
			if ( line.find( ':' ) > -1 ) {
				InterfaceList.append( line.section( ':', 0, 0 ).stripWhiteSpace());
			}
		}
	}
	NetdevFile.close();
	InterfaceList.sort();
	return InterfaceList;
}

QString NetworkInterface::getInterfaceIP( QString Interface )
{

//FIXME why this dont work on ppp0 device of l2tp tunnel?
// 	if ( !Interface.isEmpty() )
// 	{
// 		int fd=-1;
// 		QString tmpip="";
// 		struct ifreq ifr;
// 		fd = socket(AF_INET, SOCK_STREAM, 0);
// 		if (fd >= 0){
// 			strcpy(ifr.ifr_name, Interface.ascii());
// 			ifr.ifr_addr.sa_family = AF_INET;
// 			if (ioctl(fd, SIOCGIFADDR, &ifr) == 0){
// 				tmpip =  inet_ntoa(((struct sockaddr_in *) &ifr. ifr_addr)->sin_addr);
// 				return tmpip;
// 			}
// 			else
// 				return QString("");
// 		}
// 		else
// 			return QString("");
// 	}
// 	else
// 		return QString("");
	return getInterfaceIP2(Interface);
}

QString NetworkInterface::getInterfaceIP2( QString Interface )
{
        if ( !Interface.isEmpty() )
        {

                InterfaceIpProc = new QProcess(this);
                InterfaceIpProc->addArgument( GlobalConfig->pathToIp );
                InterfaceIpProc->addArgument("addr");
                InterfaceIpProc->addArgument("show");
                InterfaceIpProc->addArgument(Interface);

                retrieveInterfaceIP=true;
                readOutput=true;

                connect( InterfaceIpProc, SIGNAL( readyReadStdout() ), this, SLOT( readFromStdout_interfaceip() ) );
                connect( InterfaceIpProc, SIGNAL( readyReadStderr() ), this, SLOT( readFromStderr_interfaceip() ) );
                connect( InterfaceIpProc, SIGNAL( processExited () ), this, SLOT( processHasFinished() ) );

                if ( !InterfaceIpProc->start(env) )
                {
                        GlobalConfig->appendLogEntry(i18n("unable to start proc (%1)!").arg(i18n("getting IP address from interface")), KVpncConfig::error);
                }
                else
                {
                        while ( retrieveInterfaceIP && InterfaceIpProc->isRunning() )
                        {
                                app->processEvents();
                                usleep(500);
                        }
                        /*
                        while ( readOutput)
                                app->processEvents();*/

                        //                      disconnect( proc, SIGNAL( InterfaceIpProc() ), this, SLOT( readFromStdout() ) );
                        //                      disconnect( proc, SIGNAL( InterfaceIpProc() ), this, SLOT( readFromStderr() ) );
                        //                      disconnect( proc, SIGNAL( InterfaceIpProc () ), this, SLOT( processHasFinished() ) );
                        delete InterfaceIpProc;

                }
        }
        return InterfaceIP;

}

QString NetworkInterface::getInterfaceAddress( QString IPforInterface )
{
	/*
	// TODO fixme
	if ( !IPforInterface.isEmpty() )
	{
		this->IPforInterface = IPforInterface;
		QStringList devlist = getAllNetworkInterfaces();
		tmpfile = new KTempFile();
		QString tmpPath = locateLocal ( "data", "kvpnc/" );
		QString GetIpForInterfaceScript = tmpPath + "get_interface_for_ip_"+IPforInterface+".sh";

		QFile file ( GetIpForInterfaceScript );
		QTextStream stream( &file );
		if ( file.open( IO_WriteOnly ) )
		{
			stream << "# generated by kvpnc. Do not edit it." << "\n";
			stream << "\n";
			stream << GlobalConfig->pathToIfconfig +" | grep -B1 "+IPforInterface+" | head -n1 |awk {'print $1'} > "+ tmpfile->name()+"\n";
			file.close();



			InterfaceAddressProc = new QProcess(this);
			InterfaceAddressProc->addArgument( GlobalConfig->InterpreterShell );
			InterfaceAddressProc->addArgument(GetIpForInterfaceScript);
			retrieveInterfaceAddress=true;
			readOutput=true;

			//connect( InterfaceAddressProc, SIGNAL( readyReadStdout() ), this, SLOT( readFromStdout_interfaceaddress() ) );
			//	connect( InterfaceAddressProc, SIGNAL( readyReadStderr() ), this, SLOT( readFromStderr_interfaceaddress() ) );
			connect( InterfaceAddressProc, SIGNAL( processExited () ), this, SLOT( processHasFinished() ) );


			if ( !InterfaceAddressProc->start(env) )
			{
				GlobalConfig->appendLogEntry(i18n("unable to start proc (%1)!").arg(i18n("script for get interface from IP address")), KVpncConfig::error);
			}

			else
			{
				while (  InterfaceAddressProc->isRunning() )
				{
					usleep(250);
// 					app->processEvents();
				}

				while ( readOutput && retrieveInterfaceAddress == true )
				{
						usleep(250);
// 					app->processEvents();
				}

				// 		disconnect( InterfaceAddressProc, SIGNAL( readyReadStdout() ), this, SLOT( readFromStdout_interfaceaddress() ) );
				// 		disconnect( InterfaceAddressProc, SIGNAL( readyReadStderr() ), this, SLOT( readFromStderr_interfaceaddress() ) );
				disconnect( InterfaceAddressProc, SIGNAL( processExited () ), this, SLOT( processHasFinished() ) );
				//delete InterfaceAddressProc;
			}
		}
	}
*/
	if ( !IPforInterface.isEmpty() )
		{
			this->IPforInterface = IPforInterface;
			QStringList devlist = getAllNetworkInterfaces();
			if (!devlist.isEmpty())
			{
				for ( QStringList::Iterator it = devlist.begin(); it != devlist.end(); ++it ) {
					if (getInterfaceIP(*it) == IPforInterface)
						return QString(*it);
			}
			}
			else
				return "";
	}

	return InterfaceAddress;

}

QString NetworkInterface::getNetmaskOfInterface(QString interface)
{
	QFile NetRouteFile ( "/proc/net/route" );
	QTextStream stream ( &NetRouteFile );
	QString tmpmask;
	if ( NetRouteFile .open( IO_ReadOnly ) ) {
		QString line = "";
		while ( !stream.atEnd() ) {
			line = stream.readLine(); // line of text excluding '\n' and replace all white chars with one blank

			// FIXME netmask is guess to be ok if not 0.0.0.0 or 255.255.255.255
			if ( line.simplifyWhiteSpace().section( ' ', 1, 1 ) != "00000000" &&  line.simplifyWhiteSpace().section( ' ', 7, 7 ) != "FFFFFFFF" && line.simplifyWhiteSpace().section( ' ', 0, 0 ) == interface)
			{
				struct sockaddr_in name;
				bool ok=true;
				std::cout << "netmask of interface "<< interface.ascii() << ": " << line.simplifyWhiteSpace().section( ' ', 7, 7 ).ascii() << "\n";
				name.sin_addr.s_addr = line.simplifyWhiteSpace().section( ' ', 7, 7 ).toUInt(&ok,16);
				tmpmask = inet_ntoa(name.sin_addr); // return the value of the netmask
				NetRouteFile .close();
				return tmpmask;
			}
		}
	}
	NetRouteFile .close();

	return "0.0.0.0";
}

QString NetworkInterface::getDefaultInterface()
{
	QFile NetRouteFile ( "/proc/net/route" );
	QTextStream stream ( &NetRouteFile );
	QString tmpdev;
	if ( NetRouteFile .open( IO_ReadOnly ) ) {
		QString line = "";
		while ( !stream.atEnd() ) {
			line = stream.readLine(); // line of text excluding '\n' and replace all white chars with one blank
				QString tmptarget = line.simplifyWhiteSpace().section( ' ', 1, 1 ); // return the value of the target which is 0.0.0.0
			if ( tmptarget == "00000000" ) {
				tmpdev = line.simplifyWhiteSpace().section( ' ', 0, 0 ); // return the value of the target which is 0.0.0.0
				NetRouteFile.close();
				return tmpdev;
			}
		}
	}
	NetRouteFile.close();

	return ""; 
}

QString NetworkInterface::getGatewayOfInterface(QString interface)
{
	QFile NetRouteFile ( "/proc/net/route" );
	QTextStream stream ( &NetRouteFile );
	QString tmpgw;
	if ( NetRouteFile .open( IO_ReadOnly ) ) {
		QString line = "";
		while ( !stream.atEnd() ) {
			line = stream.readLine(); // line of text excluding '\n' and replace all white chars with one blank
			if (line.simplifyWhiteSpace().section( ' ', 0, 0 ) == interface && line.simplifyWhiteSpace().section( ' ', 1, 1 ) != "00000000")
			{
				struct sockaddr_in name;
				bool ok=true;
// 				std::cout << "gateway of interface "<< interface.ascii() << ": " << line.simplifyWhiteSpace().section( ' ', 2, 2 ).ascii() << "\n";
				name.sin_addr.s_addr = line.simplifyWhiteSpace().section( ' ', 2, 2 ).toUInt(&ok,16);
				tmpgw = inet_ntoa(name.sin_addr); // return the value of the gateway
				NetRouteFile .close();
				return tmpgw;
			}
		}
	}
	NetRouteFile .close();

	return "0.0.0.0";
}

QString NetworkInterface::getGatewayOfDefaultInterface()
{
	QFile NetRouteFile ( "/proc/net/route" );
	QTextStream stream ( &NetRouteFile );
	QString tmpgw;
	if ( NetRouteFile .open( IO_ReadOnly ) ) {
		QString line = "";
		while ( !stream.atEnd() ) {
			line = stream.readLine(); // line of text excluding '\n' and replace all white chars with one blank
			std::cout << "line: \"" << line << "\"" << "\n";
			if (line.simplifyWhiteSpace().section( ' ', 1, 1 ) == "00000000" )
			{
					struct sockaddr_in name;
					bool ok=true;
// 					std::cout << "gateway of default interface " << ": " << line.simplifyWhiteSpace().section( ' ', 2, 2 ).ascii() << "\n";
					name.sin_addr.s_addr = line.simplifyWhiteSpace().section( ' ', 2, 2 ).toUInt(&ok,16);
// 					std::cout << "s_addr: " << QString().setNum(line.simplifyWhiteSpace().section( ' ', 2, 2 ).stripWhiteSpace().toUInt(&ok,16)) << std::endl;
					tmpgw = inet_ntoa(name.sin_addr); // return the value of the gateway
					NetRouteFile .close();
// 					std::cout << "gateway of default interface (ascii)" << ": " << tmpgw<< "\n";
					return tmpgw;
			}
		}
		NetRouteFile .close();
	}
	else
	{
		std::cerr << "/proc/net/route cant be opened" << std::endl;
	}
	
	return "";
}

QString  NetworkInterface::getExternalIpAddress()
{
	qInitNetworkProtocols();
	ExternalIpAddress="";
	getExternalIpAddressRunning=true;
	http = new QHttp();
	connect (http,SIGNAL(readyRead(const QHttpResponseHeader &)), this, SLOT(externalIpDataRecieved(const QHttpResponseHeader &)));

	// FIXME how it could be better?
	http->setHost( "checkip.dyndns.org" );

	http->get
	( "/" );
	while ( getExternalIpAddressRunning )
	{
		usleep(250);
// 		app->processEvents();
	}
	delete http;
	return ExternalIpAddress;
}

bool NetworkInterface::inSameNetwork(QString ip1,QString Netmask1,QString ip2,QString Netmask2)
{
	// FIXME add valid ipv4 ip check
	// FIXME add netmask check (subnetworks!)
	if (ip1==ip2)
	{
		std::cout << "ip1 == ip2" << std::endl;
		return true;
	}
	else
	{
		std::cout << "ip1: " << ip1.section( '.', 0, 2 ) << " , ip2: " << ip2.section( '.', 0, 2 ) << std::endl;
		if (ip1.section( '.', 0, 2 ) == ip2.section( '.', 0, 2 ))
		{
			if (Netmask1 == Netmask2)
			{
				std::cout << "interface1 == interface2 and netmask1 == netmask2" << std::endl;
				return true;
			}
			else
				return false;	
		}
		else
			return false;

	}
}

void NetworkInterface::readFromStdout()
{
	while ( proc->canReadLineStdout() )
	{
		QString line = proc->readLineStdout() ;
		//		QString line = QString(proc->readStdout());


		/*
		example for one interface

		eth0      Protokoll:Ethernet  Hardware Adresse 00:10:4B:B2:19:00
		          inet Adresse:192.168.0.99  Bcast:192.168.0.255  Maske:255.255.255.0
		          inet6 Adresse: fe80::210:4bff:feb2:1900/64 Gltigkeitsbereich:Verbindung
		          UP BROADCAST NOTRAILERS RUNNING MULTICAST  MTU:1500  Metric:1
		          RX packets:31549 errors:0 dropped:0 overruns:0 frame:0
		          TX packets:34046 errors:0 dropped:0 overruns:0 carrier:0
		          Kollisionen:0 Sendewarteschlangenlï¿œge:1000
		          RX bytes:11308743 (10.7 Mb)  TX bytes:3701511 (3.5 Mb)
		          Interrupt:10 Basisadresse:0xdc00
		*/

		if ( interfaceTest )
		{
			if ( line.find( "proto", 0 , FALSE ) != -1 )
			{
				interfaceExists = true;
				interfaceTest = false;
			}
		}


	}
}

void NetworkInterface::readFromStderr()
{
	//while ( proc->canReadLineStderr() ) {
	//	QString line = proc->readLineStderr() ;
	QString line = QString( proc->readStderr() );

	if ( interfaceTest )
	{
		interfaceExists = false;
		interfaceTest = false;
	}
	//std::cerr << "dbg err: " << line << std::endl;
	//}
}

void NetworkInterface::processHasFinished()
{

// 	std::cout << "dbg: processHasFinished():" << std::endl;// << proc->exitStatus() << std::endl;;

	if (retrieveInterfaceAddress )
	{
// 		std::cout << "tmp file: " << tmpfile->name() << std::endl;
		InterfaceAddress = QString(tmpfile->file()->readAll()).stripWhiteSpace();
// 		KMessageBox::information( 0,this->defaultinterface,"default if");
		// 		tmpfile->unlink();
		retrieveInterfaceAddress=false;
	}

	if (retrieveInterfaceIP)
		retrieveInterfaceIP=false;

	readOutput=false;
}

void NetworkInterface::externalIpDataRecieved(const QHttpResponseHeader &)
{
	ExternalIpAddress=QString(http->readAll()).stripWhiteSpace().remove ("Current IP Address: ").stripWhiteSpace();
	getExternalIpAddressRunning=false;
}

void NetworkInterface::readFromStdout_interfaceip()
{
        while ( InterfaceIpProc->canReadLineStdout() )
        {
                QString line = InterfaceIpProc->readLineStdout() ;
                //              QString line = QString(InterfaceIpProc->readStdout());

                if (line.find( "inet ", 0 , FALSE ) != -1 )
                {

                        InterfaceIP = line.simplifyWhiteSpace().section(' ', 1,1).section('/',0,0); // inet 192.168.10.100/24 brd 192.168.10.255 scope global
                        retrieveInterfaceIP=false;
                }
        }
}

void NetworkInterface::readFromStderr_interfaceip()
{
        while ( InterfaceIpProc->canReadLineStderr() )
        {
                QString line = InterfaceIpProc->readLineStderr() ;
                //      QString line = QString( InterfaceIpProc->readStderr() );

                if ( interfaceTest )
                {
                        interfaceExists = false;
                        interfaceTest = false;
                }
                //std::cerr << "dbg err: " << line << std::endl;
        }
}
