/** \file ListenSocket.h
 ** \date  2004-02-13
 ** \author grymse@alhem.net
**/
/*
Copyright (C) 2004-2010  Anders Hedstrom

This library is made available under the terms of the GNU GPL, with
the additional exemption that compiling, linking, and/or using OpenSSL
is allowed.

If you would like to use this library in a closed-source application,
a separate license agreement is available. For information about
the closed-source license agreement for the C++ sockets library,
please visit http://www.alhem.net/Sockets/license.html and/or
email license@alhem.net.

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.
*/
#ifndef _SOCKETS_ListenSocket_H
#define _SOCKETS_ListenSocket_H
#include "sockets-config.h"

#ifdef _WIN32
#include <stdlib.h>
#else
#include <errno.h>
#endif

#include "ISocketHandler.h"
#include "Socket.h"
#include "Utility.h"
#include "SctpSocket.h"
#include "Ipv4Address.h"
#include "Ipv6Address.h"
#ifdef ENABLE_EXCEPTIONS
#include "Exception.h"
#endif
#include "Lock.h"

#ifdef SOCKETS_NAMESPACE
namespace SOCKETS_NAMESPACE
{
#endif


/** Binds incoming port number to new Socket class X.
  \ingroup basic */
template <class X>
class ListenSocket : public Socket
{
public:
  /** Constructor.
    \param h ISocketHandler reference
    \param use_creator Optional use of creator (default true) */
  ListenSocket(ISocketHandler& h,bool use_creator = true) : Socket(h), m_depth(0), m_creator(NULL)
    ,m_bHasCreate(false) {
    if (use_creator) {
      m_creator = new X(h);
      Socket *tmp = m_creator -> Create();

      if (tmp && dynamic_cast<X *>(tmp)) {
        m_bHasCreate = true;
      }

      if (tmp) {
        delete tmp;
      }
    }
  }
  ~ListenSocket() {
    if (m_creator) {
      delete m_creator;
    }
  }

  /** Close file descriptor. */
  int Close() {
    if (GetSocket() != INVALID_SOCKET) {
      if (closesocket(GetSocket()) == -1) {
      } else {
        Attach(INVALID_SOCKET);
      }
    }

    return 0;
  }

  /** Bind and listen to any interface.
    \param port Port (0 is random)
    \param depth Listen queue depth */
  int Bind(port_t port,int depth = 20) {
#ifdef ENABLE_IPV6
#ifdef IPPROTO_IPV6

    if (IsIpv6()) {
      Ipv6Address ad(port);
      return Bind(ad, depth);
    } else
#endif
#endif
    {
      Ipv4Address ad(port);
      return Bind(ad, depth);
    }
  }

  int Bind(SocketAddress& ad,int depth) {
#ifdef USE_SCTP

    if (dynamic_cast<SctpSocket *>(m_creator)) {
      return Bind(ad, "sctp", depth);
    }

#endif
    return Bind(ad, "tcp", depth);
  }

  /** Bind and listen to any interface, with optional protocol.
    \param port Port (0 is random)
    \param protocol Network protocol
    \param depth Listen queue depth */
  int Bind(port_t port,const std::string& protocol,int depth = 20) {
#ifdef ENABLE_IPV6
#ifdef IPPROTO_IPV6

    if (IsIpv6()) {
      Ipv6Address ad(port);
      return Bind(ad, protocol, depth);
    } else
#endif
#endif
    {
      Ipv4Address ad(port);
      return Bind(ad, protocol, depth);
    }
  }

