/*
_____       _    _    Corso   Italia,  178
(_|__   .  (_   |_|_  56125           Pisa
(_|_) |)|(()_)()| |   tel.  +39  050 46380
  |   |               picosoft@picosoft.it

 picoiiop - network support for picoSQL

 Copyright (C) Picosoft s.r.l. 1994-2002

 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
 License as published by the Free Software Foundation; either
 version 2.1 of the License, or (at your option) any later version.

 This library 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
 Lesser General Public License for more details.

 You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
# ifdef WIN32
# include <io.h>
# include <windows.h>
# else
# include <unistd.h>
# endif
# include <stdlib.h>
# include <stdio.h>
# include <fcntl.h>
# include <errno.h>
# include <string.h>
# include "iiopobj.h"
# include "connectn.h"
static char rcsid[] = "$Id: connectn.c,v 1.3 2002/05/06 10:18:26 picoSoft Exp $";
static char rcsidh[] = connectn_h;

static struct timeval *defaultTimeout = 0;
static struct timeval timeOut;

static void
Connection_close (Connection *this)
{
   LINGER linger;
   int rc;

   //
   //  Enable linger with a timeout of zero.  This will
   //  force the hard close when we call closesocket().
   //
   //  We ignore the error return from setsockopt.  If it
   //  fails, we'll just try to close the socket anyway.
   //

   linger.l_onoff  = 1;
   linger.l_linger = 0;
    
   if (this->type == Connection_SOCKET && this->sock != INVALID_SOCKET) {
       setsockopt (this->sock,
                   SOL_SOCKET,
                   SO_LINGER,
                   (char *)&linger,
                   sizeof(linger) );

      //
      //  Close the socket.
      //

      rc = closesocket( this->sock );
      this->sock = INVALID_SOCKET;
   }
}

Connection *
Connection_new(IiopObj *ob)
{
   Connection *this = malloc(sizeof(Connection));
   if (this) {
      this->fd = -1;
      this->obj = ob;
      this->sock = SOCKET_ERROR;
      this->type = Connection_PIPE;
   }
   return this;
}
      
void
Connection_delete(Connection *this)
{
   if (this) {
      Connection_close (this);
      free (this);
   }
}

CORBA_Boolean
Connection_recv (Connection *this, Exception *ex, char *buffer, int len)
{
   int rc;
   int i;

   if (this->type == Connection_PIPE) {
      for (i=0;
           i < len && (rc = read (0, &buffer[i], len - i)) > 0;
           i += rc)
         ;
   } else if (this->type == Connection_SOCKET) {
      for (i=0;
           i < len && (rc = recv (this->sock, &buffer[i], len - i, 0)) > 0;
           i += rc)
         ;
   } else {
      Exception_set (ex, ErrorCode_COMM_ERROR, "invalid connection status");
      return CORBA_False;
   }
   if (i != len) {
      char error[64];
      if ( errno != 0) {
         sprintf (error, "Error reading connection %d\n", errno);
         Exception_set (ex, ErrorCode_COMM_ERROR, error);
      } else {
         sprintf (error, "expected %d, red %d\n", len, i);
         Exception_set (ex, ErrorCode_COMM_ERROR, error);
      }
      return CORBA_False;
   } else
      return CORBA_True;
}

CORBA_Boolean Connection_send (Connection *this, Exception *ex,
                               char *buffer, int len)
{
   int rc;

   if (this->type == Connection_PIPE) {
      rc = write (1, buffer, len);
   } else if (this->type == Connection_SOCKET) {
      rc = send (this->sock, buffer, len, 0);
   } else {
      Exception_set (ex, ErrorCode_COMM_ERROR, "invalid connection status");
      return CORBA_False;
   }

   if (rc != len) {
      char error[64];
      sprintf (error, "Error writing on connection %d\n", errno);
      Exception_set (ex, ErrorCode_COMM_ERROR, error);
      return CORBA_False;
   } else
      return CORBA_True;
}

static void
SocketError(char *m, Exception *ex)
{
   char error[64];
   sprintf (error, "Error on connection %d,%d\n", GetErrno(), GetHErrno());
   Exception_set (ex, ErrorCode_COMM_ERROR, error);
}

static struct hostent *
GetHost(char *name)
{
   struct hostent *Return = (struct hostent *)0;

   struct in_addr addr;
   if ((long)(addr.s_addr = inet_addr(name)) == -1)
      Return = gethostbyname(name);
   else
      Return = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);
   return Return;
}

CORBA_Boolean
Connection_connect (Connection *this, Exception *ex)
{
   CORBA_Boolean Return = CORBA_False;

   if ((this->hp = GetHost((char *)(this->obj->host))) == 0)
      SocketError("GetHost", ex);
   else
      if ((this->sock = socket (AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
         SocketError("socket", ex);
      else {
         FD_ZERO(&this->idSet);
         FD_SET(this->sock, &this->idSet);
         memset (&this->sin, 0, sizeof(this->sin));
         memcpy (&this->sin.sin_addr, this->hp->h_addr, this->hp->h_length);
         this->sin.sin_port = htons(this->obj->port);
         this->sin.sin_family = this->hp->h_addrtype;
         if (connect(this->sock,
                     (struct sockaddr *)&this->sin,
                      sizeof(this->sin))==0) {
            this->type = Connection_SOCKET;
            Return = CORBA_True;
         } else
            SocketError("connect", ex);
      }
   return Return;
}

CORBA_Boolean
Connection_listen (Connection *this, Exception *ex)
{
   CORBA_Boolean Return = CORBA_True;
   return Return;
}

# ifdef WIN32
const unsigned int WM_SOCKET_SELECT = WM_USER + 100;
const WORD VERSION_REQUIRED = 0x101;  // winsock version we need
# endif

CORBA_Boolean
Connection_init (Exception *ex)
{
   CORBA_Boolean Return = CORBA_True;
# ifdef WIN32
   WSADATA wsaData;   
   if (WSAStartup(VERSION_REQUIRED, &wsaData) == 0)
      Return = CORBA_False;
# endif
   return Return;
}

CORBA_Boolean
Connection_exit (Exception *ex)
{
   CORBA_Boolean Return = CORBA_True;
# ifdef WIN32
   WSACleanup();
# endif
   return Return;
}

