/* TCP Network Libary -- Implementation of Libary

   BY: Kevin A. Hoogheem 
   Saint Mary's University of Minnesota
   
*/

#define TCPLIB_M


#ifndef TCPLIB_H
#import "TCPLib.h"
#endif

@implementation TCPNetwork

+ new
{
/*
 * Purpose: To create a new Network object.
 *
 * Input:
 *
 * Output:
 *
 * Return:
 *
 * Side Effects: 
 */		

	id newInstance = [super new];
	[newInstance initialize];

return newInstance;
}

+ new: (int)sock_value
{
/*
 * Purpose: To create a new Network object.
 *
 * Input:
 *
 * Output:
 *
 * Return:
 *
 * Side Effects: 
 */		

	id newInstance = [super new];
	[newInstance initialize:sock_value];

return newInstance;
}

- initialize
{
/*
 * Purpose:  Set the Sock to a initial value
 *
 * Input: Nothing
 *
 * Output:
 *
 * Return:  True if all works - should always work 
 *
 * Side Effects: Sock  set to Initvalue
 */		
	
	sock = INITVAL;
	
return(self);
}

- initialize: (int)sock_val
{
/*
 * Purpose:  Set the sock to the sock_val
 *
 * Input: Nothing
 *
 * Output:
 *
 * Return:  True if all works - should always work 
 *
 * Side Effects: sock set to the sock_val
 */		
	
	sock = sock_val;
	
return(self);
}


- (int)open_port: (int)port
{
/*
 * Purpose: Open a new network port, to be able to send traffic on it.
 *          User only chooses the port to open we will default to INET.
 *
 * Input: An integer for what port you want open.
 *
 * Output: NONE, unless errors in creating the socket and bind the 
 *         sockets occur.
 *
 * Return: TRUE if all goes well :)-  
 *
 * Side Effects: A new port on the network is opened up for communications.
 */		
	
	struct sockaddr_in sa;
	
	if ((sock = socket (AF_INET, SOCK_STREAM, 0)) < 0){
		perror("Socket Creation Error");
    	return(FALSE);
	}

	bzero( (char *) &sa, sizeof(sa));
	sa.sin_family        = AF_INET;
	sa.sin_addr.s_addr   = htonl(INADDR_ANY);
	sa.sin_port          = htons(port);
		
	if (bind(sock,(struct sockaddr *) &sa, sizeof(sa)) < 0){
		perror("Bind Error");
		return(FALSE);
	}

	listen(sock, MAXLISTENS);

	
return(TRUE);
}

- (int)close_network
{
/*
 * Purpose: To close the network so no traffic can be allow on it.
 *
 * Input: 
 *
 * Output: NONE
 *
 * Return: TRUE if all goes well.
 *
 * Side Effects: The network is closed.
 */		
	
	close(sock);

return(TRUE);
}

- (int)call: (char *)machine_name on_port: (int)port
{
/*
 * Purpose: To call a networked machine on a specified port.
 *          Allows you to use anything for the other machines name
 *          ie.. either his actual name or his ip address.
 *
 * Input:  A remote machine name and its port you want to contact.
 *
 * Output:
 *
 * Return: TRUE if all goes well ;)-
 *
 * Side Effects: Opens a channel of communication to a remote machine.
 */		

	struct sockaddr_in  sa;
	struct hostent      *hp;
	struct in_addr      *addr_ptr;
	char                *temp_ip = '\0';
	
	strcpy(temp_ip, machine_name);
	
	if (isdigit( *(temp_ip + 1)) ){
		bzero((char *) &sa, sizeof(sa));
		sa.sin_family	      =AF_INET;
		sa.sin_addr.s_addr    =inet_addr(machine_name);
		sa.sin_port	          =htons(port);
		
		if ( (sock = socket(AF_INET, SOCK_STREAM, 0)) < 0){
			perror("Socket Creation Error");
			return(FALSE);
	    }
		
	}else{
		if ( (hp = gethostbyname(machine_name) ) == NULL){
			perror("Get host name error");
			return(FALSE);
		}else{
			addr_ptr = (struct in_addr*) *(hp->h_addr_list);
			machine_name = inet_ntoa(*addr_ptr);
			}
		bzero((char *) &sa, sizeof(sa));
		bcopy(hp->h_addr,(char *)&sa.sin_addr,hp->h_length);
		sa.sin_family	           =hp->h_addrtype;
		sa.sin_port	               =htons(port);
		
		if ( (sock = socket(sa.sin_family, SOCK_STREAM, 0)) < 0){
			perror("Socket Creation Error");
			return(FALSE);
		}
	}		
		
			 
	if (connect(sock, (struct sockaddr *) &sa, sizeof(sa)) < 0){
		perror("Connection Error");
		return(FALSE);
	}

return(TRUE);
}