  /** Bind and listen to specific interface.
    \param intf Interface hostname
    \param port Port (0 is random)
    \param depth Listen queue depth */
  int Bind(const std::string& intf,port_t port,int depth = 20) {
#ifdef ENABLE_IPV6
#ifdef IPPROTO_IPV6

    if (IsIpv6()) {
      Ipv6Address ad(intf, port);

      if (ad.IsValid()) {
        return Bind(ad, depth);
      }

      Handler().LogError(this, "Bind", 0, "name resolution of interface name failed", LOG_LEVEL_FATAL);
      return -1;
    } else
#endif
#endif
    {
      Ipv4Address ad(intf, port);

      if (ad.IsValid()) {
        return Bind(ad, depth);
      }

      Handler().LogError(this, "Bind", 0, "name resolution of interface name failed", LOG_LEVEL_FATAL);
      return -1;
    }
  }

  /** Bind and listen to specific interface.
    \param intf Interface hostname
    \param port Port (0 is random)
    \param protocol Network protocol
    \param depth Listen queue depth */
  int Bind(const std::string& intf,port_t port,const std::string& protocol,int depth = 20) {
#ifdef ENABLE_IPV6
#ifdef IPPROTO_IPV6

    if (IsIpv6()) {
      Ipv6Address ad(intf, port);

      if (ad.IsValid()) {
        return Bind(ad, protocol, depth);
      }

      Handler().LogError(this, "Bind", 0, "name resolution of interface name failed", LOG_LEVEL_FATAL);
      return -1;
    } else
#endif
#endif
    {
      Ipv4Address ad(intf, port);

      if (ad.IsValid()) {
        return Bind(ad, protocol, depth);
      }

      Handler().LogError(this, "Bind", 0, "name resolution of interface name failed", LOG_LEVEL_FATAL);
      return -1;
    }
  }

  /** Bind and listen to ipv4 interface.
    \param a Ipv4 interface address
    \param port Port (0 is random)
    \param depth Listen queue depth */
  int Bind(ipaddr_t a,port_t port,int depth = 20) {
    Ipv4Address ad(a, port);
#ifdef USE_SCTP

    if (dynamic_cast<SctpSocket *>(m_creator)) {
      return Bind(ad, "sctp", depth);
    }

#endif
    return Bind(ad, "tcp", depth);
  }
  /** Bind and listen to ipv4 interface.
    \param a Ipv4 interface address
    \param port Port (0 is random)
    \param protocol Network protocol
    \param depth Listen queue depth */
  int Bind(ipaddr_t a,port_t port,const std::string& protocol,int depth) {
    Ipv4Address ad(a, port);
    return Bind(ad, protocol, depth);
  }

#ifdef ENABLE_IPV6
#ifdef IPPROTO_IPV6
  /** Bind and listen to ipv6 interface.
    \param a Ipv6 interface address
    \param port Port (0 is random)
    \param depth Listen queue depth */
  int Bind(in6_addr a,port_t port,int depth = 20) {
    Ipv6Address ad(a, port);
#ifdef USE_SCTP

    if (dynamic_cast<SctpSocket *>(m_creator)) {
      return Bind(ad, "sctp", depth);
    }

#endif
    return Bind(ad, "tcp", depth);
  }
  /** Bind and listen to ipv6 interface.
    \param a Ipv6 interface address
    \param port Port (0 is random)
    \param protocol Network protocol
    \param depth Listen queue depth */
  int Bind(in6_addr a,port_t port,const std::string& protocol,int depth) {
    Ipv6Address ad(a, port);
    return Bind(ad, protocol, depth);
  }
#endif
#endif

  /** Bind and listen to network interface.
    \param ad Interface address
    \param protocol Network protocol
    \param depth Listen queue depth */
  int Bind(SocketAddress& ad,const std::string& protocol,int depth) {
    SOCKET s;

    if ( (s = CreateSocket(ad.GetFamily(), SOCK_STREAM, protocol)) == INVALID_SOCKET) {
      return -1;
    }

    // socket must be nonblocking for async connect
    if (!SetNonblocking(true, s)) {
      closesocket(s);
      return -1;
    }

    if (bind(s, ad, ad) == -1) {
      Handler().LogError(this, "bind", Errno, StrError(Errno), LOG_LEVEL_FATAL);
      closesocket(s);
#ifdef ENABLE_EXCEPTIONS
      throw Exception("bind() failed for port " + Utility::l2string(ad.GetPort()) + ": " + StrError(Errno));
#endif
      return -1;
    }

    if (listen(s, depth) == -1) {
      Handler().LogError(this, "listen", Errno, StrError(Errno), LOG_LEVEL_FATAL);
      closesocket(s);
#ifdef ENABLE_EXCEPTIONS
      throw Exception("listen() failed for port " + Utility::l2string(ad.GetPort()) + ": " + StrError(Errno));
#endif
      return -1;
    }

    m_depth = depth;
    Attach(s);
    return 0;
  }

