/*  -*- C++ -*-
 *  Copyright (C) 2003,2005 Thiago Macieira <thiago.macieira@kdemail.net>
 *
 *
 *  Permission is hereby granted, free of charge, to any person obtaining
 *  a copy of this software and associated documentation files (the
 *  "Software"), to deal in the Software without restriction, including
 *  without limitation the rights to use, copy, modify, merge, publish,
 *  distribute, sublicense, and/or sell copies of the Software, and to
 *  permit persons to whom the Software is furnished to do so, subject to
 *  the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included 
 *  in all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/*
 * Even before our #ifdef, clean up the namespace
 */
#ifdef socket
#undef socket
#endif

#ifdef bind
#undef bind
#endif

#ifdef listen
#undef listen
#endif

#ifdef connect
#undef connect
#endif

#ifdef accept
#undef accept
#endif

#ifdef getpeername
#undef getpeername
#endif

#ifdef getsockname
#undef getsockname
#endif

#ifndef TDESOCKETBASE_H
#define TDESOCKETBASE_H

#include <tqiodevice.h>
#include <tqstring.h>

#include "tdesocketaddress.h"
#include <tdelibs_export.h>

/*
 * This is extending QIODevice's error codes
 *
 * According to tqiodevice.h, the last error is IO_UnspecifiedError
 * These errors will never occur in functions declared in QIODevice
 * (except open, but you shouldn't call open)
 */
#define IO_ListenError		(IO_UnspecifiedError+1)
#define IO_AcceptError		(IO_UnspecifiedError+2)
#define IO_LookupError		(IO_UnspecifiedError+3)
#define IO_SocketCreateError	(IO_UnspecifiedError+4)
#define IO_BindError		(IO_UnspecifiedError+5)

class TQMutex;

namespace KNetwork {

class KResolverEntry;
class TDESocketDevice;

class TDESocketBasePrivate;
/** @class TDESocketBase tdesocketbase.h tdesocketbase.h
 *  @brief Basic socket functionality.
 *
 * This class provides the basic socket functionlity for descended classes.
 * Socket classes are thread-safe and provide a recursive mutex should it be 
 * needed.
 *
 * @note This class is abstract.
 *
 * @author Thiago Macieira <thiago.macieira@kdemail.net>
 */
class TDECORE_EXPORT TDESocketBase
{
public:
  /**
   * Possible socket options.
   *
   * These are the options that may be set on a socket:
   *  - Blocking: whether the socket shall operate in blocking
   *    or non-blocking mode. This flag defaults to on.
   *    See @ref setBlocking.
   *  - AddressReusable: whether the address used by this socket will
   *    be available for reuse by other sockets. This flag defaults to off.
   *    See @ref setAddressReuseable.
   *  - IPv6Only: whether an IPv6 socket will accept IPv4 connections
   *    through a mapped address. This flag defaults to off.
   *    See @ref setIPv6Only.
   *  - KeepAlive: whether TCP should send keepalive probes when a connection
   *    has gone idle for far too long.
   *  - Broadcast: whether this socket is allowed to send broadcast packets
   *    and will receive packets sent to broadcast.
   */
  enum SocketOptions
    {
      Blocking = 0x01,
      AddressReuseable = 0x02,
      IPv6Only = 0x04,
      Keepalive = 0x08,
      Broadcast = 0x10
    };

  /**
   * Possible socket error codes.
   *
   * This is a list of possible error conditions that socket classes may
   * be expected to find.
   *
   * - NoError: no error has been detected
   * - LookupFailure: if a name lookup has failed
   * - AddressInUse: address is already in use
   * - AlreadyBound: cannot bind again
   * - AlreadyCreated: cannot recreate the socket
   * - NotBound: operation required socket to be bound and it isn't
   * - NotCreated: operation required socket to exist and it doesn't
   * - WouldBlock: requested I/O operation would block
   * - ConnectionRefused: connection actively refused
   * - ConnectionTimedOut: connection timed out
   * - InProgress: operation (connection) is already in progress
   * - NetFailure: a network failure occurred (no route, host down, host unreachable or similar)
   * - NotSupported: requested operation is not supported
   * - Timeout: a timed operation timed out
   * - UnknownError: an unknown/unexpected error has happened
   * - RemotelyDisconnected: when a connection is disconnected by the other end (since 3.4)
   *
   * @sa error, errorString
   */
  enum SocketError
    {
      NoError = 0,
      LookupFailure,
      AddressInUse,
      AlreadyCreated,
      AlreadyBound,
      AlreadyConnected,
      NotConnected,
      NotBound,
      NotCreated,
      WouldBlock,
      ConnectionRefused,
      ConnectionTimedOut,
      InProgress,
      NetFailure,
      NotSupported,
      Timeout,
      UnknownError,
      RemotelyDisconnected
    };

public:
  /**
   * Default constructor.
   */
  TDESocketBase();

