/* -------------------------------------------------------------------- */
/* SMS Client, send messages to mobile phones and pagers		*/
/*									*/
/* expect.c								*/
/*									*/
/*  Copyright (C) 1997,1998,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 <stdio.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>

#include "common/common.h"
#include "logfile/logfile.h"
#include "error.h"

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

#define MAX_LINE 	4096

volatile sig_atomic_t 
	 caught_alarm = 0;

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

static void signal_alarm(int);
static int expread(int, char *);
static int expwrite(int, char *);

/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
static void signal_alarm(int signo)
{
	caught_alarm = 1;
}

/* -------------------------------------------------------------------- */
/* Ensure that read() and write() systems calls are			*/
/* not restarted automatically on receipt of a SIGALRM.			*/
/* -------------------------------------------------------------------- */
int starttimer(int duration)
{
	struct sigaction act;

	act.sa_flags   = 0;
	act.sa_handler = signal_alarm;
	sigemptyset(&act.sa_mask);

#if defined(SA_INTERRUPT)
	act.sa_flags  |= SA_INTERRUPT;
#endif

	caught_alarm = 0;
	sigaction(SIGALRM, &act, NULL);

	return alarm(duration);
}


/* -------------------------------------------------------------------- */
/* Read explen characters from fd and copy to str,			*/
/* appending '\0'. Compare explen characters of str with expect_string.	*/
/*									*/
/* Return Values:							*/
/*									*/
/*	 0 Expect_string is found before timeout expires		*/
/*	-1 Error or Timeout exceeded					*/
/*									*/
/* -------------------------------------------------------------------- */
int expnstr(int fd, char *str, char *expect_string, int explen, int timeout)
{
	int 	i;
	char 	*ptr;

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

	starttimer(timeout);

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

	ptr = str;
	for (i=0; i<explen; i++)
	{
		switch (expread(fd, ptr))
		{
			case 1:
			{	ptr++;			
				break;
			}
			case 0:
			{
				*ptr = '\0';
				alarm(0);

				lprintf(LOG_WARNING, "EOF Detected\n");

				return -1;
			}
			default:
			{
				/* Timeout */

				*ptr = '\0';
				alarm(0);

				lprintf(LOG_ERROR, "Timeout: searching for +%s+ failed after %d seconds\n", expect_string, timeout);
				lprintf(LOG_VERBOSE, "Received String: +%s+\n", str);

				return -1;
			}
		}

	}
	
	if (strncmp(str, expect_string, explen) == 0)
	{
		*ptr = '\0';
		alarm(0);

		lprintf(LOG_VERBOSE, "Received String: +%s+\n", str);
		
		return 0;
	}


	*ptr = '\0';
	alarm(0);

	lprintf(LOG_VERBOSE, "Received String: +%s+\n", str);

	return -1;
}



/* -------------------------------------------------------------------- */
/* Read from open file fd and search for expect_string.			*/
/* All data read is placed in str and terminated with '\0'		*/
/* Return Values:							*/
/*									*/
/*	 0 Expect_string is found before timeout expires		*/
/*	-1 Error errno is set appropriately.				*/
/*									*/
/* Errors:								*/
/*									*/
/* 	   ETIMEOUT Timeout exceeded					*/
/* 	   EEOF     End of File encountered				*/
/* 	   ELEN     Buffer length exceeded				*/
/*									*/
/* -------------------------------------------------------------------- */
int expstr(int fd, char *str, char *expect_string, int maxlen, int timeout)
{
	int 	explen,
	 	i,
		len;

	char 	*ptr,
	 	*cmpptr;
	

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

	starttimer(timeout);

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

	explen = strlen(expect_string);
	
	len = 0;
	ptr = str;
	for (i=0; i<explen; i++)
	{
		if (len >= maxlen)
		{	alarm(0);
			return -1;
		}

		switch (expread(fd, ptr))
		{
			case 1:
			{	ptr++;
				len++;			
				break;
			}
			case 0:
			{	
				*ptr = '\0';
				alarm(0);

				lprintf(LOG_WARNING, "EOF Detected\n");
				return -1;
			}
			default:
			{
				/* Timeout */

				*ptr = '\0';
				alarm(0);

				lprintf(LOG_ERROR, "Timeout: searching for +%s+ failed after %d seconds\n", expect_string, timeout);
				lprintf(LOG_VERBOSE, "Received String: +%s+\n", str);
				return -1;
			}
		}
	}

	cmpptr = str;
	while (len < (maxlen -1))
	{	
		if (strncmp(cmpptr, expect_string, explen) == 0)
		{
			*ptr = '\0';
			alarm(0);

			lprintf(LOG_VERBOSE, "Received String: +%s+\n", str);
			return 0;
		}

		cmpptr++;
		
		switch (expread(fd, ptr))
		{
			case 1:
			{	ptr++;
				len++;
				break;
			}
			case 0:
			{
				*ptr = '\0';
				alarm(0);

				lprintf(LOG_WARNING, "EOF Detected\n");
				return -1;
			}
			default:
			{
				/* Timeout */
				*ptr = '\0';
				alarm(0);

				lprintf(LOG_ERROR, "Timeout: searching for +%s+ failed after %d seconds\n", expect_string, timeout);
				lprintf(LOG_VERBOSE, "Received String: +%s+\n", str);
				return -1;
			}
		}
	}
	
	*ptr = '\0';
	alarm(0);

	lprintf(LOG_ERROR, "Buffer Length exceeded: searching for +%s+ failed after reading %d bytes\n", expect_string, (maxlen -1));
	lprintf(LOG_VERBOSE, "Received String: +%s+\n", str);
	return -1;
}


