// Filename:   bbsipc.C
// Contents:   the bbs IPC object
// Author:     Greg Shaw
// Created:        6/13/93

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

In addition to the permissions in the GNU General Public License, the
Free Software Foundation gives you unlimited permission to link the
compiled version of this file with other programs, and to distribute
those programs without any restriction coming from the use of this
file.  (The General Public License restrictions do apply in other
respects; for example, they cover modification of the file, and
distribution when not linked into another program.)

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

You should have received a copy of the GNU General Public License
along with this program; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

#ifndef _BBSIPC_C_
#define _BBSIPC_C_

#undef DEBUG

#include "bbshdr.h"             // include *EVERYTHING*

// Function:   bbsipc constructor
// Purpose:    initialize the bbs ipc object.
// Inputs: none
// Outputs:    none - constructor may not return values
// Author: Greg Shaw
// Created:    6/13/93

bbsipc::bbsipc()
{
	server = 0;                 // server mode
	hostname[0] = 0;            // host name
	sock_open = 0;              // open socket
	sock_fd = 0;                // socket file descriptor
	serv_sock_fd = 0;           // server socket file descriptor
	sockt = 0;                  // socket number
	connected = 0;              // not connected
}


// Function:   bbsipc destructor
// Purpose:    clean up the bbsipc object (close socket)
// Inputs: none
// Outputs:    none
// Author: Greg Shaw
// Created:    6/16/93

bbsipc::~bbsipc()
{
	close_sock(0);
};


// Function:   close_sock
// Purpose:    close the sockets (if open)
// Inputs: none
// Outputs:    none
// Author: Greg Shaw
// Created:    6/16/93

int bbsipc::close_sock(int client_only)
{
	if (sock_open)
		close(sock_fd);
	if (!client_only)
	{
		if (server)
			close(serv_sock_fd);
		server = 0;
		sock_open = 0;
	}
	connected = 0;
	return(0);
};


// Function:   open_sock
// Purpose:    open a socket for reading, writing or as a server.
// Inputs: name - name of host to contact (NULL for server mode)
//     socknum - the socket number to contact
// Outputs:    -1 for error
// Author: Greg Shaw
// Created:    6/16/93

int bbsipc::open_sock(char *name, int socknum)
{
	// system information
	char macname[MAXHOSTNAMELEN+1];
	struct     sockaddr_in sa;  // socket information
	struct     hostent *hp;     // host entry information

	sockt = socknum;            // save socket number
	if (name == NULL)           // server mode?
	{
		#ifdef DEBUG
		fprintf(stderr,"creating server socket.\r\n");
		#endif
		// yup, create socket
		server = 1;
		// nuke sa contents
		memset(&sa, 0, sizeof(struct sockaddr_in));
		// get this host's name
		gethostname(macname,MAXHOSTNAMELEN);
		// get host info
		hp = gethostbyname(macname);
		if (hp == NULL)         // no info for me?
		{
			fprintf(stderr,"Unable to get host name.\n");
			return(-1);         // get out
		}
		// set host address
		sa.sin_family = hp->h_addrtype;
		// set port number
		sa.sin_port= htons(socknum);
		// create new socket
		if ((serv_sock_fd = socket(AF_INET, SOCK_STREAM,0)) < 0)
		{
			fprintf(stderr,"Unable to open socket.\n");
			return(-1);         // can't create socket
		}
		if (bind(serv_sock_fd,(struct sockaddr *)&sa,(int)sizeof(sa)) < 0)
		{
			close(serv_sock_fd);
			fprintf(stderr,"Unable to bind socket.\n");
			return(-1);
		}
		listen(serv_sock_fd,5); // 5 pending connections max
		sock_open = 1;
	}
	else
	{                           // connect to socket
		#ifdef DEBUG
		printf("attempting to connect client socket.\r\n");
		#endif
		strcpy(hostname, name);
		server = 0;
		// get host addr
		if ((hp = gethostbyname(hostname)) == NULL)
		{
			#ifdef DEBUG
			printf("Couldn't get hostname to connect to.\r\n");
			#endif
			// connection refused
			errno = ECONNREFUSED;
			return(-1);
		}
		// nuke sa contents
		memset(&sa, 0, sizeof(struct sockaddr_in));
		memcpy((char *)&sa.sin_addr, (char *)hp->h_addr, hp->h_length);
		sa.sin_family = hp->h_addrtype;
		sa.sin_port = htons((unsigned short)socknum);
		if ((sock_fd = socket(hp->h_addrtype, SOCK_STREAM,0)) < 0)
			return(-1);
		if (connect(sock_fd,(struct sockaddr *)&sa,sizeof(sa)) < 0)
		{
			#ifdef DEBUG
			printf("client connect failed.\r\n");
			#endif
			close(sock_fd);
			return(-1);
		}
		#ifdef DEBUG
		printf("client connect successful.\r\n");
		#endif
		connected = 1;
		sock_open = 1;
	}
	return(0);                  // exit normally
};


