/* -------------------------------------------------------------------- */
/* SMS Client, send messages to mobile phones and pagers		*/
/*									*/
/* terminal_io.c							*/
/*									*/
/*  Copyright (C) 1999 Angelo Masci					*/
/*									*/
/*  This library is free software; you can redistribute it and/or	*/
/*  modify it under the terms of the GNU Library General Public		*/
/*  License as published by the Free Software Foundation; either	*/
/*  version 2 of the License, or (at your option) any later version.	*/
/*									*/
/*  This library 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	*/
/*  Library General Public License for more details.			*/
/*									*/
/*  You should have received a copy of the GNU Library General Public	*/
/*  License along with this library; if not, write to the Free		*/
/*  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.	*/
/*									*/
/*  You can contact the author at this e-mail address:			*/
/*									*/
/*  angelo@styx.demon.co.uk						*/
/*									*/
/* -------------------------------------------------------------------- */
/* $Id$
   -------------------------------------------------------------------- */

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <errno.h>
#include <assert.h>
#include <termios.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include <netdb.h>
#include <string.h>

#include "terminal_io.h"
#include "logfile_io.h"

/* -------------------------------------------------------------------- */

#define FD_MAX(a,b) (((a)>(b))?(a):(b))
#define FD_MIN(a,b) (((a)<(b))?(a):(b))


struct 	termios 
	current_term, 
	original_term;

/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
void resetterm(void)
{
	/* Restore our old terminal settings */

	tcsetattr(STDIN_FILENO, TCSANOW, &original_term);
}


/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
void setterm(void)
{
	if (isatty(STDIN_FILENO))
	{
		/* If we're using a terminal for input	*/
		/* we want it to be unbuffered.		*/

		tcgetattr(STDIN_FILENO, &current_term);
		tcgetattr(STDIN_FILENO, &original_term);

#if 0
		current_term.c_lflag &= ~(ICANON | ECHO);
#else
		current_term.c_lflag &= ~(ICANON);
#endif

		current_term.c_cc[VMIN]  = 1;
		current_term.c_cc[VTIME] = 0;

		tcsetattr(STDIN_FILENO, TCSANOW, &current_term);

		atexit(resetterm);
	}
}


