//
// Connection.cc
//
// (c) Copyright 1993, San Diego State University -- College of Sciences
//       (See the COPYRIGHT file for more Copyright information)
//
// Implementation of the Connection class
//

#include "Connection.h"
#include <errno.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <unistd.h>


Connection::Connection()
{
	sock = -1;
	pos = pos_max = 0;
}


//*************************************************************************
// Connection::Connection(int socket)
// PURPOSE:
//   Create a connection from just a socket.
// PARAMETERS:
//   int socket:  obvious!!!!
//
Connection::Connection(int socket)
{
	sock = socket;

	int length = sizeof(server);
	if (getpeername(socket, (struct sockaddr *)&server, &length) < 0)
	{
		perror("getpeername");
	}
	pos = pos_max = 0;
}


Connection::~Connection()
{
	this->close();
}


int Connection::open(int priv)
{
	if (priv)
	{
		int	aport = IPPORT_RESERVED - 1;

		sock = rresvport(&aport);
	}
	else
		sock = socket(AF_INET, SOCK_STREAM, 0);

	if (sock == NOTOK)
		return NOTOK;

	int	on = 1;
	setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on));
	server.sin_family = AF_INET;

	pos = pos_max = 0;
	return OK;
}


int Connection::close()
{
	if (sock >= 0)
	{
		int ret = ::close(sock);
		sock = -1;
		return ret;
	}
	return NOTOK;
}


int Connection::assign_port(int port)
{
	server.sin_port = htons(port);
	return OK;
}


int Connection::assign_port(char *service)
{
	struct servent		*sp;

	sp = getservbyname(service, "tcp");
	if (sp == NULL)
	{
		return NOTOK;
	}
	server.sin_port = sp->s_port;
	return OK;
}

int Connection::assign_server(dword addr)
{
	server.sin_addr.s_addr = addr;
	return OK;
}

int Connection::assign_server(char *name)
{
	struct hostent		*hp;

	hp = gethostbyname(name);
	if (hp == NULL)
	{
		return NOTOK;
	}
	memcpy((char *)&server.sin_addr, (char *)hp->h_addr, hp->h_length);
	return OK;
}


int Connection::connect()
{
	if (::connect(sock, (struct sockaddr *)&server, sizeof(server)) == NOTOK)
	{
		return NOTOK;
	}
	return OK;
}


int Connection::bind()
{
	if (::bind(sock, (struct sockaddr *)&server, sizeof(server)) == NOTOK)
	{
		return NOTOK;
	}
	return OK;
}


int Connection::get_port()
{
	int	length;

	if (getsockname(sock, (struct sockaddr *)&server, &length) == NOTOK)
	{
		return NOTOK;
	}
	return ntohs(server.sin_port);
}


int Connection::listen(int n)
{
	return ::listen(sock, n);
}


Connection *Connection::accept(int priv)
{
	int	newsock;

	while (TRUE)
	{
		newsock = ::accept(sock, (struct sockaddr *)0, (int *)0);
		if (newsock == NOTOK && errno == EINTR)
			continue;
		break;
	}
	if (newsock == NOTOK)
		return (Connection *)0;

	Connection	*newconnect = new Connection;
	newconnect->sock = newsock;

	int length = sizeof(newconnect->server);
	getpeername(newsock, (struct sockaddr *)&newconnect->server, &length);

	if (priv && newconnect->server.sin_port >= IPPORT_RESERVED)
	{
		delete newconnect;
		return (Connection *)0;
	}

	return newconnect;
}


//*************************************************************************
// Connection *Connection::accept_privileged()
// PURPOSE:
//   Accept  in  incoming  connection  but  only  if  it  is  from a
//   privileged port
//
Connection * Connection::accept_privileged()
{
	return accept(1);
}


//*************************************************************************
// METHOD int Connection::write(char *buffer, int length)
// PURPOSE:
//   Write  <nbytes>  bytes  from  <buffer>  to file sock. This will
//   take  care  of  problems where write will return without having
//   written everything in the buffer.
// PARAMETERS:
//   char	*buffer:	The buffer to write.
//   int	nbytes:		The number of bytes to write from <buffer>.
// RETURN VALUE:
//   Returns  number  of  bytes  actually  written.  If there was an
//   error it returns -1.
// SIDE EFFECTS:
//   None
// ASSUMPTIONS:
//   None
// FUNCTIONS USED:
//   write()
// ALGORYTHM:
//   Stevens p. 279
//
int Connection::write(char *buffer, int length)
{
	int	nleft, nwritten;

	nleft = length;
	while (nleft > 0)
	{
		nwritten = ::write(sock, buffer, nleft);
		if (nwritten <= 0)
			return nwritten;
		nleft -= nwritten;
		buffer += nwritten;
	}
	return length - nleft;
}


