/* ***************************************************************** *
 * Copyright 1998 International Business Machines Corporation. All   *
 * Rights Reserved.                                                  *
 *                                                                   *
 * Please read this carefully.  Your use of this reference           *
 * implementation of certain of the IETF public-key infrastructure   *
 * specifications ("Software") indicates your acceptance of the      *
 * following.  If you do not agree to the following, do not install  *
 * or use any of the Software.                                       *
 *                                                                   *
 * Permission to use, reproduce, distribute and create derivative    *
 * works from the Software ("Software Derivative Works"), and to     *
 * distribute such Software Derivative Works is hereby granted to    *
 * you by International Business Machines Corporation ("IBM").  This *
 * permission includes a license under the patents of IBM that are   *
 * necessarily infringed by your use of the Software as provided by  *
 * IBM.                                                              *
 *                                                                   *
 * IBM licenses the Software to you on an "AS IS" basis, without     *
 * warranty of any kind.  IBM HEREBY EXPRESSLY DISCLAIMS ALL         *
 * WARRANTIES OR CONDITIONS, EITHER EXPRESS OR IMPLIED, INCLUDING,   *
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OR CONDITIONS OF       *
 * MERCHANTABILITY, NON INFRINGEMENT AND FITNESS FOR A PARTICULAR    *
 * PURPOSE.  You are solely responsible for determining the          *
 * appropriateness of using this Software and assume all risks       *
 * associated with the use of this Software, including but not       *
 * limited to the risks of program errors, damage to or loss of      *
 * data, programs or equipment, and unavailability or interruption   *
 * of operations.                                                    *
 *                                                                   *
 * IBM WILL NOT BE LIABLE FOR ANY DIRECT DAMAGES OR FOR ANY SPECIAL, *
 * INCIDENTAL, OR  INDIRECT DAMAGES OR FOR ANY ECONOMIC              *
 * CONSEQUENTIAL DAMAGES (INCLUDING LOST PROFITS OR SAVINGS), EVEN   *
 * IF IBM HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.  IBM  *
 * will not be liable for the loss of, or damage to, your records or *
 * data, or any damages claimed by you based on a third party claim. *
 *                                                                   *
 * IBM wishes to obtain your feedback to assist in improving the     *
 * Software.  You grant IBM a world-wide, royalty-free right to use, *
 * copy, distribute, sublicense and prepare derivative works based   *
 * upon any feedback, including materials, error corrections,        *
 * Software Derivatives, enhancements, suggestions and the like that *
 * you provide to IBM relating to the Software (this does not        *
 * include products for which you charge a royalty and distribute to *
 * IBM under other terms and conditions).                            *
 *                                                                   *
 * You agree to distribute the Software and any Software Derivatives *
 * under a license agreement that: 1) is sufficient to notify all    *
 * licensees of the Software and Software Derivatives that IBM       *
 * assumes no liability for any claim that may arise regarding the   *
 * Software or Software Derivatives, and 2) that disclaims all       *
 * warranties, both express and implied, from IBM regarding the      *
 * Software and Software Derivatives.  (If you include this          *
 * Agreement with any distribution of the Software or Software       *
 * Derivatives you will have met this requirement.)  You agree that  *
 * you will not delete any copyright notices in the Software.        *
 *                                                                   *
 * This Agreement is the exclusive statement of your rights in the   *
 * Software as provided by IBM.   Except for the rights granted to   *
 * you in the second paragraph above, You are not granted any other  *
 * patent rights, including but not limited to the right to make     *
 * combinations of the Software with products that infringe IBM      *
 * patents. You agree to comply with all applicable laws and         *
 * regulations, including all export and import laws and regulation. *
 * This Agreement is governed by the laws of the State of New York.  *
 * This Agreement supersedes all other communications,               *
 * understandings or agreements we may have had prior to this        *
 * Agreement.                                                        *
 * ***************************************************************** */

#include "tcp.h"
#include <stdio.h>
#include <Jonah.h>
#include <ApiNotify.h>


bool TCP::wsi = false;

ipAddressV4::ipAddressV4(const char * name) {
  ok = false;;
  setAddress(name);
}

ipAddressV4::ipAddressV4(unsigned long ip) {
  ok = false;;
  setAddress(ip);
}

ipAddressV4::ipAddressV4(void) {
  ok = false;;
}


uint32 ipAddressV4::setAddress(const char * name) {
  struct hostent * he;
  memset(&addr, 0, 4);  
  he = gethostbyname (name);
  if (he == NULL) return COM_BADADDRESS;
  memcpy(&addr, he->h_addr_list[0], 4); 
  ok = true;
  return 0;
}


ipAddressV4::ipAddressV4(unsigned char i1, unsigned char i2, unsigned char i3, unsigned char i4) {
  ok = false;;
  if (setAddress(i1, i2, i3 ,i4) == 0) ok = true;
}