  /** Return assigned port number. */
  port_t GetPort() {
    return GetSockPort();
  }

  /** Return listen queue depth. */
  int GetDepth() {
    return m_depth;
  }

  /** OnRead on a ListenSocket receives an incoming connection. */
  void OnRead() {
    struct sockaddr sa;
    int max = 10; // process max 10 incoming connection in one call

    while (max--) {
      socklen_t sa_len = sizeof(struct sockaddr);
      SOCKET a_s = accept(GetSocket(), &sa, &sa_len);

      if (a_s == INVALID_SOCKET) {
        // EAGAIN or EWOULDBLOCK
#ifdef _WIN32
        if (Errno != WSAEWOULDBLOCK)
#else
        if (Errno != EWOULDBLOCK)
#endif
        {
          Handler().LogError(this, "accept", Errno, StrError(Errno), LOG_LEVEL_ERROR);
        }

        return;
      }

      if (!Handler().OkToAccept(this)) {
        Handler().LogError(this, "accept", -1, "Not OK to accept", LOG_LEVEL_WARNING);
        closesocket(a_s);
        return;
      }

      if (Handler().GetCount() >= Handler().MaxCount()) {
        Handler().LogError(this, "accept", (int)Handler().GetCount(), "ISocketHandler socket limit reached", LOG_LEVEL_FATAL);
        closesocket(a_s);
        return;
      }

      //
      Socket *tmp = NULL;

      if (Handler().IsThreaded()) {
        ISocketHandler& h = Handler().GetRandomHandler();
        tmp = new X(h); // %! no support for Create
#ifdef ENABLE_IPV6
        tmp -> SetIpv6( IsIpv6() );
#endif
        tmp -> SetParent(this);
        tmp -> Attach(a_s);
        tmp -> SetNonblocking(true);
#ifdef ENABLE_IPV6
#ifdef IPPROTO_IPV6

        if (sa_len == sizeof(struct sockaddr_in6)) {
          if (reinterpret_cast<struct sockaddr_in6&>(sa).sin6_family == AF_INET6) {
            Ipv6Address ad(reinterpret_cast<struct sockaddr_in6&>(sa).sin6_addr,
                           ntohs(reinterpret_cast<struct sockaddr_in6&>(sa).sin6_port));
            ad.SetFlowinfo(reinterpret_cast<struct sockaddr_in6&>(sa).sin6_flowinfo);
#ifndef _WIN32
            ad.SetScopeId(reinterpret_cast<struct sockaddr_in6&>(sa).sin6_scope_id);
#endif
            tmp -> SetRemoteAddress(ad);
          }
        }

#endif
#endif

        if (sa_len == sizeof(struct sockaddr_in)) {
          struct sockaddr_in *p = (struct sockaddr_in *)&sa;

          if (p -> sin_family == AF_INET) {
            Ipv4Address ad(p -> sin_addr,ntohs(p -> sin_port));
            tmp -> SetRemoteAddress(ad);
          }
        }

        tmp -> SetConnected(true);
        tmp -> Init();
        tmp -> SetDeleteByHandler(true);
        {
          Lock lock(h.GetMutex());
          h.Add(tmp);
#ifdef HAVE_OPENSSL

          if (tmp -> IsSSL()) { // SSL Enabled socket
            // %! OnSSLAccept calls SSLNegotiate that can finish in this one call.
            // %! If that happens and negotiation fails, the 'tmp' instance is
            // %! still added to the list of active sockets in the sockethandler.
            // %! See bugfix for this in SocketHandler::Select - don't Set rwx
            // %! flags if CloseAndDelete() flag is true.
            // %! An even better fugbix (see TcpSocket::OnSSLAccept) now avoids
            // %! the Add problem altogether, so ignore the above.
            // %! (OnSSLAccept does no longer call SSLNegotiate().)
            tmp -> OnSSLAccept();
          } else
#endif
          {
            tmp -> OnAccept();
          }
        }
        h.Release();
      } else {
        tmp = m_bHasCreate ? m_creator -> Create() : new X(Handler());
#ifdef ENABLE_IPV6
        tmp -> SetIpv6( IsIpv6() );
#endif
        tmp -> SetParent(this);
        tmp -> Attach(a_s);
        tmp -> SetNonblocking(true);
#ifdef ENABLE_IPV6
#ifdef IPPROTO_IPV6

        if (sa_len == sizeof(struct sockaddr_in6)) {
          if (reinterpret_cast<struct sockaddr_in6&>(sa).sin6_family == AF_INET6) {
            Ipv6Address ad(reinterpret_cast<struct sockaddr_in6&>(sa).sin6_addr,
                           ntohs(reinterpret_cast<struct sockaddr_in6&>(sa).sin6_port));
            ad.SetFlowinfo(reinterpret_cast<struct sockaddr_in6&>(sa).sin6_flowinfo);
#ifndef _WIN32
            ad.SetScopeId(reinterpret_cast<struct sockaddr_in6&>(sa).sin6_scope_id);
#endif
            tmp -> SetRemoteAddress(ad);
          }
        }

#endif
#endif

        if (sa_len == sizeof(struct sockaddr_in)) {
          struct sockaddr_in *p = (struct sockaddr_in *)&sa;

          if (p -> sin_family == AF_INET) {
            Ipv4Address ad(p -> sin_addr,ntohs(p -> sin_port));
            tmp -> SetRemoteAddress(ad);
          }
        }

        tmp -> SetConnected(true);
        tmp -> Init();
        tmp -> SetDeleteByHandler(true);
        Handler().Add(tmp);
#ifdef HAVE_OPENSSL

        if (tmp -> IsSSL()) { // SSL Enabled socket
          // %! OnSSLAccept calls SSLNegotiate that can finish in this one call.
          // %! If that happens and negotiation fails, the 'tmp' instance is
          // %! still added to the list of active sockets in the sockethandler.
          // %! See bugfix for this in SocketHandler::Select - don't Set rwx
          // %! flags if CloseAndDelete() flag is true.
          // %! An even better fugbix (see TcpSocket::OnSSLAccept) now avoids
          // %! the Add problem altogether, so ignore the above.
          // %! (OnSSLAccept does no longer call SSLNegotiate().)
          tmp -> OnSSLAccept();
        } else
#endif
        {
          tmp -> OnAccept();
        }
      }
    } // while (true)
  }

  /** Please don't use this method.
    "accept()" is handled automatically in the OnRead() method. */
  virtual SOCKET Accept(SOCKET socket, struct sockaddr *saptr, socklen_t *lenptr) {
    return accept(socket, saptr, lenptr);
  }

  bool HasCreator() {
    return m_bHasCreate;
  }

  void OnOptions(int,int,int,SOCKET) {
    SetSoReuseaddr(true);
  }

protected:
  ListenSocket(const ListenSocket& s) : Socket(s) {}
private:
  ListenSocket& operator=(const ListenSocket& ) {
    return *this;
  }
  int m_depth;
  X *m_creator;
  bool m_bHasCreate;
};



#ifdef SOCKETS_NAMESPACE
}
#endif

#endif // _SOCKETS_ListenSocket_H