/* -------------------------------------------------------------------- */
/*									*/
/*  Terminal             Terminal_io()              Socket /dev/pts/?	*/
/*  Client                                          Server		*/
/*  -----   1.           ---------------    3.       -----		*/
/*  |   |<---------------| <------+<-- |<------------|   |		*/
/*  |   |                |        |    |             |   |		*/
/*  |   |   2.           |        |    |    4.       |   |		*/
/*  |   |--------------->| -->+---)--> |------------>|   |		*/
/*  -----                -----|---|-----             -----		*/
/*              6.            |	  |					*/
/*  Terminal  <---------------+	  |					*/
/*  Client                    |	  |					*/
/*                            V	  V					*/
/* 			      -----					*/
/*                              |					*/
/*                              | 					*/
/*                              V 5.					*/
/*                       --------------					*/
/*                       |            |	Logfile				*/
/*                       --------------					*/
/*									*/
/*	1. Client_fdout							*/
/*	2. Client_fdin							*/
/*	3. Server_fdout							*/
/*	4. Server_fdin							*/
/*	5. Flog								*/
/*	5. Terminal_fdout						*/
/*									*/
/* -------------------------------------------------------------------- */
void terminal_io(int client_fdin, int client_fdout, int server_fdin, int server_fdout, int terminal_fdout)
{

	int 	select_res,
		read_res,
		write_res,
		client_fdout_data,
		server_fdout_data,
		terminal_fdout_data,
		read_data;

	int 	nfds;

	fd_set 	readfds,
		writefds;

	char	c;


	/* ------------------------------------------------------------ */

	if (terminal_fdout == client_fdout)
	{
		/* The terminal is set to echo data, if we	*/
		/* allow data to be sent to the terminal also 	*/
		/* we'll end up with data being echoed twice	*/

		terminal_fdout = -1;
	}

	/* ------------------------------------------------------------ */



	FD_ZERO(&readfds);
	FD_ZERO(&writefds);

	nfds = FD_MAX(terminal_fdout, 
	              FD_MAX(FD_MAX(client_fdin, client_fdout),
	                     FD_MAX(server_fdin, server_fdout))) +1;
	              

	client_fdout_data   = 0;	
	server_fdout_data   = 0;
	terminal_fdout_data = 0;
	read_data           = 1;	

	do
	{	
		/* -------------------------------------------- */
		/* Set up FS_SET's we're interested in 		*/
		/* reading data from and writing data to.	*/


		FD_SET(client_fdin, &readfds);
		FD_SET(server_fdin, &readfds);


		if (client_fdout_data)
		{
			/* We have data we want to output */

			FD_SET(client_fdout, &writefds);
		}
		else
		{	/* No data to output */

			FD_CLR(client_fdout, &writefds);
		}

		if (terminal_fdout != -1)
		{
			if (terminal_fdout_data)
			{
				/* We have data we want to output */

				FD_SET(terminal_fdout, &writefds);
			}
			else
			{	/* No data to output */

				FD_CLR(terminal_fdout, &writefds);
			}
		}

		if (server_fdout_data)
		{
			/* We have data we want to output */

			FD_SET(server_fdout, &writefds);
		}
		else
		{	/* No data to output */

			FD_CLR(server_fdout, &writefds);
		}

		/* ---------------------------------------- */



		do
		{	select_res = select(nfds, &readfds, &writefds, NULL, NULL);

		} while ((select_res == -1) && 
		         (errno == EINTR));


		if (select_res == -1)
		{
			exit(-1);
		}
		else
		if (select_res > 0)
		{		
			if ((FD_ISSET(client_fdin, &readfds)) ||
			    (FD_ISSET(client_fdout, &writefds)) ||
			    ((terminal_fdout != -1) && (FD_ISSET(terminal_fdout, &writefds)))||
			    (FD_ISSET(server_fdin, &readfds)) ||
			    (FD_ISSET(server_fdout, &writefds)))
			{

				/* We've been alerted of some data	*/
				/* being present on the lines we're	*/
				/* interested in.			*/


				if (FD_ISSET(client_fdin, &readfds))
				{
					/* Something for us to READ from 'client_fdin' */


					if ((client_fdout_data == 0) &&
					    (terminal_fdout_data == 0) &&
					    (server_fdout_data == 0))
					{
						/* There aren't any characters left unsent */
						/* Read a character */


						do
						{	read_res = read(client_fdin, &c, 1);
				
						} while ((read_res == -1) && 
						         (errno == EINTR));

						if (read_res == -1)
						{
							exit(-1);
						}
						else
						if (read_res == 0)
						{
							/* EOF at our end - client */
#if 0 /* look at this */
							fprintf(stderr, "Client sent EOF.\n");
							read_data = 0;
#endif
						}
						else
						{	/* Character read */

#if 0
							if ((int)c == '\004') /* CTR-D for EOF */
							{
								fprintf(stderr, "Client sent EOF.\n");
								read_data = 0;
							}
#endif
						}



						/* Send data to 'client_fdout' and 'server_fdout' */


						log_client_send();
						log_character(c);


						if (terminal_fdout != -1)
						{
							terminal_fdout_data = 1;
						}

						server_fdout_data   = 1;
					}
				}

				if (FD_ISSET(client_fdout, &writefds))
				{
					/* Something for us to WRITE to 'client_fdout' */


					assert(client_fdout_data);

					do 
					{	write_res = write(client_fdout, &c, 1);
		
					} while ((write_res == -1) && 
					         (errno == EINTR));

					if (write_res == -1)
					{
						exit(-1);
					}
					else
					if (write_res == 0)
					{
						exit(-1);
					}
					else
					{	/* Character written */
					}


					client_fdout_data = 0;
				}

				if ((terminal_fdout != -1) && (FD_ISSET(terminal_fdout, &writefds)))
				{
					/* Something for us to WRITE to 'terminal_fdout' */


					assert(terminal_fdout_data);

					do 
					{	write_res = write(terminal_fdout, &c, 1);
		
					} while ((write_res == -1) && 
					         (errno == EINTR));

					if (write_res == -1)
					{
						exit(-1);
					}
					else
					if (write_res == 0)
					{
						exit(-1);
					}
					else
					{	/* Character written */
					}

					terminal_fdout_data = 0;
				}

				if (FD_ISSET(server_fdin, &readfds))
				{
					/* Something for us to READ from 'server_fdin' */


					if ((client_fdout_data == 0) &&
					    (terminal_fdout_data == 0) &&
					    (server_fdout_data == 0))
					{
						/* There aren't any characters left unsent */
						/* Read a character */

						do
						{	read_res = read(server_fdin, &c, 1);
				
						} while ((read_res == -1) && 
						         (errno == EINTR));


						if (read_res == -1)
						{
							exit(-1);
						}
						else
						if (read_res == 0)
						{
							/* EOF at their end - server */

							fprintf(stderr, "Server sent EOF.\n");
							read_data = 0;
						}
						else
						{	/* Character read */
						}

						/* Send data to 'client_fdout' */



						log_server_send();
						log_character(c);


						if (terminal_fdout != -1)
						{
							terminal_fdout_data = 1;
						}

						client_fdout_data = 1;
					}
				}

				if (FD_ISSET(server_fdout, &writefds))
				{
					/* Something for us to WRITE to 'server_fdout' */

					assert(server_fdout_data);

					do 
					{	write_res = write(server_fdout, &c, 1);
		
					} while ((write_res == -1) && 
					         (errno == EINTR));

					if (write_res == -1)
					{
						exit(-1);
					}
					else
					if (write_res == 0)
					{
						exit(-1);
					}
					else
					{	/* Character written */
					}

					server_fdout_data = 0;
				}
			}
			else
			{	/* Something woke us and it wasn't  	*/
				/* 'client_fdin', 'client_fdout', 	*/
				/* 'server_fdin' or 'server_fdout' 	*/

				exit(-1);
			}
		}
		else
		{	/* Timeout expired - we didn't set a timeout	*/

			exit(-1);
		}


	} while (read_data);


	fprintf(stderr, "Connection terminated.\n");
}



