/* This file is part of the KDE libraries
   Copyright (C) 2001-2003 George Staikos <staikos@kde.org>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License version 2 as published by the Free Software Foundation.

   This library 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
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.
*/

#include <config.h>

#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif

#include <tqfile.h>
#include <tqstring.h>
#include <tqmap.h>

#include <tdelocale.h>
#include <kdebug.h>
#include "klibloader.h"
#include <tdeconfig.h>
#include <tdeapplication.h>

#include <sys/types.h>
#include <sys/socket.h>

#include <unistd.h>

#include "ksocks.h"

// DO NOT RE-ORDER THESE.
enum SymbolKeys {
      S_SOCKSinit    =  0,
      S_connect      =  1,
      S_read         =  2,
      S_write        =  3,
      S_recvfrom     =  4,
      S_sendto       =  5,
      S_recv         =  6,
      S_send         =  7,
      S_getsockname  =  8,
      S_getpeername  =  9,
      S_accept       = 10,
      S_select       = 11,
      S_listen       = 12,
      S_bind         = 13
     };


extern "C" {
// Function pointer table
static int     (*F_SOCKSinit)   (char *) = 0L;
static int     (*F_connect)     (int, const struct sockaddr *, ksocklen_t) = 0L;
static signed long int (*F_read)        (int, void *, unsigned long int) = 0L;
static signed long int (*F_write)       (int, const void *, unsigned long int) = 0L;
static int     (*F_recvfrom)    (int, void *, unsigned long int, int, struct sockaddr *,
                                 ksocklen_t *) = 0L;
static int     (*F_sendto)      (int, const void *, unsigned long int, int,
                                 const struct sockaddr *, ksocklen_t) = 0L;
static int     (*F_recv)        (int, void *, unsigned long int, int) = 0L;
static int     (*F_send)        (int, const void *, unsigned long int, int) = 0L;
static int     (*F_getsockname) (int, struct sockaddr *, ksocklen_t *) = 0L;
static int     (*F_getpeername) (int, struct sockaddr *, ksocklen_t *) = 0L;
static int     (*F_accept)      (int, struct sockaddr *, ksocklen_t *) = 0L;
static int     (*F_select)      (int, fd_set *, fd_set *, fd_set *,
                                                     struct timeval *) = 0L;
static int     (*F_listen)      (int, int) = 0L;
static int     (*F_bind)        (int, const struct sockaddr *, ksocklen_t) = 0L;
}


class KSocksTable {
 public:
   KSocksTable();
  virtual ~KSocksTable();

   // The name of each symbol and it's SOCKS replacement
   TQMap<SymbolKeys,TQString>  symbols;
   // The name of this library
   TQString                   myname;
   bool                      hasWorkingAsyncConnect;
};


KSocksTable::KSocksTable() : myname("Unknown"), hasWorkingAsyncConnect(true) {
}

KSocksTable::~KSocksTable() {
}


/*
 *   How to add support for a new SOCKS package.
 *
 *   1) Subclass KSocksTable as is done below and write out all the symbols
 *   1.b) Give the class a "myname"
 *   2) Make sure that all possible library names are written into the
 *      _libNames string list.  Don't forget that different OSes name shared
 *      libraries differently.  Expect .so, .sl, .a (!) (AIX does this).
 *   3) Find a unique symbol in the library that we can use to identify that
 *      library and write out the test case in the constructor
 *   4) Make necessary changes to the KControl module in tdebase/kcontrol/....
 *   5) TEST!
 *
 */

//////////////////////////////////////////////////////////////////
///////        Define each library symbol table here       ///////
//////////////////////////////////////////////////////////////////


//
//    Support for NEC SOCKS client
//

class KNECSocksTable : public KSocksTable {
  public:
    KNECSocksTable();
    virtual ~KNECSocksTable();
};


KNECSocksTable::KNECSocksTable() : KSocksTable() {
  myname = i18n("NEC SOCKS client");
  symbols.insert(S_SOCKSinit,   "SOCKSinit");
  symbols.insert(S_connect,     "connect");
  symbols.insert(S_read,        "read");
  symbols.insert(S_write,       "write");
  symbols.insert(S_recvfrom,    "recvfrom");
  symbols.insert(S_sendto,      "sendto");
  symbols.insert(S_recv,        "recv");
  symbols.insert(S_send,        "send");
  symbols.insert(S_getsockname, "getsockname");
  symbols.insert(S_getpeername, "getpeername");
  symbols.insert(S_accept,      "accept");
  symbols.insert(S_select,      "select");
  symbols.insert(S_listen,      "listen");
  symbols.insert(S_bind,        "bind");
}

KNECSocksTable::~KNECSocksTable() {
}