uint32 ipAddressV4::setAddress(unsigned char i1, unsigned char i2, unsigned char i3, unsigned char i4) {
  unsigned char a[4];
  a[0] = i1;
  a[1] = i2;
  a[2] = i3;
  a[3] = i4;
  memcpy(&addr, a, 4);
  ok = true;
  return 0;
}

uint32 ipAddressV4::setAddress(unsigned long ip) {
  addr = ip;
  ok = true;
  return 0;
}

ipAddressV4::~ipAddressV4() {  
  ok = false;
}


uint32 ipAddressV4::getAddress(unsigned long & a) {
  if (!ok) return COM_BADADDRESS;
  a = addr;
  return 0;
}

uint32 ipAddressV4::getAddress(unsigned char & i1, unsigned char & i2, unsigned char & i3, unsigned char & i4) {
  unsigned char a[4];
  if (!ok) return COM_BADADDRESS;
  memcpy(a, &addr, 4);
  i1 = a[0];
  i2 = a[1];
  i3 = a[2];
  i4 = a[3];
  return 0;
}

TCP::TCP(SOCKET ss) {
  WORD wVersionRequested;
  WSADATA wsaData;

  listening = false;
  wVersionRequested = MAKEWORD( 2, 0 ); 
  if (!wsi) {
    error = WSAStartup( wVersionRequested, &wsaData );
    if ( error != 0 ) return;
    wsi = true;
  }
  error = 0;
  s = ss;
  myPort = -1; // Set, but unknown
}

TCP::TCP(void) {
  WORD wVersionRequested;
  WSADATA wsaData;
  listening = false;
  wVersionRequested = MAKEWORD( 2, 0 ); 
  if (!wsi) {
    error = WSAStartup( wVersionRequested, &wsaData );
    if ( error != 0 ) return;
    wsi = true;
  }
  error = 0;
  myPort = 0; // Unset
  s = socket(AF_INET,
             SOCK_STREAM,
             IPPROTO_TCP);
  if (s == INVALID_SOCKET)
    error = WSAGetLastError();
} 
  



uint32 TCP::setLocalPort(unsigned p) {
  struct sockaddr_in sa;

  if (listening) {
    error = COM_WRONGSTATE;
    return error;
  };

  error = 0;

  sa.sin_family = AF_INET;
  sa.sin_port = htons(p);
  sa.sin_addr.s_addr = ADDR_ANY;

  if (bind(s, (struct sockaddr *)&sa, sizeof(sa))) {
    error = WSAGetLastError();
    return error;
  } else myPort = p;
  return 0;
}

unsigned TCP::getLocalPort(void) {
  struct sockaddr_in sa;
  int sa_len;

  if (myPort == -1) {
// Port is set, but not currently known.  Ask WinSock what it is...
    if (getsockname (s, (struct sockaddr *)&sa, &sa_len)) {
      error = WSAGetLastError();
      return 0;
    };
    myPort = ntohs(sa.sin_port);
  } else if (myPort == 0) {
// Port has not been set.  Set it...

    error = 0;

    sa.sin_family = AF_INET;
    sa.sin_port = 0;
    sa.sin_addr.s_addr = ADDR_ANY;

    if (bind(s, (struct sockaddr *)&sa, sizeof(sa))) {
      error = WSAGetLastError();
      return 0;
    };
    if (getsockname (s, (struct sockaddr *)&sa, &sa_len)) {
      error = WSAGetLastError();
      return 0;
    };
    myPort = ntohs(sa.sin_port);
  };

  return myPort;
}

TCP::~TCP() {
  if (s) closesocket(s);
  s = 0;
}

uint32 TCP::open(ipAddressV4 host, unsigned p) {
  struct sockaddr_in sa;
  int sa_len;

  if (listening) {
    error = COM_WRONGSTATE;
    return error;
  };
  
  if (myPort == 0) {
// We've not yet bound to a local address, so do so now...
    sa.sin_family = AF_INET;
    sa.sin_port = 0;
    sa.sin_addr.s_addr = ADDR_ANY;

    if (bind(s, (struct sockaddr*)&sa, sizeof(sa))) {
      error = WSAGetLastError();
      return error;
    };
  };
  
  sa.sin_family = AF_INET;
  sa.sin_port = htons(p);
  host.getAddress(sa.sin_addr.s_addr);
  sa_len = sizeof(sa);
  if (connect(s, (struct sockaddr *)&sa, sizeof(sa))) {
    error = WSAGetLastError();
    return error;
  } else if (getsockname (s, (struct sockaddr *)&sa, &sa_len)) {
    error = WSAGetLastError();
    return error;
  } else myPort = ntohs(sa.sin_port);

  return 0;
}