- (int)call: (char *)machine using: (char *)protocol
{
/*
 * Purpose:  To call a networked machine using a well known protocol.
 *
 * Input: A remote machine name and the protcol you wish to use in 
 *        contacting it.  From /etc/services
 *
 * Output:
 *
 * Return:
 *
 * Side Effects: 
 */		

// not needed at this time.

return self;
}

- (int)send_text: (char *)message
{
/*
 * Purpose: To send desired text message. (must find its own length)
 *
 * Input: A character String message.
 *
 * Output:
 *
 * Return: Return How much was writen or True if all works well.
 *
 * Side Effects: 
 */		
	int length, written;

	length = strlen(message)+1;

	written = write(sock, message ,length);
	if (written != length){
		perror("Send_Text Error");
		return(written);
	}

return(TRUE);
}

- (int)send_text: (char *)message of_length: (int)length
{
/*
 * Purpose:  Send a (length) fixed message.
 *
 * Input: A character string message and its length.
 *
 * Output:
 *
 * Return: On Error return how much was writen to the Socket. Or 0 if works.
 *
 * Side Effects: 
 */		
	int  nleft, nwritten;
		
	nleft = length;
	while ( nleft > 0 ){
		nwritten = write(sock, message, nleft);
		if( nwritten <= 0 )
		return(nwritten);
			
		nleft   -= nwritten;
		message	+= nwritten;	
	}
		
return (length - nleft);
}


- (int)send_file: (char *)file
{
/*
 * Purpose: To send a file accrose the network.
 *
 * Input:  The name of a file.
 *
 * Output:
 *
 * Return: An int representing an error while trying to access the file.
 *
 * Side Effects: 
 */		

return self;
}

- (int)readline: (char *)ptr of_length: (int)maxlen
{
/*
*  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		n, rc;
	char    c;
			
	for (n=1; n < maxlen; n++){
			if ( (rc = read(sock, &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;

return (n);
}


- (int)receive
{
/*
 * Purpose:  Used to set up a client user allow
 *
 * Input:
 *
 * Output:
 *
 * Return: the new sock for the client to activate a new network obj.
 *
 * Side Effects: Accepts on a socket.
 */	

	struct sockaddr_in sa;
	int length;
	int t;
	
	length = sizeof(sa);
	
	if ((t = accept(sock,(struct sockaddr *)&sa, &length)) < 0)  {
	  perror("tcp error accept");
	  return(FALSE);
	}
	if(DEBUG)
		fprintf(stderr,"Connection from: %s\n",inet_ntoa(sa.sin_addr));

return(t);	
}

- (int)recv_text: (char *)message
{
/*
 * Purpose:
 *
 * Input:
 *
 * Output:
 *
 * Return:
 *
 * Side Effects: 
 */		
	int length, nread;

	length = strlen(message)+1;

	nread = read(sock, message ,length);
	if (nread != length){
		perror("Recv_Text Error");
		return(nread);
	}

return(TRUE);
}

- (int)recv_text: (char *)message of_length: (int)length
{
/*
 * Purpose:
 *
 * Input:
 *
 * Output:
 *
 * Return:
 *
 * Side Effects: 
 */		
	int  nleft, nread;
		
	nleft = length;
		
	while ( nleft > 0 ){
		nread = read(sock, message, nleft);
		if ( nread < 0 )
			return(nread);
		else
		  if ( nread == 0 )
		  break;
					
		nleft    -= nread;
		message  += nread;
	}
		
return (length - nleft);

}

- recv_file: (char *)file
{
/*
 * Purpose:
 *
 * Input:
 *
 * Output:
 *
 * Return:
 *
 * Side Effects: 
 */		


return(self);
}

- data_waiting
{
/*
 * Purpose: Sit and wait for some data to arrive over the network.
 *
 * Input:
 *
 * Output:
 *
 * Return:
 *
 * Side Effects: 
 */		

return(self);
}

- call_waiting
{
/*
 * Purpose: Sit and wait for a connection to occure.
 *
 * Input:
 *
 * Output:
 *
 * Return:
 *
 * Side Effects: 
 */		

return(self);
}

- get_remote_system_name;
{
/*
 * Purpose:
 *
 * Input:
 *
 * Output:
 *
 * Return:
 *
 * Side Effects: 
 */		

return(self);
}

- get_remote_user_name;
{
/*
 * Purpose:
 *
 * Input:
 *
 * Output:
 *
 * Return:
 *
 * Side Effects: 
 */		

return(TRUE);
}

- (int)get_service_port: (char *)service service_type: (int)type
{
/*
 * Purpose:  To get the port number of the service requested.
 *
 * Input: The service name and its type either DGRAM or STREAM
 *
 * Output:
 *
 * Return:
 *
 * Side Effects: 
 */		

// not needed at this time.

return(TRUE);
}

- print
{
/*
 * Purpose:  To debug the values of the object
 *
 * Input:  NOTHING
 *
 * Output:  Values of object
 *
 * Return: 
 *
 * Side Effects: NONE
 */		

	fprintf(stderr, "Value of Sock: %d\n", sock);
	
return(self);	
}

@end
