/* Low level TCP socket routines. These routines are used by both
   client and server API's.
   Rick Smereka, Copyright (C) 1998-2006.

   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 CodeWarrior V4 under Windows 32bit.
   Dec/98, Rick Smereka

   Ported to HP-UX under GNU C 2.8.1.
   Jan/99, Rick Smereka

   Ported to Red Hat Linux 5.2, Jul/99, Rick Smereka

   Ported to Debian Linux. Nov/2002, Rick Smereka

   Changed 'ipc_send' to send the data length before the actual data
   and changed 'ipc_recv' to receive the data length before the
   actual data. Feb/2006, Rick Smereka

   Commented out calls to 'log_file_date' unless there is an error.
   Sep/2006, Rick Smereka */

#include "stdhead.h"
#include "flsocket.h"
#include "ipcomm.h"

#ifdef OS_WIN32
int ipc_recv(SOCKET sock, char *buf)
#endif

#ifdef OS_UNIX
int ipc_recv(int sock, char *buf)
#endif
{
   /* Receive up to 'IPC_MAXREC' - 1 bytes from the
      socket which is assumed to be open. Function
      returns the number of bytes read upon success,
      zero otherwise. */

   char mname[] = "ipc_recv", *fbuf;
   int charRecv, len, pos = 0, done = FALSE;

   if ((fbuf = (char *)malloc(IPC_SR_BUFSIZE)) == (char *)NULL)
      {
      log_file_date("%s:alloc fail[fbuf]", mname);
      return(FALSE);
      }

   // receive fixed size header wich must contain length of data

#ifdef OS_WIN32
   charRecv = recv(sock, (LPSTR)fbuf, IPC_HEAD_SIZE, NO_FLAGS);
#endif

#ifdef OS_UNIX
   charRecv = recv(sock, fbuf, IPC_HEAD_SIZE, NO_FLAGS);
#endif

   if (charRecv == SOCKET_ERROR)
      {
      free(fbuf);
      log_file_date("%s:bad rc[-1] from recv[header]", mname);
      return(0);
      }
   
   if (charRecv != IPC_HEAD_SIZE)
      {
      free(fbuf);
      log_file_date("%s:received improper header size", mname);
      return(0);
      }

   fbuf[IPC_HEAD_SIZE] = EOS;
   // log_file_date("%s:recv header=%s[%d]", mname, fbuf, strlen(fbuf));

   if (!qatoi(fbuf, &len))
      {
      free(fbuf);
      log_file_date("%s:header length is non-numeric", mname);
      return(0);
      }

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

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

   	if (charRecv == SOCKET_ERROR)
   	   {
   	   free(fbuf);
           log_file_date("%s:bad rc[-1] from recv", mname);
           return(0);
           }

	fbuf[charRecv] = EOS;
	// log_file_date("%s:received %d byte(s)", mname, charRecv);

	if ((strlen(fbuf) + strlen(buf)) >= IPC_MAXREC)
	   {
	   log_file_date("%s:too much data", mname);
	   free(fbuf);
	   return(0);
	   }

         strncpy(&buf[pos], fbuf, charRecv);

   	 if (charRecv > 0)
   	    pos += charRecv;

   	 buf[pos] = EOS;

         // we are done when we have received all bytes according to header

   	 if (pos >= len)
   	    done = TRUE;
   	 }

   free(fbuf);
   // log_file_date("%s:received total of %d byte(s)", mname, pos);
   return(pos);
}

#ifdef OS_WIN32
int ipc_send(SOCKET sock, char *buf)
#endif

#ifdef OS_UNIX
int ipc_send(int sock, char *buf)
#endif
{
   /* Send data to the socket. Function returns the
      number of bytes sent upon success, zero
      otherwise. */

   char *fbuf, header[IPC_HEAD_SIZE + 1], mname[] = "ipc_send";
   int len, charSent, pos = 0, bytesToGo, done = FALSE;
   int chunk;

   len = strlen(buf);

   if (!len)
      return(0);

   // send fixed size header first consisting of data size

   sprintf(header, "%010d", len);
   // log_file_date("%s:header to send is %s[%d]", mname, header, strlen(header));

   if ((charSent = send(sock, header, IPC_HEAD_SIZE, NO_FLAGS)) ==
        SOCKET_ERROR)
      {
      log_file_date("%s:bad rc[%d] from send[header]", mname, charSent);
      return(FALSE);
      }

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

   if (len < IPC_SR_BUFSIZE)
      {
      if ((charSent = send(sock, buf, len, NO_FLAGS)) == SOCKET_ERROR)
         {
         log_file_date("%s:bad rc[%d] from send", mname, charSent);
         return(FALSE);
         }

      return(charSent);
      }

   if ((fbuf = (char *)malloc(IPC_SR_BUFSIZE)) == (char *)NULL)
      {
      log_file_date("%s:alloc fail[fbuf]", mname);
      return(FALSE);
      }

   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;

      strncpy(fbuf, &buf[pos], chunk);
      fbuf[chunk] = EOS;

      if ((charSent = send(sock, fbuf, chunk, NO_FLAGS)) == SOCKET_ERROR)
         {
         pos = 0;
         log_file_date("%s:bad rc[%d] from send", mname, charSent);
         break;
         }

      bytesToGo -= chunk;
      pos += charSent;

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

   free(fbuf);
   //log_file_date("ipc_send:sent total of %d byte(s)", pos);
   return(pos);
}
