/* net.c
   network and socket maintenance procedures */


     /*---------------------------------------------------------------*/
     /* Xgopher        version 1.1     20 April 1991                  */
     /*                version 1.0     04 March 1991                  */
     /* X window system client for the University of Minnesota        */
     /*                                Internet Gopher System.        */
     /* Allan Tuchman, University of Illinois at Urbana-Champaign     */
     /*                Computing Services Office                      */
     /* Copyright 1992 by                                             */
     /*           the Board of Trustees of the University of Illinois */
     /* Permission is granted to freely copy and redistribute this    */
     /* software with the copyright notice intact.                    */
     /*---------------------------------------------------------------*/


/* The header files here need to be checked and corrected on almost
   a per-machine basis. */

#include <stdio.h>

#ifndef IS_BSD
#include <stdlib.h>
#endif

#include <sys/types.h>
#include <linux/socket.h>
/* #include <netinet/in.h> */

#ifndef IS_A_HPUX
#include <arpa/inet.h>
#endif

#include <netdb.h>
#include <signal.h>
#include <string.h>
#ifdef sun
#include <strings.h>
#endif

#include <ctype.h>
#include <errno.h>
#include <time.h>

#include <sys/param.h>
#include "gopher.h"
#include "globals.h"
#include "list.h"



/* connectToSocket
   performs a connection to socket 'port' on host 'host'.
  
   Errors: 
	   -2 get host failed
	   -3 socket call failed
	   -4 connect call failed
 */

static struct sockaddr_in server;
struct hostent *hostPtr;

/* OS dependencies:

   . gethostname(char *, int) is a Unix kernel call to return the
     address of a string containing the local host name.
   . MAXHOSTNAMELEN is set in <sys/param.h> to be the maximum host name
     size.  This is often 64 for BSD Unix.

*/

