//----------------------------------------------------------------------------
// ObjectWindows
// Copyright (c) 1995, 1996 by Borland International, All Rights Reserved
//
/// \file
/// Winsock for OWL subsystem.
/// Based on work by Paul Pedriana, 70541.3223@compuserve.com
//----------------------------------------------------------------------------
#include <owl/pch.h>
#include <owl/defs.h>
#include <owl/winsock.h>
 
namespace owl {
 
OWL_DIAGINFO;
 
//
/// Default constructor for a socket. The individual members of the TSocket can be
/// set later.
//
TSocket::TSocket()
:
  Handle(INVALID_SOCKET),
  Bound(false),
  Family(PF_UNSPEC),
  Type(0),
  Protocol(0),
  LastError(0),
  MaxReadBufferSize(N_DEF_MAX_READ_BUFFFER_SIZE),
  SaveSocket(0),
  Window(this)
{
  Init();
}
 
//
/// TSocket(SOCKET&) is a constructor based on a Winsock SOCKET descriptor.
//
TSocket::TSocket(SOCKET& newS)
:
  Handle(newS),
  Bound(false),
  LastError(0),
  MaxReadBufferSize(N_DEF_MAX_READ_BUFFFER_SIZE),
  SaveSocket(0),
  Window(this)
{
  int temp;
  GetMyAddress(SocketAddress, temp, Handle); // Fills in our address information.
  Family = SocketAddress.sa_family;
  GetPeerAddress(SocketAddress, temp, Handle); // Fills in out Peer's address info if it exists.
  Init();
}
 
//
/// This is the standard constructor for a TSocket. It doesn't call socket() or
/// bind(). These must be done independently.
//
TSocket::TSocket(TSocketAddress& newSocketAddress, int newFamily, int newType, int newProtocol)
:
  Handle(INVALID_SOCKET),
  Bound(false),
  SocketAddress(newSocketAddress),
  Family(newFamily),
  Type(newType),
  Protocol(newProtocol),
  LastError(0),
  MaxReadBufferSize(N_DEF_MAX_READ_BUFFFER_SIZE),
  SaveSocket(0),
  Window(this)
{
  Init();
}
 
//
/// This TSocket destructor will close the socket it if has not be closed already.
/// It will also delete the friend notification window.
//
TSocket::~TSocket()
{
  if (Handle != static_cast<SOCKET>(INVALID_SOCKET) && !SaveSocket) {
    SetLingerOption(0, 60); //Don't wait till data gets sent.  Just kill the socket now.
    CloseSocket();
  }
}
 
//
/// This function is an intitialization function called by the TSocket constructors.
/// It simply creates the friend window that the TSocket needs for Winsock
/// notifications.
//
void TSocket::Init()
{
  // This is for initialization steps that are common to all constructors.
  //
  try {
     Window.Create();
  }
  catch (...) {
    //::MessageBox();
  }
}
 
//
/// Checks the return error value from a sockets call, caching the last error if one
/// occured (i.e., error is non-zero). Returns a Winsock error/noerror code.
//
int TSocket::SocketsCallCheck(int error)
{
  if (error) {
    LastError = TWinSockDll::WSAGetLastError();
    return WINSOCK_ERROR;
  }
  return WINSOCK_NOERROR;
}
 
//
// Does a deep copy of the TSocket, as much as possible.
//
TSocket& TSocket::operator =(TSocket& newSocket)
{
  Family            = newSocket.Family;
  Type              = newSocket.Type;
  Protocol          = newSocket.Protocol;
  LastError         = newSocket.LastError;
  Handle            = newSocket.Handle;
  SocketAddress     = newSocket.SocketAddress;
  PeerSocketAddress = newSocket.PeerSocketAddress;
  SaveSocket        = newSocket.SaveSocket;
 
  // Copy the friend (helper) window.  Note that there is an operator= defined
  //  for the class TSocketWindow.  However, the TSocketWindow::operator= will
  //  set its 'SocketParent' member to be the newSocket SocketWindow's previous
  //  TSocket parent, which may not be us.  Thus, after setting 'Window =
  //  newSocket.Window;', we set 'Window.SocketParent = this;'.
  //
  Window              = newSocket.Window;
  Window.SetSocketParent(this);
 
  return *this;
}
 
//
/// While it's possible that two sockets could refer to the same SOCKET (though this
/// would likely create a mess if not governed with care), it's defined as not
/// possible that two Sockets could have the same window member. This is because the
/// window is created uniquely on construction for each TSocket.
//
bool operator ==(const TSocket& socket1, const TSocket& socket2)
{
  return socket1.Handle == socket2.Handle && socket1.Window == socket2.Window;
}
 
//
/// This function says to listen only to FD_ACCEPT messages. Note that a socket set
/// up to be a listening socket will never be a connected socket, and a connected
/// socket will never receive FD_ACCEPT messages. Thus all stream sockets are
/// implicitly either connected sockets or listening sockets. Since the accepted
/// socket needs a different notification window from the listening socket, and the
/// sockets specification says that an accepted socket inherits the notification
/// properties of the listening socket, the listening socket must not be set to
/// receive FD_READ, etc, notifications. This is because it's possible that between
/// the accept() call for the new socket and the WSAAsyncSelect() call for the new
/// socket, data may be received for the new socket. Thus the listening socket may
/// get sent the message and it would never get routed to the new socket. Calling
/// this function is saying that this SocketWindow is for listening for connections.
///
/// The return value is WINSOCK_ERROR or WINSOCK_NOERROR. You can then examine
/// GetLastError().
//
int TSocket::StartAcceptNotification()
{
  int error = Window.StartAcceptNotification();
  if (error == WINSOCK_ERROR) {
    LastError = Window.GetLastError();
  }
  return error;
}
 
//
/// This function turns on all Winsock notifications except FD_ACCEPT. Calling this
/// function is saying that this SocketWindow is for connections rather than for
/// listening. Since a Winsock socket cannot be a listening socket and a connected
/// socket at the same time, the notification functions are separated from each
/// other: StartAcceptNotification() and StartRegularNotification().
///
/// The return value is WINSOCK_ERROR or WINSOCK_NOERROR. You can then examine
/// GetLastError().
//
int TSocket::StartRegularNotification()
{
  int error = Window.StartRegularNotification();
  if (error == WINSOCK_ERROR) {
    LastError = Window.GetLastError();
  }
  return error;
}
 
//
/// The return value is WINSOCK_ERROR or WINSOCK_NOERROR. You can then examine
/// GetLastError().
//
int TSocket::StartCustomNotification(int selectionOptions)
{
  int error = Window.StartCustomNotification(selectionOptions);
  if (error == WINSOCK_ERROR) {
    LastError = Window.GetLastError();
  }
  return error;
}
 
//
/// CancelNotification() turns off the notification to this window. This also
/// changes the socket to be blocking.
/// The return value is WINSOCK_ERROR or WINSOCK_NOERROR. You can then examine
/// GetLastError().
//
int TSocket::CancelNotification()
{
  int error = Window.CancelNotification();
  if (error == WINSOCK_ERROR) {
    LastError = Window.GetLastError();
  }
  return error;
}
 
//
/// The SetSocketStyle function can be used to set or change some TSocket member
/// data. Note that the newFamily is also represented in the TSocketAddress member,
/// and so they should match.
//
void TSocket::SetSocketStyle(int newFamily, int newType, int newProtocol)
{
  Family   = newFamily;
  Type     = newType;
  Protocol = newProtocol;
}
 
//
/// Converts a string protocol to integer value. Makes assumptions about the
/// protocol string. Only "tcp" and udp return valid values.
//
int TSocket::ConvertProtocol(char* protocol)
{
  if (!protocol)
    return IPPROTO_TCP;
  if (strcmp("tcp", protocol) == 0)
    return IPPROTO_TCP;
  if(strcmp("udp", protocol) == 0)
    return IPPROTO_UDP;
  return IPPROTO_IP;
}
 
//
/// The CreateSocket function is much like the Winsock socket() function. This
/// function assumes that nFamily, nType, and nProtocol are already set properly.
/// Note also that since the return of socket() is assigned to 's', that 's' must
/// not already be used. This is another way of saying that there can only be one
/// SOCKET for each TSocket object.
//
int TSocket::CreateSocket()
{
  Handle = TWinSockDll::socket(Family, Type, Protocol);
  return SocketsCallCheck(Handle == static_cast<SOCKET>(INVALID_SOCKET));
}
 
//
/// The CloseSocket() function is much like the Winsock closesocket() function.
//
int TSocket::CloseSocket()
{
  if (Handle != static_cast<SOCKET>(INVALID_SOCKET)) {
    int error = TWinSockDll::closesocket(Handle);
    if (error) {
      LastError = TWinSockDll::WSAGetLastError();
      return WINSOCK_ERROR;
    }
  }
  Handle = INVALID_SOCKET;  // It's invalid now.
  Bound = false;
  return WINSOCK_NOERROR;
}
 
//
/// The ShutDownSocket() function is much like the Winsock shutdown() function. Note
/// that shutting down a socket essentially means that you can't un-shut it down.
/// It's a graceful way of preparing to end a session, somewhat like a yellow
/// stoplight. Use this function to close your socket, while still allowing data be
/// received from the network. This is as opposed to CloseSocket(), which kills all
/// transfers in both directions. shutMode is one of the enumerations:
/// ShutModeNoRecv, ShutModeNoSend, or ShutModeNoRecvSend.
//
int TSocket::ShutDownSocket(TShutMode shutMode)
{
  if (Handle != static_cast<SOCKET>(INVALID_SOCKET)) {
    int error = TWinSockDll::shutdown(Handle, shutMode);
    if (error) {
      LastError = TWinSockDll::WSAGetLastError();
      return WINSOCK_ERROR;
    }
    Handle = INVALID_SOCKET;  // It's invalid now.
    Bound = false;
  }
  return WINSOCK_NOERROR;
}
 
//
/// BindSocket is much like the Winsock bind() function. Regardless of what
/// mySocketAddress may have been previously, a call to 'bind()' immediately makes
/// the socket's address the one put into the bind() call. Thus, mySocketAddress is
/// always assigned to be boundSocketAddress. The address argument must be in
/// network byte ordering. On the other hand, the SocketAddress class always keeps
/// its addresses in network byte ordering.
//
int TSocket::BindSocket(const TSocketAddress& addressToBindTo)
{
  SocketAddress = addressToBindTo;
 
  // bind() ideally returns 0.
  //
  if (TWinSockDll::bind(Handle, &SocketAddress, sizeof(sockaddr))) {
    LastError = TWinSockDll::WSAGetLastError();
    return WINSOCK_ERROR;
  }
  Bound = true;
  return WINSOCK_NOERROR;
}
 
//
/// This BindSocket simply binds with the previously defined member data socket
/// address.
//
int TSocket::BindSocket()
{
  return BindSocket(SocketAddress);
}
 
//
/// This function stores the address into the reference argument 'socketAddress'.
/// 'addressLength' will hold the length of the address. 'socket' refers to the
/// socket whose address will be examined.
//
int TSocket::GetMyAddress(TSocketAddress& socketAddress, int& addressLength, SOCKET& socket)
{
  return SocketsCallCheck(!TWinSockDll::getsockname(socket, &socketAddress, &addressLength));
}
 
//
/// This function stores the address into the reference argument 'socketAddress'.
/// 'addressLength' will hold the length of the address. Uses the SOCKET in my
/// member data as the socket to get the address of.
//
int TSocket::GetMyAddress(TSocketAddress& socketAddress, int& addressLength)
{
  return GetMyAddress(socketAddress, addressLength, Handle);
}
 
//
/// The GetPeerAddress() function is much like the Winsock getpeername() function.
/// The Winsock getpeername() function is misnamed; it should be getpeeraddress().
/// socketAddress will be changed to have the right addressing info, and
/// nAddressLength will be set to be the address length.
/// Note that this function can be used to get the address for any socket
/// descriptor, not just our own socket descriptor.
//
int TSocket::GetPeerAddress(TSocketAddress& socketAddress, int& addressLength, SOCKET& socket)
{
  // This code only works because SOCKET is defined as a uint and not a struct
  //
  if (!socket)
    socket = Handle;
 
  return SocketsCallCheck(!TWinSockDll::getpeername(socket, &socketAddress, &addressLength));
}
 
//
/// This version of GetPeerAddress() works on our own socket descriptor.
//
int TSocket::GetPeerAddress(TSocketAddress& socketAddress, int& addressLength)
{
  return GetPeerAddress(socketAddress, addressLength, Handle);
}
 
//
/// This may be useful for changing the address or setting the address before
/// binding. It's no good to change this after binding, as a binding is a permanent
/// association between a socket descriptor and a full address (for IP, this is a
/// ushort port and ulong address).
//
void TSocket::SetMyAddress(TSocketAddress& newSocketAddress)
{
  SocketAddress = newSocketAddress;
}
 
//
/// The 'myPeerSocketAddress' member variable is useful for Datagram sockets because
/// it allows them to specify a default destination to send datagrams to. With a
/// default destination, a datagram socket that always or often sends to one address
/// can simply call the Write or Send functions with no address arguments and
/// the data will send to the default address. This function can also be used by a
/// stream socket to set the address for a peer that it wants to connect to.
//
void TSocket::SetPeerSocketAddress(TSocketAddress& newPeerSocketAddress)
{
  PeerSocketAddress = newPeerSocketAddress;
}
 
#if !defined(__GNUC__) //JJH removal of pragma warn for GCC
#pragma warn -cln
#endif
 
//
/// GetDriverWaitingSize() is much like calling ioctlsocket(s, FIONREAD,...) in
/// Winsock. It returns the number of bytes waiting to be read on the socket. For
/// datagrams, it is the size of the next datagram. For streams, it should be the
/// total waiting bytes.
//
ulong TSocket::GetDriverWaitingSize()
{
  ulong charsWaiting;
  if (TWinSockDll::ioctlsocket(Handle, FIONREAD, &charsWaiting)) {
    return 0;
  }
  return charsWaiting;
}
#if !defined(__GNUC__) //JJH removal of pragma warn for GCC
#pragma warn .cln
#endif
 
//
/// Returns the total number of bytes waiting to be read.
//
ulong TSocket::GetTotalWaitingSize()
{
  return GetDriverWaitingSize();
}
 
//
/// This function gets called whenever the socket gets a read notification. This
/// means that data on the port is ready to be read. Thus this function must be
/// subclassed by a DatagramSocket and StreamSocket.
//
int TSocket::DoReadNotification(const SOCKET& /*socket*/, int /*nError*/)
{
  return 0;
}
 
//
/// The generic socket doesn't know how many bytes it can send, since this limit is
/// dependent on whether the socket is a stream or datagram socket. Thus this
/// function must be subclassed by a DatagramSocket and StreamSocket.
//
int TSocket::DoWriteNotification(const SOCKET& /*s*/, int /*nError*/)
{
  return 0;
}
 
//
/// This isn't responded to in the generic TSocket class.
//
int TSocket::DoOOBNotification(const SOCKET& /*s*/, int /*nError*/)
{
  return 0;
}
 
//
/// This isn't responded to in the generic TSocket class.
//
int TSocket::DoAcceptNotification(const SOCKET& /*s*/, int /*nError*/)
{
  return 0;
}
 
//
/// This isn't responded to in the generic TSocket class.
//
int TSocket::DoConnectNotification(const SOCKET& /*s*/, int /*nError*/)
{
  return 0;
}
 
//
/// This isn't responded to in the generic TSocket class.
//
int TSocket::DoCloseNotification(const SOCKET& /*s*/, int /*nError*/)
{
  return 0;
}
 
//
/// This should be called by someone who knows what the correct value is.
//
void TSocket::SetMaxReadBufferSize(int maxReadBufferSize)
{
  if(maxReadBufferSize > 0)
    MaxReadBufferSize = maxReadBufferSize;
}
 
//
/// Allows transmission of broadcast messages.
//
int TSocket::SetBroadcastOption(bool broadcast)
{
  // Must pass an int, not a bool, to setsockopt
  //
  BOOL bcast = broadcast;
  return SocketsCallCheck(TWinSockDll::setsockopt(Handle, SOL_SOCKET, SO_BROADCAST, (char*)&bcast, sizeof bcast));
}
 
//
/// Records debugging info.
//
int TSocket::SetDebugOption(bool debug)
{
  // Must pass an BOOL, not a bool, to setsockopt
  //
  BOOL bDebug = debug;
  return SocketsCallCheck(TWinSockDll::setsockopt(Handle, SOL_SOCKET, SO_DEBUG, (char*)&bDebug, sizeof bDebug));
}
 
//
/// If you set 'linger' to true, then that means to linger for 'lingerTime' seconds.
/// Examples:
/// - linger=true, lingerTime=0  Hard immediate close. All queued data for sending
/// gets canned immediately.
/// - linger=true, lingerTime=2.  Graceful close. Waits 2 seconds to try to send any
/// pending data.
/// - linger=false, lingerTime=\<any\>.  "Graceful" immediate close. Causes data to be
/// still in queue to send when ready.
//
int TSocket::SetLingerOption(bool linger, ushort lingerTime)
{
  LINGER lingerOptions;
 
  lingerOptions.l_onoff = linger;  //Note that bLinger is a bool and LINGER.l_onoff is a u_short.
  lingerOptions.l_linger = lingerTime;
 
  return SocketsCallCheck(TWinSockDll::setsockopt(Handle, SOL_SOCKET, SO_LINGER, (char*)&lingerOptions, sizeof lingerOptions));
}
 
//
/// A false argument means don't route.
//
int TSocket::SetRouteOption(bool route)
{
  BOOL bRout = !route;
  return SocketsCallCheck(TWinSockDll::setsockopt(Handle, SOL_SOCKET, SO_DONTROUTE, (char*)&bRout, sizeof bRout));
}
 
//
/// Sends keepAlive messages.
//
int TSocket::SetKeepAliveOption(bool keepAlive)
{
  BOOL bKeepAlive = keepAlive;
  return SocketsCallCheck(TWinSockDll::setsockopt(Handle, SOL_SOCKET, SO_KEEPALIVE, (char*)&bKeepAlive, sizeof bKeepAlive));
}
 
//
/// Receives out-of-band (OOB) data in the normal data stream.
//
int TSocket::SetOOBOption(bool sendOOBDataInline)
{
  BOOL bSendOOBDataInline = sendOOBDataInline;
  return SocketsCallCheck(TWinSockDll::setsockopt(Handle, SOL_SOCKET, SO_OOBINLINE, (char*)&bSendOOBDataInline, sizeof bSendOOBDataInline));
}
 
//
/// Sets the buffer size for receiving messages.
//
int TSocket::SetReceiveBufferOption(int receiveBufferSize)
{
  return SocketsCallCheck(TWinSockDll::setsockopt(Handle, SOL_SOCKET, SO_RCVBUF, (char*)&receiveBufferSize, sizeof receiveBufferSize));
}
 
//
/// Sets the buffer size for sending messages.
//
int TSocket::SetSendBufferOption(int sendBufferSize)
{
  return SocketsCallCheck(TWinSockDll::setsockopt(Handle, SOL_SOCKET, SO_SNDBUF, (char*)&sendBufferSize, sizeof sendBufferSize));
}
 
//
/// Allows the socket to bind to an already-bound address.
//
int TSocket::SetReuseAddressOption(bool allowReuseAddress)
{
  BOOL bAllowReuseAddress = allowReuseAddress;
  return SocketsCallCheck(TWinSockDll::setsockopt(Handle, SOL_SOCKET, SO_REUSEADDR, (char*)&bAllowReuseAddress, sizeof bAllowReuseAddress));
}
 
//
/// Retrieves the current broadcast option.
//
int TSocket::GetBroadcastOption(bool& broadcast)
{
  BOOL bBroadcast;
  int size = sizeof bBroadcast;
  int retval = SocketsCallCheck(TWinSockDll::getsockopt(Handle, SOL_SOCKET, SO_BROADCAST, (char*)&bBroadcast, &size));
  broadcast = bBroadcast != FALSE;
  return retval;
}
 
//
/// Retrieves the current debugging option.
//
int TSocket::GetDebugOption(bool& debug)
{
  BOOL bDebug;
  int size = sizeof bDebug;
  int retval = SocketsCallCheck(TWinSockDll::getsockopt(Handle, SOL_SOCKET, SO_DEBUG, (char*)&bDebug, &size));
  debug = bDebug != FALSE;
  return retval;
}
 
//
/// Retreives the current linger option.
//
int TSocket::GetLingerOption(bool& linger, ushort& lingerTime)
{
  LINGER lingerOptions;
  int size = sizeof lingerOptions;
 
  if (SocketsCallCheck(TWinSockDll::getsockopt(Handle, SOL_SOCKET, SO_LINGER, (char*)&lingerOptions, &size)) == WINSOCK_NOERROR) {
    linger = lingerOptions.l_onoff;
    lingerTime = lingerOptions.l_linger;
    return WINSOCK_NOERROR;
  }
  return WINSOCK_ERROR;
}
 
//
/// Retrieves the routing option.
//
int TSocket::GetRouteOption(bool& route)
{
  BOOL bRoute;
  int size = sizeof bRoute;
  if (SocketsCallCheck(TWinSockDll::getsockopt(Handle, SOL_SOCKET, SO_DONTROUTE, (char*)&bRoute, &size)) == WINSOCK_NOERROR) {
    // route value of true means "don't route."  So we change it to mean what
    // you'd think.
    //
    route = bRoute == FALSE;
    return WINSOCK_NOERROR;
  }
  return WINSOCK_ERROR;
}
 
//
/// Retrieves the keepAlive option.
//
int TSocket::GetKeepAliveOption(bool& keepAlive)
{
  BOOL bKeepAlive;
  int size = sizeof bKeepAlive;
  int retval = SocketsCallCheck(TWinSockDll::getsockopt(Handle, SOL_SOCKET, SO_KEEPALIVE, (char*)&bKeepAlive, &size));
  keepAlive = bKeepAlive != FALSE;
  return retval;
}
 
//
/// Retrieves the out-of-band (OOB) option.
//
int TSocket::GetOOBOption(bool& sendOOBDataInline)
{
  BOOL bSendOOBDataInline;
  int size = sizeof bSendOOBDataInline;
  int retval = SocketsCallCheck(TWinSockDll::getsockopt(Handle, SOL_SOCKET, SO_OOBINLINE, (char*)&bSendOOBDataInline, &size));
  sendOOBDataInline = bSendOOBDataInline != FALSE;
  return retval;
}
 
//
/// Retrieves the current receiving buffer size.
//
int TSocket::GetReceiveBufferOption(int& receiveBufferSize)
{
  int size = sizeof receiveBufferSize;
  return SocketsCallCheck(TWinSockDll::getsockopt(Handle, SOL_SOCKET, SO_RCVBUF, (char*)&receiveBufferSize, &size));
}
 
//
/// Retrieves the current sending buffer size.
//
int TSocket::GetSendBufferOption(int& sendBufferSize)
{
  int size = sizeof sendBufferSize;
  return SocketsCallCheck(TWinSockDll::getsockopt(Handle, SOL_SOCKET, SO_SNDBUF, (char*)&sendBufferSize, &size));
}
 
//
/// Retrieves the reusable address option.
//
int TSocket::GetReuseAddressOption(bool& allowReuseAddress)
{
  BOOL bAllowReuseAddress;
  int size = sizeof bAllowReuseAddress;
  int retval = SocketsCallCheck(TWinSockDll::getsockopt(Handle, SOL_SOCKET, SO_REUSEADDR, (char*)&bAllowReuseAddress, &size));
  allowReuseAddress = bAllowReuseAddress != FALSE;
  return retval;
}
 
//----------------------------------------------------------------------------
 
//
//
//
DEFINE_RESPONSE_TABLE1(TSocketWindow, TWindow)
  EV_MESSAGE(MSG_SOCKET_NOTIFY, DoNotification),
END_RESPONSE_TABLE;
 
//
// Default this to be our standard MSG_SOCKET_NOTIFY
//
uint TSocketWindow::MsgSocketNotification = MSG_SOCKET_NOTIFY;
 
TSocketWindow::TSocketWindow(TSocket* socketParent)
:
  TWindow(0, _T("TSocketWindow")),
  SocketParent(socketParent),
  SelectOptions(0),
  WindowNotification(0),
  NotificationSet(NotifyAll),
  LastError(0)
{
  Attr.Style = 0;  // Turn off WS_CHILD (the default) style.
}
 
//
/// From the user's standpoint, the only thing that is required to make two SocketWindows
///  act as equal is to make their parents and selection options the same.  The actual
///  window handle identities are unimportant.  Thus, we keep our original Window handle,
///  even if the newSocketWindow had an empty window handle.
///
/// However, it may be impossible to assign the parent correctly if this operator is being called
///  in the parent's operator=().  The new parent SHOULD be the original parent, yet the
///  newSocketWindow has a new parent.  We cannot know in this function the conditions
///  under which this assignment is called, so we blindly copy the new parent.
///  The parent will have to override this assignment if the old parent is to
///  remain as it was.
///
/// This function does the best it can to make this window act just like newSocketWindow:
//
TSocketWindow& TSocketWindow::operator =(TSocketWindow& src)
{
  SelectOptions      = src.SelectOptions;
  SocketParent       = src.SocketParent;
  WindowNotification = src.WindowNotification;
  NotificationSet    = src.NotificationSet;
  LastError           = src.LastError;
 
  if (SocketParent->Handle) {
    // Note that if SelectOptions were empty (0), then we are saying to turn
    //  off notifications and make the socket blocking.
    //
    TWinSockDll::WSAAsyncSelect(SocketParent->Handle, *this, MSG_SOCKET_NOTIFY, SelectOptions);
  }
  return *this;
}
 
//
/// This function says to only listen to FD_ACCEPT messages.  It is important to note that a
///  socket set up to be a listening socket will never be a connected socket, and a connected
///  socket will never receive FD_ACCEPT messages.  Thus all stream sockets are implicitly either
///  connected sockets or listening sockets.
///
/// Since the accepted socket will want to have a different notification window than the
///  listening socket, and the sockets specification says that an accepted socket
///  inherits the notification properties of the listening socket, it is imperative
///  that the listening socket not be set to receive FD_READ, etc notifications.  This
///  is because it is possible that between the accept() call for the new socket and
///  the WSAAsyncSelect() call for the new socket, data may be received for the new socket.
///  Thus the listening socket may get sent the message and it would never get routed to
///  the new socket.
///
/// Calling this function is saying that this SocketWindow is for listening for connections.
///
/// The return value is WINSOCK_ERROR or WINSOCK_NOERROR.  You can then examine GetLastError().
//
int TSocketWindow::StartAcceptNotification()
{
  SelectOptions = FD_ACCEPT;
  return StartCustomNotification(SelectOptions);
}
 
//
/// This function turns on all Winsock notifications except FD_ACCEPT.
///
/// Calling this function is saying that this SocketWindow is for connections rather
/// than for listening.  Since a Winsock socket cannot be a listening socket and
/// a connected socket at the same time, we have serarate the notification functions
/// from each other: StartAcceptNotification() and StartRegularNotification().
///
/// The return value is WINSOCK_ERROR or WINSOCK_NOERROR.  You can then examine
/// GetLastError().
//
int TSocketWindow::StartRegularNotification()
{
  SelectOptions = FD_ALL & ~FD_ACCEPT;
  return StartCustomNotification(SelectOptions);
}
 
//
/// The return value is WINSOCK_ERROR or WINSOCK_NOERROR.  You can then examine
/// GetLastError().
//
int TSocketWindow::StartCustomNotification(int selectOptions)
{
  SelectOptions = selectOptions;
 
  int error = TWinSockDll::WSAAsyncSelect(*SocketParent, *this, MSG_SOCKET_NOTIFY, SelectOptions);
  if (error) {
    LastError = TWinSockDll::WSAGetLastError();
    return WINSOCK_ERROR;
  }
  return WINSOCK_NOERROR;
}
 
//
/// CancelNotification() turns off the notification to this window. This also changes the
///  socket to be blocking.
/// The return value is WINSOCK_ERROR or WINSOCK_NOERROR.  You can then examine GetLastError().
//
int TSocketWindow::CancelNotification()
{
  SelectOptions = 0;
  return StartCustomNotification(SelectOptions);
}
 
//
/// DoNotification() is the SocketWindow's protected internal notification system.
//
TResult TSocketWindow::DoNotification(TParam1 param1, TParam2 param2)
{
  SOCKET socket = param1;
  int     error   = (int)WSAGETSELECTERROR(param2);
 
  if (socket != SocketParent->Handle)
    return 0;
 
  switch (WSAGETSELECTEVENT(param2)) {
    case FD_READ:
      if (WindowNotification && (NotificationSet & NotifyRead)) {
        return WindowNotification->SendMessage(MsgSocketNotification, param1, param2);
      }
      SocketParent->DoReadNotification(socket, error);
      break;
    case FD_WRITE:
      if (WindowNotification && (NotificationSet & NotifyWrite)) {
        return WindowNotification->SendMessage(MsgSocketNotification, param1, param2);
      }
      SocketParent->DoWriteNotification(socket, error);
      break;
    case FD_OOB:
      if (WindowNotification && (NotificationSet & NotifyOOB)) {
        return WindowNotification->SendMessage(MsgSocketNotification, param1, param2);
      }
      SocketParent->DoOOBNotification(socket, error);
      break;
    case FD_ACCEPT:
      if (WindowNotification && (NotificationSet & NotifyAccept)) {
        return WindowNotification->SendMessage(MsgSocketNotification, param1, param2);
      }
      SocketParent->DoAcceptNotification(socket, error);
      break;
    case FD_CONNECT:
      if (WindowNotification && (NotificationSet & NotifyConnect)) {
        return WindowNotification->SendMessage(MsgSocketNotification, param1, param2);
      }
      SocketParent->DoConnectNotification(socket, error);
      break;
    case FD_CLOSE:
      if (WindowNotification && (NotificationSet & NotifyClose)) {
        return WindowNotification->SendMessage(MsgSocketNotification, param1, param2);
      }
      SocketParent->DoCloseNotification(socket, error);
  }
  return 1;
}
 
} // OWL namespace
/* ========================================================================== */
 

V730 It is possible that not all members of a class are initialized inside the constructor. Consider inspecting: Type, Protocol.

V565 An empty exception handler. Silent suppression of exceptions can hide the presence of bugs in source code during testing.

V669 The 'socket' argument is a non-constant reference. The analyzer is unable to determine the position at which this argument is being modified. It is possible that the function contains an error.