//
//    Support for Dante SOCKS client
//

class KDanteSocksTable : public KSocksTable {
  public:
    KDanteSocksTable();
    virtual ~KDanteSocksTable();
};

KDanteSocksTable::KDanteSocksTable() : KSocksTable() {
  hasWorkingAsyncConnect = false;
  myname = i18n("Dante SOCKS client");
  symbols.insert(S_SOCKSinit,   "SOCKSinit");
  symbols.insert(S_connect,     "Rconnect");
  symbols.insert(S_read,        "Rread");
  symbols.insert(S_write,       "Rwrite");
  symbols.insert(S_recvfrom,    "Rrecvfrom");
  symbols.insert(S_sendto,      "Rsendto");
  symbols.insert(S_recv,        "Rrecv");
  symbols.insert(S_send,        "Rsend");
  symbols.insert(S_getsockname, "Rgetsockname");
  symbols.insert(S_getpeername, "Rgetpeername");
  symbols.insert(S_accept,      "Raccept");
  symbols.insert(S_select,      "Rselect");
  symbols.insert(S_listen,      "Rlisten");
  symbols.insert(S_bind,        "Rbind");
}


KDanteSocksTable::~KDanteSocksTable() {
}



//////////////////////////////////////////////////////////////////
///////        End of all symbol table definitions         ///////
//////////////////////////////////////////////////////////////////


KSocks *KSocks::_me = 0;
#ifdef __CYGWIN__
bool KSocks::_disabled = true;
#else 
bool KSocks::_disabled = false;
#endif 
static KStaticDeleter<KSocks> med;

void KSocks::disable()
{
   if (!_me)
      _disabled = true;
}

KSocks *KSocks::self() {
  // Note that we don't use a static deleter here. It makes no sense and tends to cause crashes.
  if (!_me) {
     if (kapp) {
        TDEConfigGroup cfg(kapp->config(), "Socks");
        _me = new KSocks(&cfg);
     } else {
        _disabled = true;
        _me = new KSocks(0);
     }
  }
  return _me;
}

void KSocks::setConfig(TDEConfigBase *config)
{
  // We can change the config from disabled to enabled
  // but not the other way around.
  if (_me && _disabled) {
     delete _me;
     _me = 0;
     _disabled = false;
  }
  if (!_me)
    _me = new KSocks(config);
}

bool KSocks::activated() { return (_me != 0L); }