uint32 TCP::close(void) {
  if (s) closesocket(s);
  s = 0;
  listening = false;
  return 0;
}

uint32 TCP::send(const unsigned char * data, size_t data_len) {
  unsigned sent = 0;
  int ds;
  char message[BUFSIZ];
  if (listening) {
    error = COM_WRONGSTATE;
    return error;
  };
  sprintf(message, "Sending %d bytes", data_len);
  //  ApiDisplay(DISPLAY_LOGDEBUG, (utf8String) message);
  while (sent < data_len) {
    if ((ds = ::send(s, (const char *)data+sent, data_len-sent, 0)) == SOCKET_ERROR) {
      error = WSAGetLastError();
      return error;
    } else sent += ds;
    sprintf(message, "Sent %d bytes in last chunk total = %d", ds, sent);
    //    ApiDisplay(DISPLAY_LOGDEBUG, (utf8String) message);
  };
  return 0;
}

uint32 TCP::send(const unsigned char * data, size_t data_len, unsigned timeout) {
  unsigned sent = 0;
  int ds;
  char message[BUFSIZ];

  fd_set writeset;
  struct timeval timeoutval; 
  timeoutval.tv_usec = timeout % 1000;
  timeoutval.tv_sec = timeout / 1000;

  if (listening) {
    error = COM_WRONGSTATE;
    return error;
  };
  sprintf(message, "Sending %d bytes", data_len);
  //  ApiDisplay(DISPLAY_LOGDEBUG, (utf8String) message);
  while (sent < data_len) {
    FD_ZERO(&writeset);
    FD_SET(s, &writeset);
    if ( select (0, NULL, &writeset, NULL, &timeoutval) == 0) return COM_TIMEDOUT;
    if (( ds = ::send(s, (const char *)data+sent, data_len-sent, 0)) == SOCKET_ERROR) {
      error = WSAGetLastError();
      return error;
    } else sent += ds;
    sprintf(message, "Sent %d bytes in last chunk total = %d", ds, sent);
    //    ApiDisplay(DISPLAY_LOGDEBUG, (utf8String) message);
  };
  
  return 0;
}

uint32 TCP::rcv(unsigned char * buffer, size_t buf_len, size_t & data_len) {
  int ds;
  if (listening) {
    error = COM_WRONGSTATE;
    return error;
  };

  if ((ds = recv(s, (char *)buffer, buf_len, 0)) == SOCKET_ERROR) {
    error = WSAGetLastError();
    return error;
  };
  data_len = ds;
  return 0;
} 
 

uint32 TCP::rcv(unsigned char * buffer, size_t buf_len, size_t & data_len, 
                unsigned timeout) {
  int ds;

  fd_set readset;
  struct timeval timeoutval; 
  
  if (listening) {
    error = COM_WRONGSTATE;
    return error;
  };
  
  FD_ZERO(&readset);
  FD_SET(s, &readset);
  timeoutval.tv_usec = timeout % 1000;
  timeoutval.tv_sec = timeout / 1000;
  if ( select (0, &readset, NULL, NULL, &timeoutval) == 0) return COM_TIMEDOUT;
 
  if ((ds = recv(s, (char *)buffer, buf_len, 0)) == SOCKET_ERROR) {
    error = WSAGetLastError();
    return error;
  };
  
  data_len = ds;
  return 0;
}

uint32 TCP::listen(void) {

  if (listening) {
    error = COM_WRONGSTATE;
    return error;
  };
  
  if (::listen(s, SOMAXCONN)) {
    error = WSAGetLastError();
    return error;
  }

  if (myPort == 0) myPort = -1;
  listening = true;
  
  return 0;
}

TCP * TCP::accept(void) {
  SOCKET s1;
  int r;
  TCP * con;
  struct sockaddr_in sa;
  int sas;
  
  if (!listening) {
    error = COM_WRONGSTATE;
    return NULL;
  };
  
  sas = sizeof(sa);

  if ((r = ::accept(s, (struct sockaddr *)&sa, &sas)) == INVALID_SOCKET) {
    error = WSAGetLastError();
    return NULL;
  };
  s1 = r;
  con = new TCP(s1);
  return con;
}

uint32 TCP::listen(unsigned p) {
  struct sockaddr_in sa;

  if (listening) {
    error = COM_WRONGSTATE;
    return error;
  };

  sa.sin_family = AF_INET;
  sa.sin_port = htons(p);
  sa.sin_addr.s_addr = ADDR_ANY;
  //  ApiDisplay(DISPLAY_LOGDEBUG, (utf8String)  "In listen");
  if (bind(s, (struct sockaddr*)&sa, sizeof(sa))) {
    error = WSAGetLastError();
    return error;
  }
  myPort = p;

  if (::listen(s, SOMAXCONN)) {
    error = WSAGetLastError();
    return error;
  };
  listening = true;
  return 0;
}