//*************************************************************************
// METHOD int Connection::write(char *buffer)
// PURPOSE:
//   This is identical to the other write except that the length of the
//   buffer is not passed.  The buffer is assumed to be NULL terminated.
// PARAMETERS:
//   char	*buffer:	The buffer to write.
// RETURN VALUE:
//   Returns  number  of  bytes  actually  written.  If there was an
//   error it returns -1.
//
int Connection::write(char *buffer)
{
	return write(buffer, strlen(buffer));
}


//*************************************************************************
// int Connection::read(char *buffer, int length)
// PURPOSE:
//   Read  <length>  bytes  from the current TCP connection. We will
//   not  return  until  we  have  all  the  data we are waiting for
//   unless there was an error
// PARAMETERS:
//   char *buffer:	Buffer to read the data into
//   int length:	The number of bytes to read into the buffer
// RETURN VALUE:
//   The actual number of bytes read.  NOTOK if an error occured.
// ASSUMPTIONS:
//   The connection has been previously established.
// FUNCTIONS USED:
//   read()
// ALGORYTHM:
//   Stevens p. 279
//
int Connection::read(char *buffer, int length)
{
	int	nleft, nread;

	nleft = length;
	while (nleft > 0)
	{
		nread = ::read(sock, buffer, nleft);
		if (nread < 0 && errno == EINTR)
			continue;
		else if (nread < 0)
		{
			perror("read");
			return nread;
		}
		else if (nread == 0)
			return -1;			// End of file/stream

		nleft -= nread;
		buffer += nread;
	}
	return length - nleft;
}


//***************************************************************************
// char *Connection::read_line(char *buffer, int maxlength)
// PURPOSE:
//   Read a line of text, terminated by CR and or LF.
//
char *Connection::read_line(char *buffer, int maxlength)
{
	while (maxlength > 0)
	{
		int	ch = get_char();
		if (ch < 0)
			return (char *) 0;
		else if (ch == '\r')
			continue;
		else if (ch == '\n')
			break;
		else
		{
			*buffer++ = ch;
			maxlength--;
		}
	}
	*buffer = '\0';
	return buffer;
}


//*************************************************************************
// int Connection::read_partial(char *buffer, int maxlength)
// PURPOSE:
//   Read  at  most  <maxlength>  from  the  current TCP connection.
//   This  is  equivalent  to  the  workings  of the standard read()
//   system call
// PARAMETERS:
//   char *buffer:	Buffer to read the data into
//   int maxlength:	Maximum number of bytes to read into the buffer
// RETURN VALUE:
//   The actual number of bytes read in.
// ASSUMPTIONS:
//   The connection has been previously established.
// FUNCTIONS USED:
//   read()
//
int Connection::read_partial(char *buffer, int maxlength)
{
	return ::read(sock, buffer, maxlength);
}


//*************************************************************************
// int Connection::bytes_available()
// PURPOSE:
//   Check  to  see  how  many bytes can be read from the connection
//   currently
// RETURN VALUE:
//   The number of bytes available.
//
int Connection::bytes_available()
{
	int	count = -1;

	ioctl(sock, FIONREAD, (char *) &count);

	return count;
}


//*************************************************************************
// char * Connection::socket_as_string()
// PURPOSE:
//   Return  the  numeric  ASCII  equivalent  of  the socket number.
//   This is needed to pass the socket to another program
//
char * Connection::socket_as_string()
{
	char	buffer[20];

	sprintf(buffer, "%d", sock);
	return strdup(buffer);
}


//*************************************************************************
// int Connection::get_socket()
// PURPOSE:
//   Return the current socket number
//
int Connection::get_socket()
{
	return sock;
}


//*************************************************************************
// int Connection::isopen()
// PURPOSE:
//   Return true if the connection is open
//
int Connection::isopen()
{
	return sock >= 0;
}


//*************************************************************************
// int Connection::get_char()
//
int Connection::get_char()
{
	if (pos >= pos_max)
	{
		pos_max = read_partial(buffer, BUFFER_SIZE);
		pos = 0;
		if (pos_max <= 0)
			return -1;
	}
	return buffer[pos++];
}