  /**
   * Destructor.
   */
  virtual ~TDESocketBase();

  /*
   * The following functions are shared by all descended classes and will have
   * to be reimplemented.
   */

protected:
  /**
   * Set the given socket options.
   *
   * The default implementation does nothing but store the mask internally.
   * Descended classes must override this function to achieve functionality and
   * must also call this implementation.
   *
   * @param opts	a mask of @ref SocketOptions or-ed bits of options to set
   *			or unset
   * @returns true on success
   * @note this function sets the options corresponding to the bits enabled in @p opts
   *       but will also unset the optiosn corresponding to the bits not set.
   */
  virtual bool setSocketOptions(int opts);

  /**
   * Retrieves the socket options that have been set.
   *
   * The default implementation just retrieves the mask from an internal variable.
   * Descended classes may choose to override this function to read the values
   * from the operating system.
   *
   * @returns the mask of the options set
   */
  virtual int socketOptions() const;

public:
  /**
   * Sets this socket's blocking mode.
   *
   * In blocking operation, all I/O functions are susceptible to blocking --
   * i.e., will not return unless the I/O can be satisfied. In non-blocking
   * operation, if the I/O would block, the function will return an error
   * and set the corresponding error code.
   *
   * The default implementation toggles the Blocking flag with the current
   * socket options and calls @ref setSocketOptions.
   *
   * @param enable		whether to set this socket to blocking mode
   * @returns whether setting this value was successful; it is NOT the 
   *          final blocking mode.
   */
  virtual bool setBlocking(bool enable);

  /**
   * Retrieves this socket's blocking mode.
   *
   * @returns true if this socket is/will be operated in blocking mode,
   *          false if non-blocking.
   */
  bool blocking() const;

  /**
   * Sets this socket's address reuseable flag.
   *
   * When the address reuseable flag is active, the address used by
   * this socket is left reuseable for other sockets to bind. If
   * the flag is not active, no other sockets may reuse the same
   * address.
   *
   * The default implementation toggles the AddressReuseable flag with the current
   * socket options and calls @ref setSocketOptions.
   *
   * @param enable		whether to set the flag on or off
   * @returns true if setting this flag was successful
   */
  virtual bool setAddressReuseable(bool enable);

  /**
   * Retrieves this socket's address reuseability flag.
   *
   * @returns true if this socket's address can be reused,
   *          false if it can't.
   */
  bool addressReuseable() const;

  /**
   * Sets this socket's IPv6 Only flag.
   *
   * When this flag is on, an IPv6 socket will only accept, connect, send to or
   * receive from IPv6 addresses. When it is off, it will also talk to
   * IPv4 addresses through v4-mapped addresses.
   *
   * This option has no effect on non-IPv6 sockets.
   *
   * The default implementation toggles the IPv6Only flag with the current
   * socket options and calls @ref setSocketOptions.
   *
   * @param enable		whether to set the flag on or off
   * @returns true if setting this flag was successful
   */
  virtual bool setIPv6Only(bool enable);

  /**
   * Retrieves this socket's IPv6 Only flag.
   *
   * @returns true if this socket will ignore IPv4-compatible and IPv4-mapped
   *	      addresses, false if it will accept them.
   */
  bool isIPv6Only() const;

  /**
   * Sets this socket Broadcast flag.
   *
   * Datagram-oriented sockets cannot normally send packets to broadcast
   * addresses, nor will they receive packets that were sent to a broadcast
   * address. To do so, you need to enable the Broadcast flag.
   *
   * This option has no effect on stream-oriented sockets.
   *
   * @returns true if setting this flag was successful.
   */
  virtual bool setBroadcast(bool enable);

  /**
   * Retrieves this socket's Broadcast flag.
   *
   * @returns true if this socket can send and receive broadcast packets,
   *          false if it can't.
   */
  bool broadcast() const;