KSocks::KSocks(TDEConfigBase *config) : _socksLib(0L), _st(0L) {
   _hasSocks = false;
   _useSocks = false;

   if (!config)
      return;

   if (!(config->readBoolEntry("SOCKS_enable", false))) {
      _disabled = true;
   }

   if (_disabled)
      return;

   _libPaths << ""
	     << "/usr/lib" KDELIBSUFF "/"
             << "/usr/lib/"
	     << "/usr/local/lib" KDELIBSUFF "/"
             << "/usr/local/lib/"
	     << "/usr/local/socks5/lib" KDELIBSUFF "/"
             << "/usr/local/socks5/lib/"
	     << "/opt/socks5/lib" KDELIBSUFF "/"
             << "/opt/socks5/lib/";
   _libNames << "libsocks.so"                  // Dante
             << "libdsocksd.so.0"              // Dante 1.1.14-2 on
                                               // Debian unstable 17-12-2003
             << "libsocks5.so"                 // ?
             << "libsocks5_sh.so";             // NEC

   // Add the custom library paths here
   TQStringList newlibs = config->readListEntry("SOCKS_lib_path");

   for (TQStringList::Iterator it = newlibs.begin();
                              it != newlibs.end();
                              ++it) {
      TQString thisone = *it;
      if (thisone[thisone.length()-1] != '/') thisone += "/";
      _libPaths << thisone;
      kdDebug(171) << "KSocks added a new library path: " << thisone << endl;
   }

   // Load the proper libsocks and KSocksTable
   KLibLoader *ll = KLibLoader::self();


   int _meth = config->readNumEntry("SOCKS_method", 1);
         /****       Current methods
          *   1) Autodetect (read: any)     2) NEC
          *   3) Dante                      4) Custom
          */

   if (_meth == 4) {         // try to load^H^H^H^Hguess at a custom library
      _socksLib = ll->library(config->readPathEntry("SOCKS_lib").latin1());
      if (_socksLib && _socksLib->symbol("Rconnect")) {  // Dante compatible?
         _st = new KDanteSocksTable;
         _useSocks = true;
         _hasSocks = true;
      } else if (_socksLib && _socksLib->symbol("connect")) { // NEC compatible?
         _st = new KNECSocksTable;
         _useSocks = true;
         _hasSocks = true;
      } else if (_socksLib) {
         _socksLib->unload();
         _socksLib = 0L;
      }
   } else              // leave this here   "else for {}"
   for (TQStringList::Iterator pit  = _libPaths.begin();
                              !_hasSocks && pit != _libPaths.end();
                              ++pit)
   for (TQStringList::Iterator it  = _libNames.begin();
                              it != _libNames.end();
                              ++it) {
      _socksLib = ll->library((*pit + *it).latin1());
      if (_socksLib) {
         if ((_meth == 1 || _meth == 2) &&
             _socksLib->symbol("S5LogShowThreadIDS") != 0L) {  // NEC SOCKS
            kdDebug(171) << "Found NEC SOCKS" << endl;
            _st = new KNECSocksTable;
            _useSocks = true;
            _hasSocks = true;
            break;
         } else if ((_meth == 1 || _meth == 3) &&
                    _socksLib->symbol("sockaddr2ruleaddress") != 0L) { //Dante
            kdDebug(171) << "Found Dante SOCKS" << endl;
            _st = new KDanteSocksTable;
            _useSocks = true;
            _hasSocks = true;
            break;
         } else {
           _socksLib->unload();
           _socksLib = 0L;
         }
      }
   }

   // Load in all the symbols
   if (_st) {
      for (TQMap<SymbolKeys,TQString>::Iterator it  = _st->symbols.begin();
                                              it != _st->symbols.end();
                                              ++it) {
         switch(it.key()) {
         case S_SOCKSinit:
           F_SOCKSinit = (int (*)(char *))
                         _socksLib->symbol(it.data().latin1());
          break;
         case S_connect:
           F_connect = (int (*)(int, const struct sockaddr *, ksocklen_t))
                       _socksLib->symbol(it.data().latin1());
          break;
         case S_read:
           F_read = (signed long int (*)(int, void *, unsigned long int))
                    _socksLib->symbol(it.data().latin1());
          break;
         case S_write:
           F_write = (signed long int (*)(int, const void *, unsigned long int))
                     _socksLib->symbol(it.data().latin1());
          break;
         case S_recvfrom:
           F_recvfrom = (int (*)(int, void *, unsigned long int, int,
                                 struct sockaddr *, ksocklen_t *))
                        _socksLib->symbol(it.data().latin1());
          break;
         case S_sendto:
           F_sendto = (int (*)(int, const void *, unsigned long int, int,
                               const struct sockaddr *, ksocklen_t))
                      _socksLib->symbol(it.data().latin1());
          break;
         case S_recv:
           F_recv = (int (*)(int, void *, unsigned long int, int))
                    _socksLib->symbol(it.data().latin1());
          break;
         case S_send:
           F_send = (int (*)(int, const void *, unsigned long int, int))
                    _socksLib->symbol(it.data().latin1());
          break;
         case S_getsockname:
           F_getsockname = (int (*)(int, struct sockaddr *, ksocklen_t *))
                           _socksLib->symbol(it.data().latin1());
          break;
         case S_getpeername:
           F_getpeername = (int (*)(int, struct sockaddr *, ksocklen_t *))
                           _socksLib->symbol(it.data().latin1());
          break;
         case S_accept:
           F_accept = (int (*)(int, struct sockaddr *, ksocklen_t *))
                      _socksLib->symbol(it.data().latin1());
          break;
         case S_select:
           F_select = (int (*)(int, fd_set *, fd_set *, fd_set *, struct timeval *))
                      _socksLib->symbol(it.data().latin1());
          break;
         case S_listen:
           F_listen = (int (*)(int, int))
                      _socksLib->symbol(it.data().latin1());
          break;
         case S_bind:
           F_bind = (int (*)(int, const struct sockaddr *, ksocklen_t))
                    _socksLib->symbol(it.data().latin1());
          break;
         default:
          kdDebug(171) << "KSocks got a symbol it doesn't know about!" << endl;
          break;
         }
      }

      // Now we check for the critical stuff.
      if (F_SOCKSinit) {
         int rc = (*F_SOCKSinit)((char *)"KDE");
         if (rc != 0)
            stopSocks();
         else kdDebug(171) << "SOCKS has been activated!" << endl;
      } else {
         stopSocks();
      }
   }
}


KSocks::~KSocks() {
   stopSocks();
   _me = 0;
}

void KSocks::die() {
   if (_me == this) {
      _me = 0;
      delete this;
   }
}

void KSocks::stopSocks() {
   if (_hasSocks) {
      // This library doesn't even provide the basics.
      // It's probably broken.  Let's abort.
      _useSocks = false;
      _hasSocks = false;
      if (_socksLib) {
         _socksLib->unload();
         _socksLib = 0L;
      }
      delete _st;
      _st = 0L;
   }
}