int
connectToSocket(host, port)
char *host;
int  port;
{
	/* Host can be a hostname or ip-address.
	   If 'host' is null, the local host is assumed. */

	int	iSock = 0;
	char	hostName[MAXHOSTNAMELEN];

	LOG(logFP, "Connecting to \'%s\' at port %d\n", host, port);
	if (host == '\0') {
		gethostname(hostName, MAXHOSTNAMELEN);
		host = hostName;
	}

	if ((server.sin_addr.s_addr = inet_addr(host)) == -1) {
		if ((hostPtr = gethostbyname(host)) != NULL) {
			bzero((char *) &server, sizeof(server));
			bcopy(hostPtr->h_addr, (char *) &server.sin_addr,
						hostPtr->h_length);
			server.sin_family = hostPtr->h_addrtype;
		} else {
			return (-2);
		}
	} else {
		server.sin_family = AF_INET;
	}

	server.sin_port = (unsigned short) htons(port);

	/* in the socket call, should AF_INET be replaced with 
	   server.sin_family? */

	if ((iSock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
		return (-3);

	setsockopt(iSock, SOL_SOCKET, ~SO_LINGER, 0, 0);
	setsockopt(iSock, SOL_SOCKET, SO_REUSEADDR, 0, 0);
	setsockopt(iSock, SOL_SOCKET, SO_KEEPALIVE, 0, 0);

	if (connect(iSock, (struct sockaddr *) &server, sizeof(server)) < 0) {
		close(iSock);
		return (-4);
	}
	return(iSock);
}


/* networkError
   Checks the return value from connectToSoocket and report any error */

void
networkError(gfd, host, port)
int gfd;
char *host;
{
     char errorString[MESSAGE_STRING_LEN];
#define NET_ERROR_MESSAGE "Cannot establish a network connection"

     switch(gfd)  {
     case -4:
	  if (port != 0)
	      (void) sprintf(errorString,
	        "%s\n(could not connect to remote computer \'%s\' at port %d)",
		    NET_ERROR_MESSAGE, host, port);
	  else
	      (void) sprintf(errorString,
		"%s\n(could not connect to remote computer \'%s\')",
		    NET_ERROR_MESSAGE, host);
          break;
     case -3:
	  (void) sprintf(errorString,
		    "%s\n(because socket failed)",
		    NET_ERROR_MESSAGE);
	  break;
     case -2:
	  (void) sprintf(errorString,
		    "%s\n(could not identify the remote host computer \'%s\')",
		    NET_ERROR_MESSAGE, host);
	  break;
     }
     
     if (gfd < 0) {
	  showError(errorString);
     }
}

/* ----- */

/* Read "n" bytes from a descriptor.
 * Use in place of read() when fd is a stream socket
 *
 * Returns the number of total bytes read.
 */

int readn(fd, ptr, nbytes)
int fd;
char *ptr;
int nbytes;
{
	int nleft, nread;

	nleft = nbytes;
	while (nleft > 0) {
		nread = read(fd, ptr, nleft);
		if (nread < 0)
			return(nread);	/* error, return <0 */
		else if (nread == 0)	/* EOF */
			break;
	
		nleft 	-= nread;
		ptr 	+= nread;
	}
	return(nbytes - nleft);	/* return >= 0) */
}



/*
 * Write "n" bytes to a descriptor.
 * Use in place of write() when fd is a stream socket
 *
 * We return the number of bytes written
 */

int writen(fd, ptr, nbytes)
int	fd;
char	*ptr;
int	nbytes;
{
	int nleft, nwritten;

	nleft = nbytes;
	while(nleft > 0) {
		nwritten = write(fd, ptr, nleft);
		if (nwritten <= 0)
			return(nwritten);	/* error */

		nleft	-= nwritten;
		ptr	+= nwritten;
	}
	return(nbytes - nleft);
}


/*
 * Writestring uses the writen and strlen calls to write a
 * string to the file descriptor fd.  If the write fails
 * a -1 is returned. Otherwise zero is returned.
 */

int writeString(fd, stringptr)
int	fd;
char	*stringptr;
{
	int length;

	length = strlen(stringptr);
	if (writen(fd, stringptr, length) != length)
		return(-1);
	else
		return(0);
}


/*
 * Read a line from a descriptor.  Read the line one byte at a time,
 * looking for the newline.  We store the newline in the buffer,
 * then follow it with a null (the same as fgets(3)).
 * We return the number of characters up to, but not including,
 * the null (the same as strlen(3))
 */

int readLine(fd, ptr, maxlen)
int	fd;
char	*ptr;
int 	maxlen;
{
	int n;
	int rc;
	char c;

	for (n=1; n < maxlen; n++) {
		if ( (rc = read(fd, &c, 1)) == 1) {
			*ptr++ = c;
			if (c == '\n')
				break;
		}
		else if (rc == 0) {
			if (n == 1)
				return(0);	/* EOF, no data read */
			else
				break;		/* EOF, some data was read */
		}
		else
			return(-1);		/* error */
	}

	*ptr = 0; 				/* Tack a NULL on the end */
	return(n);
}

/*
 * Readfield reads data up to a tab, (like readline above)
 */

int readField(fd, ptr, maxlen)
int	fd;
char	*ptr;
int 	maxlen;
{
	int n;
	int rc;
	char c;

	for (n=1; n < maxlen; n++) {
		if ( (rc = read(fd, &c, 1)) == 1) {
			*ptr++ = c;
			if (c == '\t') {
			     *(ptr - 1) = '\0';
				break;
			}
		}
		else if (rc == 0) {
			if (n == 1)
				return(0);	/* EOF, no data read */
			else
				break;		/* EOF, some data was read */
		}
		else
			return(-1);		/* error */
	}

	*ptr = 0; 				/* Tack a NULL on the end */
	return(n);
}


/*
 * readDelim reads data up to a delimiter or EOL (similar to readfield above)
 */

int readDelim(fd, ptr, maxlen, delim)
int	fd;
char	*ptr;
int 	maxlen;
char 	delim;
{
	int n;
	int rc;
	char c;

	for (n=1; n < maxlen; n++) {
		if ( (rc = read(fd, &c, 1)) == 1) {
			*ptr++ = c;
			if (c == delim  ||  c == '\n') {
			     *(ptr - 1) = '\0';
				break;
			}
		}
		else if (rc == 0) {
			if (n == 1)
				return(0);	/* EOF, no data read */
			else
				break;		/* EOF, some data was read */
		}
		else
			return(-1);		/* error */
	}

	*ptr = 0; 				/* Tack a NULL on the end */
	return(n);
}


int
sreadword(input, output, maxlen)
  char *input;
  char *output;
  int maxlen;
{
     int n;
     char c;
     
     for (n=0; n < maxlen; n++) {
	  c = *input++;
	  *output++ = c;
	  if (isspace(c)) {
	       *(output - 1) = '\0';
	       break;
	  }
	  
	  if (c == NULL) {
	       break;
	  }
     }
     
     *output = '\0'; 				/* Tack a NULL on the end */
     return(n);
}


/*
 * zapCRLF removes all carriage returns and linefeeds from a C-string.
 */

void
zapCRLF(inputline)
  char *inputline;
{
     char *cp;

     cp = index(inputline, '\r');    /* Zap CR-LF */
     if (cp != NULL)
	  *cp = '\0';
     else {
	  cp = index(inputline, '\n');
	  if (cp != NULL)
	       *cp = '\0';
     }
}