  /**
   * Retrieves the socket implementation used on this socket.
   *
   * This function creates the device if none has been set
   * using the default factory.
   */
  TDESocketDevice* socketDevice() const;

  /**
   * Sets the socket implementation to be used on this socket.
   *
   * Note: it is an error to set this if the socket device has
   * already been set once.
   *
   * This function is provided virtual so that derived classes can catch
   * the setting of a device and properly set their own states and internal
   * variables. The parent class must be called.
   *
   * This function is called by @ref socketDevice above when the socket is
   * first created.
   */
  virtual void setSocketDevice(TDESocketDevice* device);

  /**
   * Sets the internally requested capabilities for a socket device.
   *
   * Most socket classes can use any back-end implementation. However, a few
   * may require specific capabilities not provided in the default
   * implementation. By using this function, derived classes can request
   * that a backend with those capabilities be created when necessary.
   *
   * For the possible flags, see @ref TDESocketDevice::Capabilities. However, note
   * that only the Can* flags make sense in this context.
   *
   * @note Since socketDevice must always return a valid backend object, it
   *       is is possible that the created device does not conform to all
   *       requirements requested. Implementations sensitive to this fact
   *       should test the object returned by @ref socketDevice (through
   *       @ref TDESocketDevice::capabilities, for instance) the availability.
   *
   * @param add		mask of @ref TDESocketDevice::Capabilities to add
   * @param remove	mask of bits to remove from the requirements
   * @return the current mask of requested capabilities
   */
  int setRequestedCapabilities(int add, int remove = 0);

protected:
  /**
   * Returns true if the socket device has been initialised in this
   * object, either by calling @ref socketDevice() or @ref setSocketDevice
   */
  bool hasDevice() const;

  /**
   * Sets the socket's error code.
   *
   * @param error		the error code
   */
  void setError(SocketError error);

public:
  /**
   * Retrieves the socket error code.
   * @sa errorString
   */
  SocketError error() const;

  /**
   * Returns the error string corresponding to this error condition.
   */
  inline TQString errorString() const
  { return errorString(error()); }

  /**
   * Returns the internal mutex for this class.
   *
   * Note on multithreaded use of sockets:
   * the socket classes are thread-safe by design, but you should be aware of
   * problems regarding socket creation, connection and destruction in
   * multi-threaded programs. The classes are guaranteed to work while
   * the socket exists, but it's not wise to call connect in multiple
   * threads. 
   *
   * Also, this mutex must be unlocked before the object is destroyed, which
   * means you cannot use it to guard against other threads accessing the object
   * while destroying it. You must ensure there are no further references to this
   * object when deleting it.
   */
  TQMutex* mutex() const;

public:
  /**
   * Returns the string describing the given error code, i18n'ed.
   *
   * @param code		the error code
   */
  static TQString errorString(SocketError code);

  /**
   * Returns true if the given error code is a fatal one, false
   * otherwise. The parameter here is of type int so that
   * casting isn't necessary when using the parameter to signal
   * QClientSocketBase::gotError.
   *
   * @param code		the code to test
   */
  static bool isFatalError(int code);

private:
  /// @internal
  /// called by TDESocketDevice
  void unsetSocketDevice();

  TDESocketBase(const TDESocketBase&);
  TDESocketBase& operator =(const TDESocketBase&);

  TDESocketBasePrivate *d;

  friend class TDESocketDevice;
};

/**
 * @class KActiveSocketBase tdesocketbase.h tdesocketbase.h
 * @brief Abstract class for active sockets
 *
 * This class provides the standard interfaces for active sockets, i.e.,
 * sockets that are used to connect to external addresses.
 *
 * @author Thiago Macieira <thiago.macieira@kdemail.net>
 */
class TDECORE_EXPORT KActiveSocketBase: public TQIODevice, virtual public TDESocketBase
{
public:
  /**
   * Constructor.
   */
  KActiveSocketBase();

  /**
   * Destructor.
   */
  virtual ~KActiveSocketBase();

  /**
   * Binds this socket to the given address.
   *
   * The socket will be constructed with the address family,
   * socket type and protocol as those given in the
   * @p address object.
   *
   * @param address		the address to bind to
   * @returns true if the binding was successful, false otherwise
   */
  virtual bool bind(const KResolverEntry& address) = 0;