bool KSocks::usingSocks() {
   return _useSocks;
}


bool KSocks::hasSocks() {
   return _hasSocks;
}


void KSocks::disableSocks() {
   _useSocks = false;
}


void KSocks::enableSocks() {
   if (_hasSocks)
      _useSocks = true;
}

bool KSocks::hasWorkingAsyncConnect()
{
   return (_useSocks && _st) ? _st->hasWorkingAsyncConnect : true;
}


/*
 *   REIMPLEMENTED FUNCTIONS FROM LIBC
 *
 */

int KSocks::connect (int sockfd, const sockaddr *serv_addr,
                                                   ksocklen_t addrlen) {
   if (_useSocks && F_connect)
      return (*F_connect)(sockfd, serv_addr, addrlen);
   else return ::connect(sockfd, (sockaddr*) serv_addr, (socklen_t)addrlen);
}


signed long int KSocks::read (int fd, void *buf, unsigned long int count) {
   if (_useSocks && F_read)
      return (*F_read)(fd, buf, count);
   else return ::read(fd, buf, count);
}


signed long int KSocks::write (int fd, const void *buf, unsigned long int count) {
   if (_useSocks && F_write)
      return (*F_write)(fd, buf, count);
   else return ::write(fd, buf, count);
}


int KSocks::recvfrom (int s, void *buf, unsigned long int len, int flags,
                                sockaddr *from, ksocklen_t *fromlen) {
   if (_useSocks && F_recvfrom) {
      return (*F_recvfrom)(s, buf, len, flags, from, fromlen);
   } else {
      socklen_t casted_len = (socklen_t) *fromlen;
      int rc = ::recvfrom(s, (char*) buf, len, flags, from, &casted_len);
      *fromlen = casted_len;
      return rc;
   }
}


int KSocks::sendto (int s, const void *msg, unsigned long int len, int flags,
                             const sockaddr *to, ksocklen_t tolen) {
   if (_useSocks && F_sendto)
      return (*F_sendto)(s, msg, len, flags, to, tolen);
   else return ::sendto(s, (char*) msg, len, flags, to, (socklen_t)tolen);
}


int KSocks::recv (int s, void *buf, unsigned long int len, int flags) {
   if (_useSocks && F_recv)
      return (*F_recv)(s, buf, len, flags);
   else return ::recv(s, (char*) buf, len, flags);
}


int KSocks::send (int s, const void *msg, unsigned long int len, int flags) {
   if (_useSocks && F_send)
      return (*F_send)(s, msg, len, flags);
   else return ::send(s, (char*) msg, len, flags);
}


int KSocks::getsockname (int s, sockaddr *name, ksocklen_t *namelen) {
   if (_useSocks && F_getsockname) {
      return (*F_getsockname)(s, name, namelen);
   } else {
     socklen_t casted_len = *namelen;
     int rc = ::getsockname(s, name, &casted_len);
     *namelen = casted_len;
     return rc;
   }
}


int KSocks::getpeername (int s, sockaddr *name, ksocklen_t *namelen) {
   if (_useSocks && F_getpeername) {
      return (*F_getpeername)(s, name, namelen);
   } else {
      socklen_t casted_len = *namelen;
      int rc = ::getpeername(s, name, &casted_len);
      *namelen = casted_len;
      return rc;
   }
}


int KSocks::accept (int s, sockaddr *addr, ksocklen_t *addrlen) {
   if (_useSocks && F_accept) {
     return (*F_accept)(s, addr, addrlen);
   } else {
      socklen_t casted_len = *addrlen;
      int rc = ::accept(s, addr, &casted_len);
      *addrlen = casted_len;
      return rc;
   }
}


int KSocks::select (int n, fd_set *readfds, fd_set *writefds,
                                fd_set *exceptfds, struct timeval *timeout) {
   if (_useSocks && F_select)
      return (*F_select)(n, readfds, writefds, exceptfds, timeout);
   else return ::select(n, readfds, writefds, exceptfds, timeout);
}


int KSocks::listen (int s, int backlog) {
   if (_useSocks && F_listen)
      return (*F_listen)(s, backlog);
   else return ::listen(s, backlog);
}


int KSocks::bind (int sockfd, const sockaddr *my_addr, ksocklen_t addrlen) {
   if (_useSocks && F_bind)
      return (*F_bind)(sockfd, my_addr, addrlen);
   else return ::bind(sockfd, my_addr, (socklen_t)addrlen);
}

int KSocks::bind (int sockfd, sockaddr *my_addr, ksocklen_t addrlen) {
   if (_useSocks && F_bind)
      return (*F_bind)(sockfd, my_addr, addrlen);
   else return ::bind(sockfd, my_addr, (socklen_t)addrlen);
}