/* Iterative server socket class for TCP connection oriented IPC.
   Rick Smereka, Copyright (C) 2000-2002.

   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, get a copy via the Internet at
   http://gnu.org/copyleft/gpl.html or write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston,
   MA 02111-1307 USA

   You can contact the author via email at rsmereka@future-lab.com.

   Original version for Unix and Windows 32-bit.
   Sep/2000, Rick Smereka

   Added 'using namespace std'. Dec/2002, Rick Smereka */

#include "stdhead.h"
#include "flsocket.h"
#include "ssocket.h"

using namespace std;

ssocket::ssocket()
{
   // No parameter constructor.

   server_hostname = "";
   server_port = 0;
   server_init = FALSE;
}

ssocket::ssocket(int port)
{
   /* Constructor using port number. After loading port number,
      attempt to initialize the server. */

   server_hostname = "";
   server_port = port;
   server_init = FALSE;
   (void)init(port);
}

bool ssocket::init(int port)
{
   /* Initialize the server for communication. The port
      that the server listens to is expected in 'port'.
      Function returns 'TRUE' upon success,'FALSE' otherwise. */

   char *localHost;
   int err = FALSE;

   if (port <= 0)
      return(FALSE);

   if (server_init)
      return(FALSE);

   try
      {
      localHost = new char[1024];
      }

   catch(bad_alloc)
      {
      err = TRUE;
      }

   if (err)
      return(FALSE);

   // attempt to resolve the local host name

   if (gethostname(localHost, 1023))
      {
      delete localHost;
      return(FALSE);
      }

#ifdef OS_WIN32
	serverSocket = socket(PF_INET, SOCK_STREAM, DEFAULT_PROTOCOL);
#endif

#ifdef OS_UNIX
	serverSocket = socket(AF_INET, SOCK_STREAM, DEFAULT_PROTOCOL);
#endif

	if (serverSocket == INVALID_SOCKET)
	   {
      delete localHost;
	   return(FALSE);
	   }

	// populate server address structure

   memset(&sockServerAddr, 0,	sizeof(sockServerAddr));
	sockServerAddr.sin_family = AF_INET;
	sockServerAddr.sin_addr.s_addr = INADDR_ANY;
   sockServerAddr.sin_port = htons(port);

	// bind the server socket

#ifdef OS_WIN32
	if (bind(serverSocket,(LPSOCKADDR)&sockServerAddr,sizeof(sockServerAddr))
	   == SOCKET_ERROR)
#endif

#ifdef OS_UNIX
	if (bind(serverSocket, (sockaddr *)&sockServerAddr, sizeof(sockServerAddr))
	   == SOCKET_ERROR)
#endif

	   {
      delete localHost;
	   return(FALSE);
	   }

	// listen on port

	if (listen(serverSocket, QUEUE_SIZE) == SOCKET_ERROR)
	   {
      delete localHost;
	   return(FALSE);
	   }

	srvclientSocket = INVALID_SOCKET;
   server_hostname = localHost;
   delete localHost;
   server_init = TRUE;
	return(TRUE);
}

bool ssocket::server_wait(void)
{
   /* Accept a client connection. Function
		returns 'TRUE' if a client connection
		was successful, 'FALSE' otherwise. */

#ifdef OS_WIN32
   int len = sizeof(SOCKADDR);
#else
   socklen_t len = sizeof(sockClientAddr);
#endif

   if (!is_init())
      return(FALSE);

	srvclientSocket = INVALID_SOCKET;

#ifdef OS_WIN32
	srvclientSocket = accept(serverSocket, (LPSOCKADDR)&sockClientAddr,
	                      (LPINT)&len);
#endif

#ifdef OS_UNIX
	srvclientSocket = accept(serverSocket, (sockaddr *)&sockClientAddr, &len);
#endif

	if (srvclientSocket == INVALID_SOCKET)
		return(FALSE);

	return(TRUE);
}

bool ssocket::close_client(void)
{
   /* Close the client socket in use by the server.
      Function returns 'TRUE' upon success, 'FALSE'
      otherwise. */

   if (srvclientSocket == INVALID_SOCKET)
      return(FALSE);

#ifdef OS_WIN32
   if (closesocket(srvclientSocket) == SOCKET_ERROR)
      return(FALSE);
#endif

#ifdef OS_UNIX
   close(srvclientSocket);
#endif

   srvclientSocket = INVALID_SOCKET;
   return(TRUE);
}

bool ssocket::close_all(void)
{
   /* Close all sockets in use by the server.
      Function returns 'TRUE' upon success, 'FALSE'
      otherwise. */

   if (srvclientSocket != INVALID_SOCKET)
#ifdef OS_WIN32
      if (closesocket(srvclientSocket) == SOCKET_ERROR)
         return(FALSE);
#endif

#ifdef OS_UNIX
      close(srvclientSocket);
#endif

#ifdef OS_WIN32
   if (closesocket(serverSocket) == SOCKET_ERROR)
         return(FALSE);
#endif

#ifdef OS_UNIX
   close(serverSocket);
#endif

   return(TRUE);
}

int ssocket::recv_data(string& buf)
{
   /* Receive data from the socket which is assumed
      to be open. Method returns the number of bytes
      read upon success with the received data loaded
      into 'buf', zero otherwise. */

   char *fbuf;
   int charRecv, done = FALSE;

   fbuf = new char[IPC_SR_BUFSIZE];
   buf = "";

   while(!done)
   	{
#ifdef OS_WIN32
      charRecv = recv(srvclientSocket, (LPSTR)fbuf, (IPC_SR_BUFSIZE - 1), NO_FLAGS);
#endif

#ifdef OS_UNIX
      charRecv = recv(srvclientSocket, fbuf, (IPC_SR_BUFSIZE - 1), NO_FLAGS);
#endif

   	if (charRecv == SOCKET_ERROR)
   	   {
         delete fbuf;
         return(0);
         }

		fbuf[charRecv] = EOS;
      buf += fbuf;

   	if (charRecv < (IPC_SR_BUFSIZE - 1))
   	   done = TRUE;
   	}

   delete fbuf;
   return(buf.length());
}

int ssocket::send_data(string& buf)
{
   /* Send data to the socket. Method returns the
		number of bytes sent upon success, zero
      otherwise. */

   string fbuf;
	int len, charSent, pos = 0, bytesToGo, done = FALSE;
	int chunk;

   len = buf.length();

	if (!len)
		return(0);

	// if less than 'IPC_SR_BUFSIZE' to send, use one shot

	if (len < IPC_SR_BUFSIZE)
	   {
      if ((charSent = send(srvclientSocket, buf.c_str(), len, NO_FLAGS)) == SOCKET_ERROR)
		   return(FALSE);

	   return(charSent);
	   }

	bytesToGo = len;

	// send one buffer length at a time

	while(!done)
	   {
	   if (bytesToGo > (IPC_SR_BUFSIZE - 1))
	   	chunk = IPC_SR_BUFSIZE - 1;
	  	else
	  		chunk = bytesToGo;

      fbuf = buf.substr(pos, chunk);

      if ((charSent = send(srvclientSocket, fbuf.c_str(), chunk, NO_FLAGS)) == SOCKET_ERROR)
			{
	   	pos = 0;
	   	break;
	   	}

	   bytesToGo -= chunk;
	   pos += charSent;

	   if (bytesToGo <= 0)
	      done = TRUE;
	   }

	return(pos);
}