  /**
   * Connect to a remote host.
   *
   * This will make this socket try to connect to the remote host.
   * If the socket is not yet created, it will be created using the
   * address family, socket type and protocol specified in the
   * @p address object.
   *
   * If this function returns with error InProgress, calling it
   * again with the same address after a time will cause it to test
   * if the connection has succeeded in the mean time.
   *
   * @param address		the address to connect to
   * @returns true if the connection was successful or has been successfully
   *          queued; false if an error occurred.
   */
  virtual bool connect(const KResolverEntry& address) = 0;

  /**
   * Disconnects this socket from a connection, if possible.
   *
   * If this socket was connected to an endpoint, the connection
   * is severed, but the socket is not closed. If the socket wasn't
   * connected, this function does nothing.
   *
   * If the socket hadn't yet been created, this function does nothing
   * either.
   *
   * Not all socket types can disconnect. Most notably, only
   * connectionless datagram protocols such as UDP support this operation.
   *
   * @return true if the socket is now disconnected or false on error.
   */
  virtual bool disconnect() = 0;

  /**
   * This call is not supported on sockets. Reimplemented from TQIODevice.
   * This will always return 0.
   */
#ifdef USE_QT4
  virtual qint64 size() const
#else // USE_QT4
  virtual Offset size() const
#endif // USE_QT4
  { return 0; }

  /**
   * This call is not supported on sockets. Reimplemented from TQIODevice.
   * This will always return 0.
   */
  virtual Offset at() const
  { return 0; }

  /**
   * This call is not supported on sockets. Reimplemented from TQIODevice.
   * This will always return false.
   */
  virtual bool at(Offset)
  { return false; }

  /**
   * This call is not supported on sockets. Reimplemented from TQIODevice.
   * This will always return true.
   */
  virtual bool atEnd() const
  { return true; }

  /**
   * Returns the number of bytes available for reading without
   * blocking.
   */
#ifdef USE_QT3
  virtual TQ_LONG bytesAvailable() const = 0;
#endif
#ifdef USE_QT4
  virtual qint64 bytesAvailable() const = 0;
#endif

  /**
   * Waits up to @p msecs for more data to be available on this socket.
   *
   * If msecs is -1, this call will block indefinetely until more data
   * is indeed available; if it's 0, this function returns immediately.
   *
   * If @p timeout is not NULL, this function will set it to indicate
   * if a timeout occurred.
   *
   * @returns the number of bytes available
   */
  virtual TQ_LONG waitForMore(int msecs, bool *timeout = 0L) = 0;

  /**
   * Reads data from the socket.
   *
   * Reimplemented from TQIODevice. See TQIODevice::readBlock for
   * more information.
   */
  virtual TQT_TQIO_LONG tqreadBlock(char *data, TQT_TQIO_ULONG len) = 0;

  /** @overload
   * Receives data and the source address.
   *
   * This call will read data in the socket and will also
   * place the sender's address in @p from object.
   *
   * @param data		where to write the read data to
   * @param maxlen		the maximum number of bytes to read
   * @param from		the address of the sender will be stored here
   * @returns the actual number of bytes read
   */
  virtual TQT_TQIO_LONG tqreadBlock(char *data, TQT_TQIO_ULONG maxlen, TDESocketAddress& from) = 0;

  /**
   * Peeks the data in the socket.
   *
   * This call will allow you to peek the data to be received without actually
   * receiving it -- that is, it will be available for further peekings and
   * for the next read call.
   *
   * @param data		where to write the peeked data to
   * @param maxlen		the maximum number of bytes to peek
   * @returns the actual number of bytes copied into @p data
   */
  virtual TQ_LONG peekBlock(char *data, TQ_ULONG maxlen) = 0;

  /** @overload
   * Peeks the data in the socket and the source address.
   *
   * This call will allow you to peek the data to be received without actually
   * receiving it -- that is, it will be available for further peekings and
   * for the next read call.
   *
   * @param data		where to write the peeked data to
   * @param maxlen		the maximum number of bytes to peek
   * @param from		the address of the sender will be stored here
   * @returns the actual number of bytes copied into @p data
   */
  virtual TQ_LONG peekBlock(char *data, TQ_ULONG maxlen, TDESocketAddress& from) = 0;