// Function:   connect
// Purpose:    connect to another process via server socket
// Inputs: none
// Outputs:    returns true if socket connection successful
// Author: Greg Shaw
// Created:    6/18/93

int bbsipc::do_connect(int wait)
{
	struct sockaddr_in isa;     // socket address info
	int    i_size;              // socket address size
	int    x;
	fd_set pfd;                 // file descriptor set
	struct timeval waittime;

	waittime.tv_sec = 0;
	waittime.tv_usec = 100;     // 100msec

	FD_ZERO(&pfd);
	FD_SET(serv_sock_fd,&pfd);
	i_size = sizeof(isa);
	getsockname(serv_sock_fd,(struct sockaddr *)&isa,&i_size);

	// accept connection
	if (wait)
	{
		if ((sock_fd = accept(serv_sock_fd,(struct sockaddr *)&isa,&i_size)) < 0)
		{
			#ifdef DEBUG
			printf("server: accept failed.\r\n");
			#endif
			return(-1);
		}
	}
	else                        // don't wait, do a select
	{
		x = select(FD_SETSIZE,&pfd,NULL,NULL,&waittime);
		#ifdef DEBUG
		if (x < 0)              // error
		{
			perror("do_connect");
			exit(1);
		}
		#endif
		if (!FD_ISSET(serv_sock_fd,&pfd))
			return(-1);
		if ((sock_fd = accept(serv_sock_fd,(struct sockaddr *)&isa,&i_size)) < 0)
		{
			#ifdef DEBUG
			printf("server: accept failed.\r\n");
			#endif
			return(-1);
		}
	}
	#ifdef DEBUG
	printf("server: accept succeeded.\r\n");
	#endif
	connected = 1;
	return(0);
};


// Function:   send
// Purpose:    send a message via the socket
// Inputs: msg - the message to send (null terminated string)
// Outputs:    non-zero for error
// Author: Greg Shaw
// Created:    6/16/93

int bbsipc::send(char *msg)
{
	int    cw;
	char eom = ETX;

	// send a message
	#ifdef DEBUG
	printf("send: Sending %s.\r\n",msg);
	#endif
	if (!sock_open && !connected)
	{
		#ifdef DEBUG
		printf("send: socket not open.\r\n");
		#endif
		return(-1);
	}
	if (cw = write(sock_fd,msg,strlen(msg)), cw != strlen(msg))
	{
		#ifdef DEBUG
		printf("Unable to write message!\r\n");
		#endif
		return(-1);
	}
	if (cw = write(sock_fd,&eom,1), cw != 1)
	{
		#ifdef DEBUG
		printf("send: unable to send end of message.\r\n");
		#endif
		return(-1);
	}
	#ifdef DEBUG
	printf("send: sent %s.\r\n",msg);
	#endif

	return(0);
};


// Function:   receive
// Purpose:        receive a message from the other end of the socket
// Inputs:     none
// Outputs:        msg - the message to be received.
// Author:     Greg Shaw
// Created:        6/22/93

int bbsipc::receive(char *msg)
{
	char c;                     // character read
	char eom;                   // end of message found?
	int count;                  // byte counter
	int byr;                    // number of bytes read

	#ifdef DEBUG
	printf("receive: start\r\n");
	#endif
	count = 0;
	byr = 0;
	eom = 0;
	while (!eom)
	{                           // look for trailing null
		if ((byr = read(sock_fd,&c,(unsigned)1)), byr > 0)
		{
			#ifdef DEBUG
			printf("%x char read\r\n",c);
			#endif
			if (c == ETX)
				eom++;
			else
				// add to msg
				msg[count++] = c;
		}
		else if (byr == 0)      // connection is closed for 0 chars read
			return(-1);
	}
	msg[count] = 0;             /* add terminating null */
	return(count);
};


// Function:   msg_avail
// Purpose:    poll the socket to determine whether a message is available
//         for reading.
// Input:  none
// Output: non-zero for a message available
// Author: Greg Shaw
// Created:    6/22/93

int bbsipc::msg_avail(char wait)
{

	fd_set pfd;                 // file descriptor set
	FD_ZERO(&pfd);
	FD_SET(sock_fd,&pfd);
	struct timeval waittime;

	waittime.tv_sec = 0;
	waittime.tv_usec = 100;     // 100msec
	if (!wait)                  // wait?
	{
		select(FD_SETSIZE,&pfd,NULL,NULL,&waittime);
		return(FD_ISSET(sock_fd,&pfd));
		// don't wait, return immed.
	}
	else
	{
		// wait forever
		select(FD_SETSIZE,&pfd,NULL,NULL,NULL);
		return(FD_ISSET(sock_fd,&pfd));
	}
};


#endif                          // _BBSIPC_C_






