/*****************************************************************************
 * Copyright (C) 2004-2009 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 package 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 package; if not, write to the Free Software               *
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA *
 *****************************************************************************/

#include "networkinterface.h"

#include <QList>
#include <QTextStream>
#include <QtCore/QFile>
#include <QtCore/QTextStream>

#include <kglobal.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <kstandarddirs.h>

#include <arpa/inet.h>
#ifdef Q_OS_LINUX
#include <linux/if.h>
#include <linux/sockios.h>
#endif
#include <sys/ioctl.h>
#include <unistd.h>
#include <iostream>

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

    this->app = app;
    interfaceTest = false;
    retrieveInterfaceAddress = false;
    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(QIODevice::ReadOnly)) {
            QStringList contentlist = QString(NetdevFile.readAll()).split("\n");
            for (int i = 0; i < contentlist.size(); ++i) {
                QString line = contentlist.at(i);
                if (line.indexOf(':') > -1) {
                    QString tmpdev = line.section(':', 0, 0).trimmed();
                    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(QIODevice::ReadOnly)) {
        QStringList contentlist = QString(NetdevFile.readAll()).split("\n");
        for (int i = 0; i < contentlist.size(); ++i) {
            QString line = contentlist.at(i);
            if (line.indexOf(':') > -1) {
                InterfaceList.append(line.section(':', 0, 0).trimmed());
            }
        }
        NetdevFile.close();
    }
    InterfaceList.sort();
    return InterfaceList;
}

QString NetworkInterface::getInterfaceIP(QString Interface)
{

// FIXME why this does not 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);
        QString proc = GlobalConfig->pathToIp;
        QStringList args;
        args.append("addr");
        args.append("show");
        args.append(Interface);

        retrieveInterfaceIP = true;
        readOutput = true;

        connect(InterfaceIpProc, SIGNAL(readyReadStandardOutput()), this, SLOT(readFromStdout_interfaceip()));
        connect(InterfaceIpProc, SIGNAL(readyReadStandardError()), this, SLOT(readFromStderr_interfaceip()));
        connect(InterfaceIpProc, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(processHasFinished(int, QProcess::ExitStatus)));
        InterfaceIpProc->setEnvironment(*env);
        InterfaceIpProc->start(proc, args);
        if (!InterfaceIpProc->waitForStarted()) {
            GlobalConfig->appendLogEntry(i18n("Unable to start proc (%1).", i18n("getting IP address from interface")), KVpncEnum::error);
        } else {
            if (GlobalConfig->KvpncDebugLevel > 3)
                GlobalConfig->appendLogEntry(i18n("\"%1\" started." , i18n("getting IP address from interface")), KVpncEnum::debug);
            InterfaceIpProc->waitForFinished();
            if (GlobalConfig->KvpncDebugLevel > 3)
                GlobalConfig->appendLogEntry(i18n("\"%1\" finished." , i18n("getting IP address from interface")), KVpncEnum::debug);
        }
        disconnect(InterfaceIpProc, SIGNAL(readyReadStandardOutput()), this, SLOT(readFromStdout_interfaceip()));
        disconnect(InterfaceIpProc, SIGNAL(readyReadStandardError()), this, SLOT(readFromStderr_interfaceip()));
        disconnect(InterfaceIpProc, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(processHasFinished(int, QProcess::ExitStatus)));
        delete InterfaceIpProc;
    }
    return InterfaceIP;

}

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

     QFile file ( GetIpForInterfaceScript );
     QTextStream stream( &file );
     if ( file.open( QIODevice::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);
    QString proc = GlobalConfig->InterpreterShell;
    QStringList args;
      args.append(GetIpForInterfaceScript);
      retrieveInterfaceAddress=true;
      readOutput=true;

    //connect( InterfaceAddressProc, SIGNAL( readyReadStandardOutput() ), this, SLOT( readFromStdout_interfaceaddress() ) );
    // connect( InterfaceAddressProc, SIGNAL( readyReadStandardError() ), this, SLOT( readFromStderr_interfaceaddress() ) );
    connect(InterfaceAddressProc, SIGNAL(finished ( int, QProcess::ExitStatus )), this, SLOT(processHasFinished(int, QProcess::ExitStatus)));


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

      else
      {
    InterfaceAddressProc->waitForFinished();

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

    //   disconnect( InterfaceAddressProc, SIGNAL( readyReadStandardOutput() ), this, SLOT( readFromStdout_interfaceaddress() ) );
    //   disconnect( InterfaceAddressProc, SIGNAL( readyReadStandardError() ), this, SLOT( readFromStderr_interfaceaddress() ) );
    disconnect(InterfaceAddressProc, SIGNAL(finished ( int, QProcess::ExitStatus )), this, SLOT(processHasFinished(int, QProcess::ExitStatus)));
       //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");
    QString tmpmask;
    if (NetRouteFile .open(QIODevice::ReadOnly)) {
        QStringList contentlist = QString(NetRouteFile.readAll()).split("\n");
        for (int i = 0; i < contentlist.size(); ++i) {
            QString line = contentlist.at(i);
            // FIXME netmask is guess to be ok if not 0.0.0.0 or 255.255.255.255
            if (line.simplified().section(' ', 1, 1) != "00000000" &&  line.simplified().section(' ', 7, 7) != "FFFFFFFF" && line.simplified().section(' ', 0, 0) == interface) {
                struct sockaddr_in name;
                bool ok = true;
                name.sin_addr.s_addr = line.simplified().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");
    QString tmpdev;
    if (NetRouteFile.open(QIODevice::ReadOnly)) {
        QStringList contentlist = QString(NetRouteFile.readAll()).split("\n");
        for (int i = 0; i < contentlist.size(); ++i) {
            QString line = contentlist.at(i);
            //std::cout << qPrintable("line: "+line) << std::endl;
            QString tmptarget = line.simplified().section(' ', 1, 1);   // return the value of the target which is 0.0.0.0
//    std::cout << qPrintable("tmptarget: "+tmptarget) << std::endl;
            if (tmptarget == "00000000") {
                tmpdev = line.simplified().section(' ', 0, 0);   // return the value of the target which is 0.0.0.0
                NetRouteFile.close();
                return tmpdev;
            }
        }
        NetRouteFile.close();
    }
    return "";
}

int NetworkInterface::getDefaultRouteCount()
{
    QFile NetRouteFile("/proc/net/route");
    int defaultroutecount = 0;
    if (NetRouteFile .open(QIODevice::ReadOnly)) {
        QStringList contentlist = QString(NetRouteFile.readAll()).split("\n");
        QString line = "";
        for (int i = 0; i < contentlist.size(); ++i) {
            QString line = contentlist.at(i);
            QString tmptarget = line.simplified().section(' ', 1, 1);   // return the value of the target which is 0.0.0.0
            if (tmptarget == "00000000")
                defaultroutecount++;
        }
        NetRouteFile.close();
    }
    return defaultroutecount;
}

QString NetworkInterface::getGatewayOfInterface(QString interface)
{
    QFile NetRouteFile("/proc/net/route");
    QString tmpgw;
    if (NetRouteFile .open(QIODevice::ReadOnly)) {
        QStringList contentlist = QString(NetRouteFile.readAll()).split("\n");
        QString line = "";
        for (int i = 0; i < contentlist.size(); ++i) {
            QString line = contentlist.at(i);
            if (line.simplified().section(' ', 0, 0) == interface && line.simplified().section(' ', 1, 1) != "00000000") {
                struct sockaddr_in name;
                bool ok = true;
                name.sin_addr.s_addr = line.simplified().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");
    QString tmpgw;
    if (NetRouteFile.open(QIODevice::ReadOnly)) {
        QStringList contentlist = QString(NetRouteFile.readAll()).split("\n");
        for (int i = 0; i < contentlist.size(); ++i) {
            QString line = contentlist.at(i);
            if (line.simplified().section(' ', 1, 1) == "00000000") {
                struct sockaddr_in name;
                bool ok = true;
                name.sin_addr.s_addr = line.simplified().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 "";
}

QString  NetworkInterface::getExternalIpAddress()
{
    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) {
        return true;
    } else {
        if (ip1.section('.', 0, 2) == ip2.section('.', 0, 2)) {
            if (Netmask1 == Netmask2) {
                return true;
            } else
                return false;
        } else
            return false;

    }
}

void NetworkInterface::readFromStdout()
{
    QStringList msg_list = QString(proc->readAllStandardOutput()).split('\n');
    for (int i = 0; i < msg_list.size(); ++i) {
        QString line = msg_list.at(i);


        /*
        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.contains(QString("proto") , Qt::CaseInsensitive)) {
                interfaceExists = true;
                interfaceTest = false;
            }
        }


    }
}

void NetworkInterface::readFromStderr()
{
    QStringList msg_list = QString(proc->readAllStandardError()).split('\n');
    for (int i = 0; i < msg_list.size(); ++i) {
        QString line = msg_list.at(i);

        if (interfaceTest) {
            interfaceExists = false;
            interfaceTest = false;
        }
    }
}

void NetworkInterface::processHasFinished(int, QProcess::ExitStatus)
{
    if (retrieveInterfaceAddress) {
        InterfaceAddress = QString(tmpfile->readAll()).trimmed();
//   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()).trimmed().remove("Current IP Address: ").trimmed();
    getExternalIpAddressRunning = false;
}

void NetworkInterface::readFromStdout_interfaceip()
{
    QStringList msg_list = QString(InterfaceIpProc->readAllStandardOutput()).split('\n');
    for (int i = 0; i < msg_list.size(); ++i) {
        QString line = msg_list.at(i);

        if (line.contains("inet ", Qt::CaseInsensitive)) {

            InterfaceIP = line.simplified().section(' ', 1, 1).section('/', 0, 0); // inet 192.168.10.100/24 brd 192.168.10.255 scope global
            retrieveInterfaceIP = false;
			// it could more than one ip addresses on a interface. we only need the first.
			break;
        }
    }
}

void NetworkInterface::readFromStderr_interfaceip()
{
    QStringList msg_list = QString(InterfaceIpProc->readAllStandardError()).split('\n');
    for (int i = 0; i < msg_list.size(); ++i) {
        QString line = msg_list.at(i);

        if (interfaceTest) {
            interfaceExists = false;
            interfaceTest = false;
        }
    }
}

#include "networkinterface.moc"