  /**
   * Writes the given data to the socket.
   *
   * Reimplemented from TQIODevice. See TQIODevice::writeBlock for
   * more information.
   */
  virtual TQT_TQIO_LONG tqwriteBlock(const char *data, TQT_TQIO_ULONG len) = 0;

  /** @overload
   * Writes the given data to the destination address.
   *
   * Note that not all socket connections allow sending data to different
   * addresses than the one the socket is connected to.
   *
   * @param data		the data to write
   * @param len			the length of the data
   * @param to			the address to send to
   * @returns the number of bytes actually sent
   */
  virtual TQT_TQIO_LONG tqwriteBlock(const char *data, TQT_TQIO_ULONG len, const TDESocketAddress& to) = 0;

  /**
   * Reads one character from the socket.
   * Reimplementation from TQIODevice. See TQIODevice::getch for more information.
   */
  virtual int getch();

  /**
   * Writes one character to the socket.
   * Reimplementation from TQIODevice. See TQIODevice::putch for more information.
   */
  virtual int putch(int ch);

  /**
   * This call is not supported on sockets. Reimplemented from TQIODevice.
   * This will always return -1;
   */
  virtual int ungetch(int)
  { return -1; }

  /**
   * Returns this socket's local address.
   */
  virtual TDESocketAddress localAddress() const = 0;

  /**
   * Return this socket's peer address, if we are connected.
   * If the address cannot be retrieved, the returned object will contain
   * an invalid address.
   */
  virtual TDESocketAddress peerAddress() const = 0;

  // FIXME KDE 4.0:
  // enable this function
#if 0
  /**
   * Returns this socket's externally-visible address, if known.
   */
  virtual TDESocketAddress externalAddress() const = 0;
#endif

protected:
  /**
   * Sets the socket's error code and the I/O Device's status.
   *
   * @param status		the I/O Device status
   * @param error		the error code
   */
  void setError(int status, SocketError error);

  /**
   * Resets the socket error code and the I/O Device's status.
   */
  void resetError();
};

/**
 * @class KPassiveSocketBase tdesocketbase.h tdesocketbase.h
 * @brief Abstract base class for passive sockets.
 *
 * This socket provides the initial functionality for passive sockets,
 * i.e., sockets that accept incoming connections.
 *
 * @author Thiago Macieira <thiago.macieira@kdemail.net>
 */
class TDECORE_EXPORT KPassiveSocketBase: virtual public TDESocketBase
{
public:
  /**
   * Constructor
   */
  KPassiveSocketBase();

  /**
   * Destructor
   */
  virtual ~KPassiveSocketBase();

  /**
   * Binds this socket to the given address.
   *
   * The socket will be constructed with the address family,
   * socket type and protocol as those given in the
   * @p address object.
   *
   * @param address		the address to bind to
   * @returns true if the binding was successful, false otherwise
   */
  virtual bool bind(const KResolverEntry& address) = 0;

  /**
   * Puts this socket into listening mode.
   *
   * Placing a socket in listening mode means that it will
   * be allowed to receive incoming connections from
   * remote hosts.
   *
   * Note that some socket types or protocols cannot be
   * put in listening mode.
   *
   * @param backlog		the number of accepted connections to
   *				hold before starting to refuse
   * @returns true if the socket is now in listening mode
   */
  virtual bool listen(int backlog) = 0;

  /**
   * Closes this socket. All resources used are freed. Note that closing
   * a passive socket does not close the connections accepted with it.
   */
  virtual void close() = 0;

  /**
   * Accepts a new incoming connection.
   *
   * If this socket was in listening mode, you can call this
   * function to accept an incoming connection.
   *
   * If this function cannot accept a new connection (either
   * because it is not listening for one or because the operation
   * would block), it will return NULL.
   *
   * Also note that descended classes will override this function
   * to return specialised socket classes.
   */
  virtual KActiveSocketBase* accept() = 0;

  /**
   * Returns this socket's local address.
   */
  virtual TDESocketAddress localAddress() const = 0;

  /**
   * Returns this socket's externally-visible address if known.
   */
  virtual TDESocketAddress externalAddress() const = 0;

private:
  KPassiveSocketBase(const KPassiveSocketBase&);
  KPassiveSocketBase& operator = (const KPassiveSocketBase&);
};

}				// namespace KNetwork

#endif