/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
int expdrain(int fd, int timeout)
{
	int 	n,
		drain;
	char 	c;

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

	starttimer(timeout);

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

	lprintf(LOG_VERYVERBOSE, "Draining any remaining data...\n");

	drain = TRUE;
	n     = 0;

	while (drain)
	{
		switch (expread(fd, &c))
		{
			case 1:
				n++;
				break;

			case 0:
				lprintf(LOG_WARNING, "EOF Detected [338]\n");

				drain = FALSE;
				break;
			default:
				/* Timeout */

				drain = FALSE;
				break;
		}
   	} 

	lprintf(LOG_VERYVERBOSE, "Drain complete\n");

	alarm(0);	
	return n;
}


/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
static int expread(int fd, char *buf)
{
	int	len;


	do 
	{	if (caught_alarm)
		{	
			lprintf(LOG_WARNING, "read() Timeout\n");
			return -1;
		}	

		len = read(fd, buf, 1);
		if (len == -1)
		{
			if (errno != EINTR)
			{
				lprintf(LOG_ERROR, "read() Failed with errno %d\n", errno);
				return -1;
			}		
		}

	} while (len == -1);


	if (len == 1)
	{
		if (*buf != '\0')
		{	lprintf(LOG_VERYVERBOSE, "Received Character: +%c+\n", *buf);
		}
		else
		{	lprintf(LOG_VERYVERBOSE, "Received Character: +<NUL>+\n");
		}
	}
	else
	if (len == 0)
	{	lprintf(LOG_VERBOSE, "read() Returned EOF\n");
	}
	else
	{	lprintf(LOG_WARNING, "read() Unexpected return value %d\n", len);
	}

	return len;
}


/* -------------------------------------------------------------------- */
/* Write len bytes of buf to fd, return prematurely on timeout		*/
/* Return Values:							*/
/*									*/
/*	-1 On timeout							*/
/*	 0 Success							*/
/* -------------------------------------------------------------------- */
int twrite(int fd, char *buf, int len, int timeout)
{
	char 	*ptr,
		*line;
	int 	i;

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

	starttimer(timeout);

	/* ---------------------------- */
	
	ptr = buf;
	for (i=0; i<len; i++)
	{
		if (expwrite(fd, ptr) != 1)
		{
			/* Timeout */

			alarm(0);

			lprintf(LOG_ERROR, "Timeout: Written %d characters of +%s+ failed after %d seconds\n", i, buf, timeout);
			return -1;
		}		

		ptr++;
	}

	alarm(0);

	line = (char *)malloc(sizeof(char) * (len +1));
	if (line == NULL)
	{
		lprintf(LOG_ERROR, "malloc() failed\n");
		exit(EMALLOC);	
	}

	strncpy(line, buf, len);
	line[len] = '\0';

	lprintf(LOG_VERBOSE, "Written String: +%s+\n", line);

	free(line);
	return 0;
}

/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
static int expwrite(int fd, char *buf)
{
	int 	len;

	do
	{	if (caught_alarm)
		{
			lprintf(LOG_WARNING, "write() Timeout\n");
			return -1;
		}	

		len = write(fd, buf, 1);
		if (len == -1)
		{
			if (errno != EINTR)
			{
				lprintf(LOG_ERROR, "write() Failed with errno %d\n", errno);
				return -1;
			}		
		}

	} while (len == -1);


	if (len == 1)
	{	lprintf(LOG_VERYVERBOSE, "Written Character: +%c+\n", *buf);
	}
	else
	{	lprintf(LOG_WARNING, "write() Unexpected return value %d\n", len);
	}

	return len;
}

