/* -------------------------------------------------------------------- */
/* SMS Client, send messages to mobile phones and pagers		*/
/*									*/
/* modem.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 <termios.h>
#include <stdlib.h>
#include <time.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/stat.h>

#if defined(SOLARIS)
#include <sys/mkdev.h>
#endif

#include "modem.h"
#include "logfile/logfile.h"
#include "common/common.h"
#include "expect.h"
#include "lock/lock.h"
#include "resource/resource.h"

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

#if !defined(MMODEMDIR)
#error "MMODEMDIR undefined"
#else
#define MODEMDIR        MMODEMDIR
#endif


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

#define MDM_OK			1
#define MDM_CONNECT		2
#define MDM_BUSY		3
#define MDM_NO_DIALTONE		4
#define MDM_NO_ANSWER		5
#define MDM_RING		6
#define MDM_NO_CARRIER		7
#define MDM_ERROR		8
#define MDM_UNKNOWN_RESPONSE	9
#define MDM_DISCONNECTED	10
#define MDM_RINGING		11

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

struct response_struct 
	{
		char 	*response_str;
		int	response_code;
	};

typedef struct response_struct RESPONSE;

static RESPONSE response_list[] = 
	{
		{ "OK",           MDM_OK 		},
		{ "CONNECT",      MDM_CONNECT 		},
		{ "BUSY",         MDM_BUSY		},
		{ "NO DIALTONE",  MDM_NO_DIALTONE	},
		{ "NO DIAL TONE", MDM_NO_DIALTONE	},
		{ "NO ANSWER",    MDM_NO_ANSWER		},
		{ "RINGING",      MDM_RINGING		},
		{ "RING",         MDM_RING		},
		{ "NO CARRIER",   MDM_NO_CARRIER	},
		{ "ERROR",        MDM_ERROR		},
		{ "UNKNOWN",      MDM_UNKNOWN_RESPONSE	},
		{ "CARRIER",      MDM_CONNECT		},
		{ NULL,		 0			}
	};

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

#define MODEM_OPEN   0
#define MODEM_CLOSED 1

#define MAX_BUFSIZE  1024

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

static int modem_status = MODEM_CLOSED;

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

static 	struct termios 
	t,
	t_orig;

static  long	
	MDM_response_timeout,
	MDM_hangup_timeout,
	MDM_connect_timeout,
	MDM_write_timeout,
	MDM_echo_timeout,
	MDM_drain_timeout,
	MDM_test_echo_timeout,
	MDM_dtr_hangup_sleep,
	MDM_dtr_init_sleep,
	MDM_settle_time,
	MDM_max_rings,
	MDM_lock_retry_delay,
	MDM_pause_before_dial,
	MDM_pause_after_connect;


static 	int	
	modem_echo = TRUE;		/* Is the modem echoing 	*/
					/* Characters? Default is YES 	*/

static 	long	
	soft_hangup_retries;		/* Number of times I should 	*/ 
					/* continue sending +++ATH	*/
					/* If 0 then don't bother	*/
					/* and rely on lowering DTR	*/

static 	long	
	guard_time; 			/* Escape Code Guard Time	*/
					/* in microseconds		*/

static 	long	
	test_guard_time; 		/* Echo Test Guard Time		*/
					/* in microseconds		*/

static 	long 	
	esc_time; 			/* Time between sending each	*/
					/* character of attention 	*/
					/* string in microseconds	*/

static	char	
	modem_file[512],
	modem_lockfile[512];

static 	char 	
	*device_dir,
	*device,
 	*command_prefix,
	*command_suffix,
	*number_prefix,
	*dial_command,
	*hangup_command,
	*init_command,
	*attention_command,
	*response_prefix,
	*response_suffix,
	*set_local_echo_off,
	*lock_action,
	*flow_control,
	*lock_dir,
	*lock_prefix,
	*lock_platform,
	*lock_format,
	*MDM_disable_echo_test,
	*restore_terminal;
	
static 	RESOURCE resource_list[] = 
	{
		{ RESOURCE_STRING,  "MDM_device", 		0, 1, NULL, 0, "modem",     0, 	  &device  		},
		{ RESOURCE_STRING,  "MDM_device_dir", 		0, 0, NULL, 0, "/dev/",     0, 	  &device_dir  		},

		{ RESOURCE_STRING,  "MDM_lock_dir", 		0, 0, NULL, 0, "/var/lock", 0, 	  &lock_dir  		},
		{ RESOURCE_STRING,  "MDM_lock_prefix", 		0, 0, NULL, 0, "LCK..",     0, 	  &lock_prefix  	},

		{ RESOURCE_STRING,  "MDM_lock_platform", 	0, 0, NULL, 0, "TRADITIONAL",   0, 	  &lock_platform  	},
		{ RESOURCE_STRING,  "MDM_lock_format", 		0, 0, NULL, 0, "TRADITIONAL",   0, 	  &lock_format  	},

		{ RESOURCE_STRING,  "MDM_command_prefix", 	0, 1, NULL, 0,  "AT",       0, 	  &command_prefix 	},
		{ RESOURCE_STRING,  "MDM_flow_control", 	0, 1, NULL, 0,  "Hardware", 0, 	  &flow_control 	},
		{ RESOURCE_STRING,  "MDM_command_suffix", 	0, 1, NULL, 0,  "\r",	    0, 	  &command_suffix  	},
		{ RESOURCE_STRING,  "MDM_number_prefix", 	0, 1, NULL, 0,  "",         0, 	  &number_prefix  	},
		{ RESOURCE_STRING,  "MDM_dial_command", 	0, 1, NULL, 0,  "DT",       0, 	  &dial_command  	},
		{ RESOURCE_STRING,  "MDM_hangup_command", 	0, 1, NULL, 0,  "H",        0, 	  &hangup_command  	},
		{ RESOURCE_STRING,  "MDM_init_command", 	0, 1, NULL, 0,  "Z",        0, 	  &init_command  	},
		{ RESOURCE_STRING,  "MDM_set_local_echo_off", 	0, 0, NULL, 0,  "E0",       0, 	  &set_local_echo_off  	},
		{ RESOURCE_STRING,  "MDM_response_prefix", 	0, 1, NULL, 0,  "\r\n",     0, 	  &response_prefix  	},
		{ RESOURCE_STRING,  "MDM_response_suffix", 	0, 1, NULL, 0,  "\r\n",     0, 	  &response_suffix  	},
		{ RESOURCE_STRING,  "MDM_attention_command", 	0, 1, NULL, 0,  "+++",      0, 	  &attention_command  	},
		{ RESOURCE_NUMERIC, "MDM_soft_hangup_retries", 	0, 0, NULL, 0,  NULL,       3, 	  &soft_hangup_retries  },
		{ RESOURCE_NUMERIC, "MDM_guard_time", 		0, 0, NULL, 0,  NULL,       1750000, &guard_time  	},
		{ RESOURCE_NUMERIC, "MDM_test_guard_time", 	0, 0, NULL, 0,  NULL,       250000,  &test_guard_time  	},
		{ RESOURCE_NUMERIC, "MDM_esc_time", 		0, 0, NULL, 0,  NULL,       125000,  &esc_time  	},
		{ RESOURCE_STRING,  "MDM_lock_action", 		0, 1, NULL, 0,  "NO_BLOCK", 0, 	     &lock_action  	},
		{ RESOURCE_NUMERIC, "MDM_lock_retry_delay", 	0, 1, NULL, 0,  NULL, 	    5000000, &MDM_lock_retry_delay  },
		{ RESOURCE_NUMERIC, "MDM_response_timeout", 	0, 0, NULL, 0,  NULL,       10,	  &MDM_response_timeout },
		{ RESOURCE_NUMERIC, "MDM_hangup_timeout", 	0, 0, NULL, 0,  NULL,       60,   &MDM_hangup_timeout },
		{ RESOURCE_NUMERIC, "MDM_connect_timeout", 	0, 0, NULL, 0,  NULL,       60,   &MDM_connect_timeout },
		{ RESOURCE_NUMERIC, "MDM_write_timeout", 	0, 0, NULL, 0,  NULL,       10,   &MDM_write_timeout },
		{ RESOURCE_NUMERIC, "MDM_echo_timeout", 	0, 0, NULL, 0,  NULL,       10,   &MDM_echo_timeout },
		{ RESOURCE_NUMERIC, "MDM_test_echo_timeout", 	0, 0, NULL, 0,  NULL,       10,   &MDM_test_echo_timeout },
		{ RESOURCE_NUMERIC, "MDM_drain_timeout", 	0, 0, NULL, 0,  NULL,       2,   &MDM_drain_timeout },
		{ RESOURCE_STRING,  "MDM_disable_echo_test", 	0, 0, NULL, 0,  "NO",       0,   &MDM_disable_echo_test },
		{ RESOURCE_STRING,  "MDM_restore_terminal", 	0, 0, NULL, 0,  "YES",      0,   &restore_terminal },
		{ RESOURCE_NUMERIC, "MDM_pause_before_dial", 	0, 0, NULL, 0,  NULL,       1000000, 	 &MDM_pause_before_dial },
		{ RESOURCE_NUMERIC, "MDM_pause_after_connect", 	0, 0, NULL, 0,  NULL,       0, 	 &MDM_pause_after_connect },
		{ RESOURCE_NUMERIC, "MDM_dtr_hangup_sleep", 	0, 0, NULL, 0,  NULL,       3000000, 	 &MDM_dtr_hangup_sleep },
		{ RESOURCE_NUMERIC, "MDM_dtr_init_sleep", 	0, 0, NULL, 0,  NULL,       1000000, 	 &MDM_dtr_init_sleep },
		{ RESOURCE_NUMERIC, "MDM_settle_time", 		0, 0, NULL, 0,  NULL,       1, 	 &MDM_settle_time },

		{ RESOURCE_NUMERIC, "MDM_max_rings", 		0, 0, NULL, 0,  NULL,       4, 	 &MDM_max_rings },
		{ RESOURCE_NULL,     NULL, 			0, 1, NULL, 0,  NULL,       0, 	  NULL  		},
	};

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

MDM_response_timeout	Used as timeout for standard
			AT command responses
			ie. Waiting for OK

MDM_hangup_timeout	Used as timeout for standard
			ATH response
			Waiting for OK

MDM_connect_timeout	Used as timeout for connect
			string response. Includes
			time taken to dial,
			other party to answer and
			both modems to negotiate
			ie. CONNECT

MDM_write_timeout	Used as timeout for writes
			of commands to modem

MDM_echo_timeout	Used as timeout for echoing
			of commands written to modem

MDM_drain_timeout       Timeout period to watch modem and drain
                        any leftover characters before sending init string

MDM_test_echo_timeout	Used as timeout for testing
			whether the modem is
			echoing commands we send it

MDM_dtr_hangup_sleep	How many seconds should we
			sleep after lowering DTR to
			ensure disconnect

MDM_settle_time		How many seconds should we
			sleep after receiving the 
			lockfile

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

int MDM_send(int modem, char *str);
int MDM_response(int modem, int timeout);

void fcntl_set(int fd, int flags);
void fcntl_clear(int fd, int flags);

int MDM_init(char *modem_file, char data, char parity, char stop, char flow, long baud);
int MDM_dial(char *number, char data, char parity, char stop, long baud);

void MDM_release_lock(void);
void MDM_obtain_lock(char *device);
void MDM_hangup(int modem);

int toggle_DTR(int modem, long sleep_period);

/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
int SMS_dial(char *number, char *params, long baud)
{
	int 	fd;

	lprintf(LOG_VERBOSE, "Using SMSmodem Package\n");

	fd = MDM_dial(number,
	              params[0],	/* Bits 	*/
	              params[1],	/* Parity 	*/
	              params[2],	/* Stop		*/
	              baud);		/* Baud		*/

	return fd;
}

/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
void SMS_hangup(int modem)
{
	MDM_hangup(modem);
}


/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
char *get_response(int id)
{
	RESPONSE *ptr;

	ptr = response_list;
	while (ptr->response_str != NULL)
	{	if (ptr->response_code == id)
		{	return ptr->response_str;
		}

		ptr++;
	}

	return "UNKOWN";
}

/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
int MDM_send(int modem, char *str)
{
	char	buf[1024];
	int	res;

	res = twrite(modem, str, strlen(str), MDM_write_timeout);
	if (res)
	{	return(res);
	}

	if (modem_echo)
	{
		/* The modem is echoing our commands. So we	*/
		/* should expect to read these back		*/

		res = expnstr(modem, buf, str, strlen(str), MDM_echo_timeout);
		if (res)
		{	return(res);
		}
	}

	return 0;
}

/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
int MDM_response(int modem, int timeout)
{
	int	i;
	char	buf[MAX_BUFSIZE];
	int	res;

	res = expstr(modem, buf, response_prefix, MAX_BUFSIZE, timeout);
	if (res)
	{
		if (res == -2)
		{	return(MDM_DISCONNECTED);
		}

		return(res);
	}

	res = expstr(modem, buf, response_suffix, MAX_BUFSIZE, timeout);
	if (res)
	{
		if (res == -2)
		{	return(MDM_DISCONNECTED);
		}

		return(res);
	}

	for(i=0; response_list[i].response_str != NULL; i++)
	{	if (strncmp(buf, response_list[i].response_str, strlen(response_list[i].response_str)) == 0)
		{	return(response_list[i].response_code);
		}
	}

	return(MDM_UNKNOWN_RESPONSE);
}

/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
void fcntl_set(int fd, int flags)
{
	int	val;


	if ((val = fcntl(fd, F_GETFL, 0)) < 0)
	{	lprintf(LOG_ERROR, "MODEM: fcntl F_GETFL\n");
		exit(-1);
	}

	val |= flags;

	if (fcntl(fd, F_SETFL, val) < 0)
	{	lprintf(LOG_ERROR, "MODEM: fcntl F_SETFL\n");
		exit(-1);
	}
}

/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
void fcntl_clear(int fd, int flags)
{
	int	val;


	if ((val = fcntl(fd, F_GETFL, 0)) < 0)
	{	lprintf(LOG_ERROR, "MODEM: fcntl F_GETFL\n");
		exit(-1);
	}

	val &= ~flags;

	if (fcntl(fd, F_SETFL, val) < 0)
	{	lprintf(LOG_ERROR, "MODEM: fcntl F_SETFL\n");
		exit(-1);
	}
}

/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
void MDM_release_lock(void)
{	resource_unlock(modem_lockfile);
}

/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
void MDM_obtain_lock(char *device)
{
	int 	no_block,
		notify;
	

	resource_check_lockdir(modem_lockfile);	

					/* Exit with message if lockdir	*/
					/* does not exist or we dont	*/
					/* have write permissions	*/


	if (strcmp("NO_BLOCK", lock_action) == 0)
	{	no_block = TRUE;
	}
	else if (strcmp("BLOCK", lock_action) == 0)
	{	no_block = FALSE;	
	}
	else
	{	lprintf(LOG_WARNING, "MDM_lock_action invalid, defaulting to NO_BLOCK\n");
		no_block = TRUE;
	}



	notify = TRUE;
	while(resource_lock(modem_lockfile))
	{
		if (no_block)
		{	lprintf(LOG_ERROR, "Could not obtain lock for modem device\n"); 
			lprintf(LOG_STANDARD, "Another program is using the modem.\n"); 
			exit(-1);
		}

		if (notify)
		{	lprintf(LOG_VERBOSE, "Blocking on lockfile '%s'\n", modem_lockfile); 
			lprintf(LOG_STANDARD, "Another program is using the modem.\n"); 
			lprintf(LOG_STANDARD, "Waiting...\n"); 
			notify = FALSE;
		}

		resource_wait(modem_lockfile, MDM_lock_retry_delay);
	}

	if (!notify)
	{	lprintf(LOG_STANDARD, "Modem is now free.\n"); 
		lprintf(LOG_STANDARD, "Continuing...\n"); 
	}

	atexit(MDM_release_lock);	/* Establish EXIT handler	*/
					/* to release the lockfile if	*/
					/* we leave prematurely		*/

	sleep(MDM_settle_time); 		/* Allow modem to settle down 	*/
					/* I might have just obtained	*/
					/* The lock on the device from	*/
					/* another process		*/
}

/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
int MDM_init(char *modem_file, char data, char parity, char stop, char flow, long baud)
{
	int 	retry,
		modem,
		t_baud;


	modem = open(modem_file, O_RDWR|O_NONBLOCK);
	if (modem == -1)
	{	lprintf(LOG_WARNING, "MODEM: Failed to open %s\n", modem_file);
		return -1;
	}

	/* ---------------------------- */
	/* Toggle DTR to reset modem	*/
	/* ---------------------------- */

	if (toggle_DTR(modem, MDM_dtr_init_sleep))
	{
		lprintf(LOG_WARNING, "MODEM: Failed to toggle DTR\n");

		close(modem);
		return -1;
	}

	/* ---------------------------- */
	/* Get terminal line state	*/
	/* ---------------------------- */

	retry = 3;
	while (retry--)
	{
		if (tcgetattr(modem, &t) == -1)
		{	lprintf(LOG_WARNING, "MODEM: Failed tcgetattr() errno %d\n", errno);

			if (retry == 1)
			{
				close(modem);
				return -1;
			}

			sleep(1);
		}
		else
		{	retry = 0;
		}
	}

	/* ---------------------------- */
	/* Save original line state	*/
	/* ---------------------------- */

	retry = 3;
	while (retry--)
	{
		if (tcgetattr(modem, &t_orig) == -1) 
		{	lprintf(LOG_WARNING, "MODEM: Failed tcgetattr() errno %d\n", errno);

			if (retry == 1)
			{
				close(modem);
				return -1;
			}

			sleep(1);
		}
		else
		{	retry = 0;
		}
	}

	/* ---------------------------- */
	/* Set up terminal attributes	*/
	/* for the device		*/
	/* ---------------------------- */

	t.c_cflag = 0;
	t.c_oflag = 0;		/* Turn off all output processing	*/
	t.c_iflag = 0;
	t.c_lflag = 0;		/* Everything off in local flags,	*/
				/* disables:				*/
				/*	canonical mode			*/
				/*	signal generation		*/
				/*	echo				*/


	t.c_cc[VMIN]  = 1; 	/* 1 Character buffer			*/
	t.c_cc[VTIME] = 0;	/* Block indefinitely			*/

	t.c_cflag |= CREAD  | 	/* Enable receiver 			*/
	             CLOCAL | 
	             HUPCL;	/* Lower modem lines on last close	*/
				/* 1 stop bit (since CSTOPB off)	*/

	/* ---------------------------- */
	/* Set data bits 5 through 8 	*/
	/* ---------------------------- */

	t.c_cflag &= ~(CSIZE);			/* Mask data size	*/
	switch (data)
	{
	case '5':
		t.c_cflag |= CS5;		/* Set 5 bits		*/
		break;
        case '6':
		t.c_cflag |= CS6;		/* Set 6 bits		*/
		break;			
	case '7':
		t.c_cflag |= CS7;		/* Set 7 bits		*/
		break;
	case '8':
		t.c_cflag |= CS8;		/* Set 8 bits		*/
		break;
        default:
		lprintf(LOG_ERROR, "MODEM: Number of bits must be either 5,6,7 or 8\n");

		close(modem);
		return -1; 
	}

	/* ---------------------------- */
	/* Set parity Even, Odd or None	*/
	/* ---------------------------- */

	switch (parity) 
	{
	case 'E':
		t.c_cflag |= PARENB;		/* Enable parity	*/
		break;
	case 'O':
		t.c_cflag |= PARENB;		/* Enable parity	*/
		t.c_cflag |= PARODD; 		/* set parity to odd	*/
		break;
	case 'N':
		break;				/* No parity DEFAULT	*/
	default:
		lprintf(LOG_ERROR, "MODEM: Parity must be either E,O or N\n");

		close(modem);
		return -1; 
	}


	/* ---------------------------- */
	/* Set stop bits 1 or 2		*/
	/* ---------------------------- */
	
	switch (stop)
	{
	case '2':
		t.c_cflag |= CSTOPB;		/* 2 Stop bits		*/
		break;
	case '1':				/* 1 Stop bits DEFAULT	*/ 
		break;
	default:
		lprintf(LOG_ERROR, "MODEM: Stop bits must be either 1 or 2\n");

		close(modem);
		return -1; 
	}


	/* ---------------------------- */
	/* Set flow control		*/
	/* Hardware or Software		*/
	/* ---------------------------- */

	switch (flow)
	{
#if defined(LINUX) || defined(SOLARIS)
	case 'H':
		t.c_cflag |= CRTSCTS;		/* Hardware Flow	*/
		             			/* control		*/
		break;
#endif
	case 'S':
		t.c_iflag |= IXON |		/* Xon/Xoff Flow	*/
		             IXOFF |		/* control		*/
		             IXANY;
		break;
	default:
#if !defined(LINUX) && !defined(SOLARIS)
		lprintf(LOG_ERROR, "MODEM: Flow control must be S for this platform\n");
#else
		lprintf(LOG_ERROR, "MODEM: Flow control must be either H or S for this platform\n");
#endif
		close(modem);
		return -1; 
	}


	/* ---------------------------- */
	/* Set baud rate		*/
	/* Convert from numeric to 	*/
	/* defines set in termios	*/
	/* ---------------------------- */

	if (baud >= 38400)
	{	t_baud = B38400;
	}
	else if (baud >= 19200)
	{	t_baud = B19200;
	}
	else if (baud >= 9600)
	{	t_baud = B9600;
	}
	else if (baud >= 4800)
	{	t_baud = B4800;
	}
	else if (baud >= 2400)
	{	t_baud = B2400;
	}
	else if (baud >= 1200)
	{	t_baud = B1200;
	}
	else
	{	t_baud = B300;
	}

 	if (cfsetispeed(&t, t_baud) == -1) 	/* Set Input Baud rate 	*/
	{
		lprintf(LOG_WARNING, "MODEM: Failed Trying to set Input baud to %ld\n", baud);

		close(modem);
		return -1;
	}

	if (cfsetospeed(&t, t_baud) == -1) 	/* Set output Baud rate	*/
	{
		lprintf(LOG_WARNING, "MODEM: Failed Trying to set Output baud to %ld\n", baud);

		close(modem);
		return -1;		
	}

	/* ---------------------------- */
	/* Apply changes to modem	*/
	/* device			*/
	/* ---------------------------- */

	if (tcflush(modem, TCIOFLUSH) == -1) 
	{	lprintf(LOG_WARNING, "MODEM: Failed tcflush()\n");

		close(modem);
		return -1;
	}

	if (tcsetattr(modem,TCSANOW, &t) == -1) 
	{	lprintf(LOG_WARNING, "MODEM: Failed tcsetattr()\n");

		close(modem);
		return -1;
	}

	/* ---------------------------- */
	/* Reset mode to blocking	*/
	/* ---------------------------- */

	fcntl_clear(modem, O_NONBLOCK);

	if (toggle_DTR(modem, MDM_dtr_init_sleep))
	{
		lprintf(LOG_WARNING, "MODEM: Failed to toggle DTR\n");

		close(modem);
		return -1;
	}

	modem_status = MODEM_OPEN;
	return modem;
}


/* -------------------------------------------------------------------- */
/* Send AT string. Check if result contains AT.				*/
/* If command is echoed then set modem_echo flag.			*/
/* -------------------------------------------------------------------- */
int test_modem_echo(int modem)
{
	char	buf[MAX_BUFSIZE],
		str[MAX_BUFSIZE];


	strcpy(str, command_prefix);
	strcat(str, command_suffix);

	if (twrite(modem, str, strlen(str), MDM_write_timeout))
	{	return(-1);
	}

	if (expstr(modem, buf, response_prefix, MAX_BUFSIZE, MDM_echo_timeout))
	{	return(-1);
	}

	if (strncmp(buf, str, strlen(str)) == 0)
	{
		modem_echo = TRUE;
		lprintf(LOG_VERBOSE, "MODEM: Modem echo is set to ON\n");
	} 
	else
	{	modem_echo = FALSE;
		lprintf(LOG_VERBOSE, "MODEM: Modem echo is set to OFF\n");
	}

	if (expstr(modem, buf, response_suffix, MAX_BUFSIZE, MDM_echo_timeout))
	{	return(-1);
	}

	if (strncmp(buf, "OK", strlen("OK")))
	{	return(-1);
	}

	return 0;
}

/* -------------------------------------------------------------------- */
/* Send ATE0 string to set local echo to off.				*/
/* -------------------------------------------------------------------- */
int set_local_echo(int modem)
{
	char	str[MAX_BUFSIZE];
	int	res;

	sms_usleep(test_guard_time);

	if (twrite(modem, "\r", strlen("\r"), MDM_write_timeout))
	{	return(-1);
	}

	sms_usleep(test_guard_time);

	if (twrite(modem, "\r", strlen("\r"), MDM_write_timeout))
	{	return(-1);
	}

	sms_usleep(test_guard_time);

	strcpy(str, command_prefix);
	strcat(str, set_local_echo_off);
	strcat(str, command_suffix);

	if (twrite(modem, str, strlen(str), MDM_write_timeout))
	{	return(-1);
	}

	if ((res = MDM_response(modem, MDM_response_timeout)) != MDM_OK)
	{	return(-1);
	}

	lprintf(LOG_VERBOSE, "MODEM: Setting Local Modem Echo to OFF\n");

	if (test_modem_echo(modem))
	{	return(-1);
	}
	
	if (modem_echo)
	{	return(-1);
	}

	return 0;
}

/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
int MDM_send_hangup(int modem)
{
	char 	str[MAX_BUFSIZE],
		*ptr;
	int	res, 
		retry,
		hangup_status;


	hangup_status = FALSE;
	for (retry = soft_hangup_retries; retry > 0; retry--)
	{
		tcflush(modem, TCIOFLUSH);	/* Flush all data 	*/

		sms_usleep(guard_time);		/* Sleep for gaurd_time	*/
						/* before transmitting	*/
						/* any data		*/

		for(ptr = attention_command; *ptr ;ptr++)
		{
			str[0] = *ptr;
			str[1] = '\0';
		
			if (twrite(modem, str, strlen(str), MDM_write_timeout))
			{	break;
			}
	
			if (ptr[1] != '\0')
			{	sms_usleep(esc_time);
			}
		}

		if (*ptr)
		{	lprintf(LOG_WARNING, "MODEM: Failed to issue attention command - %s\n", attention_command); 
			break;
		}

		sms_usleep(guard_time);		/* Sleep for gaurd_time	*/
						/* before transmitting	*/
						/* any data		*/
	


		res = MDM_response(modem, MDM_hangup_timeout);
	 	if (res != MDM_OK)
		{	lprintf(LOG_WARNING, "MODEM: Expecting OK response - %s\n", get_response(res)); 
		}


		sms_usleep(guard_time);		/* Sleep for gaurd_time	*/


		strcpy(str, command_prefix);
		strcat(str, command_suffix);
	
		twrite(modem, str, strlen(str), MDM_write_timeout);
		res = MDM_response(modem, MDM_hangup_timeout);
	 	if (res != MDM_OK)
		{	lprintf(LOG_WARNING, "MODEM: Expecting OK response - %s\n", get_response(res)); 
		}

		strcpy(str, command_prefix);
		strcat(str, hangup_command);
		strcat(str, command_suffix);

		twrite(modem, str, strlen(str), MDM_write_timeout);
		res = MDM_response(modem, MDM_hangup_timeout);
	 	if (res != MDM_OK)
		{	if ((res == MDM_NO_CARRIER) ||
			    (res == MDM_DISCONNECTED))
			{
				hangup_status = TRUE;
				break;
			}

			lprintf(LOG_WARNING, "MODEM: Expecting OK response - %s\n", get_response(res)); 
		}
		else
		{	hangup_status = TRUE;
			break;
		}
	}

	return hangup_status;
}


/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
int toggle_DTR(int modem, long sleep_period)
{
#if defined(TIOCSDTR)

	lprintf(LOG_VERBOSE, "MODEM: Toggle DTR using ioctl() %ld Microseconds\n", sleep_period);


	if (ioctl(modem, TIOCCDTR, 0) == -1)
	{
		lprintf(LOG_WARNING, "MODEM: Failed ioctl() errno %d\n", errno);
	}

	sms_usleep(sleep_period);		/* Allow things to 	*/
						/* Settle down DTR may	*/
						/* need to be left	*/
						/* lowered for a period	*/
						/* of time		*/


	if (ioctl(modem, TIOCSDTR, 0) == -1)
	{
		lprintf(LOG_WARNING, "MODEM: Failed ioctl() errno %d\n", errno);
	}

#else
int	retry;
struct 	termios 
	t, 
	ot;


	lprintf(LOG_VERBOSE, "MODEM: Toggle DTR %ld Microseconds\n", sleep_period);

	retry = 3;
	while (retry--)
	{
		if (tcgetattr(modem, &t) == -1)
		{	lprintf(LOG_WARNING, "MODEM: Failed tcgetattr() errno %d\n", errno);

			if (retry == 1)
			{
				close(modem);
				return -1;
			}

			sleep(1);
		}
		else
		{	retry = 0;
		}
	}

	retry = 3;
	while (retry--)
	{
		if (tcgetattr(modem, &ot) == -1)
		{	lprintf(LOG_WARNING, "MODEM: Failed tcgetattr() errno %d\n", errno);

			if (retry == 1)
			{
				close(modem);
				return -1;
			}

			sleep(1);
		}
		else
		{	retry = 0;
		}
	}

	if ((cfsetospeed(&t, B0) == -1) || 	/* B0 causes		*/
	    (cfsetispeed(&t, B0) == -1)) 	/* DTR to be lowered	*/
	{					
		lprintf(LOG_WARNING, "MODEM: Failed cfsetspeed() - B0 HANGUP\n");
		return -1;
	}

	retry = 3;
	while (retry--)
	{
		if (tcsetattr(modem,TCSANOW, &t) == -1) 
		{	lprintf(LOG_WARNING, "MODEM: Failed tcsetattr() errno %d - B0 HANGUP\n", errno);

			if (retry == 1)
			{
				return -1;
			}

			sleep(1);
		}
		else
		{	retry = 0;
		}
	}

	sms_usleep(sleep_period);		/* Allow things to 	*/
						/* Settle down DTR may	*/
						/* need to be left	*/
						/* lowered for a period	*/
						/* of time		*/

	retry = 3;
	while (retry--)
	{
 		if (tcsetattr(modem,TCSANOW, &ot) == -1) 
		{
			lprintf(LOG_WARNING, "MODEM: Failed tcsetattr() errno %d - setting speed\n", errno);

			if (retry == 1)
			{
				return -1;
			}

			sleep(1);
		}
		else
		{	retry = 0;
		}
	}
#endif
	return 0;
}

/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
void MDM_hangup(int modem)
{
	int retry;


	if (modem_status == MODEM_CLOSED)
	{	return;
	}

	MDM_send_hangup(modem);
	toggle_DTR(modem, MDM_dtr_hangup_sleep);

	if (strcmp(restore_terminal, "YES") == 0)
	{
		lprintf(LOG_VERBOSE, "MODEM: Returning Terminal to original settings\n");

		retry = 3;
		while (retry--)
		{
			if (tcsetattr(modem, TCSANOW, &t_orig) == -1)
			{
							/* Failed to return 	*/
							/* to original terminal	*/
							/* settings		*/

				lprintf(LOG_WARNING, "MODEM: Failed tcsetattr() errno %d - Trying to return modem device to original settting\n", errno);

				sleep(1);
			}
			else
			{	retry = 0;
			}
		}
	}

	close(modem);
	modem_status = MODEM_CLOSED;

	lprintf(LOG_VERBOSE, "MODEM: Hangup Complete\n");

	resource_unlock(modem_lockfile);	/* Remove lock file	*/
						/* on this device	*/
}

/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
int MDM_dial(char *number, char data, char parity, char stop, long baud) 
{
	struct 	stat 
		stat_buf;

	int 	modem,
		res,
		ring_count;


	char 	str[MAX_BUFSIZE],
		fname[MAX_BUFSIZE];


	strcpy(fname, MODEMDIR);
	sms_filecat(fname, "sms_modem");

	if (read_resource_file(fname, resource_list, TRUE) != RESOURCE_FILE_OK)
	{	lprintf(LOG_ERROR, "MODEM: Unrecoverable Failure Parsing modem file '%s'\n", fname); 
		exit(-1);
	}



	strcpy(modem_file, device_dir);
	strcat(modem_file, device);

	strcpy(modem_lockfile, lock_dir);
	sms_filecat(modem_lockfile, lock_prefix);


	if (stat(modem_file, &stat_buf) == -1)
	{
		lprintf(LOG_ERROR, "MODEM: stat() Failed\n"); 
		lprintf(LOG_STANDARD, "\n");
		lprintf(LOG_STANDARD, "Ensure that the device entry '%s' exists\n", modem_file);
		lprintf(LOG_STANDARD, "If you want to use a different device, change the\n");
		lprintf(LOG_STANDARD, "value of 'MDM_device' in '%s' to that device.\n", MODEMDIR "/sms_modem");
		lprintf(LOG_STANDARD, "\n");

		exit(-1);
	}

	if (strcmp(lock_platform, "SOLARIS") == 0)
	{
#if defined(SOLARIS)
		sms_snprintf(&modem_lockfile[strlen(modem_lockfile)], 64, "%03u.%03u.%03u",
		            (unsigned int)(major(stat_buf.st_dev)),
		            (unsigned int)(stat_buf.st_rdev >> 18),
		            (unsigned int)(minor(stat_buf.st_rdev)));

		lprintf(LOG_VERBOSE, "Generating SOLARIS style lockfile '%s'\n", modem_lockfile);
#else
		lprintf(LOG_ERROR, "SOLARIS style lockfiles only supported under SOLARIS\n");
		exit(-1);
#endif
	}
	else
	{	if (strcmp(lock_platform, "TRADITIONAL") != 0)
		{	lprintf(LOG_WARNING, "MODEM: MDM_lock_platform invalid using 'TRADITIONAL'\n"); 
		}

		strcat(modem_lockfile, device);
	}


	if ((strcmp(flow_control, "Software") != 0) &&
	    (strcmp(flow_control, "Hardware") != 0))
	{
		lprintf(LOG_ERROR, "MODEM: MDM_flow_control must be either 'Software' or 'Hardware'\n"); 
		exit(-1);
	}

	MDM_obtain_lock(device);	/* May Block until lock		*/
					/* obtained. This is dependant 	*/
					/* on 'lock_action' set to	*/
					/* BLOCK or NO_BLOCK		*/


	modem = MDM_init(modem_file, data, parity, stop, flow_control[0], baud);
	if (modem < 0)
	{	lprintf(LOG_ERROR, "MODEM: MDM_init() Failed\n"); 
		exit(-1);
	}

	if (MDM_drain_timeout != 0)
	{
		/* Drain any pending chars from modem */

		lprintf(LOG_VERBOSE, "MDM_send: Drain required.\n");

		while (expdrain(modem, MDM_drain_timeout))
		{
			lprintf(LOG_VERBOSE, "MDM_send: Draining.");
		}
	}

	if (strcmp(MDM_disable_echo_test, "NO") == 0)
	{	if (set_local_echo(modem) == -1)
		{	lprintf(LOG_ERROR, "MODEM: Setting Local echo Failed\n"); 
			exit(-1);
		}
	}	

	strcpy(str, command_prefix);
	strcat(str, init_command);
	strcat(str, command_suffix);

	lprintf(LOG_VERBOSE, "MODEM: Sending Initialization string %s to modem\n", str); 

	if (MDM_send(modem, str) != 0)
	{	lprintf(LOG_VERBOSE, "MODEM: Sending Initialization string\n"); 
		exit(-1);
	}

	lprintf(LOG_VERBOSE, "MODEM: Waiting for Initialization to complete...\n"); 

	if ((res = MDM_response(modem, MDM_response_timeout)) != MDM_OK)
	{	lprintf(LOG_VERBOSE, "MODEM: Expecting OK response - %s\n", get_response(res)); 
		exit(-1);
	}

	lprintf(LOG_VERBOSE, "MODEM: Initialization complete\n"); 



	if (MDM_pause_before_dial)
	{
		lprintf(LOG_VERBOSE, "MODEM: Pause before dialing\n"); 
		lprintf(LOG_VERBOSE, "MODEM: Sleeping for %ld Microseconds...\n", MDM_pause_before_dial); 
	
		sms_usleep(MDM_pause_before_dial);
	}



	lprintf(LOG_VERBOSE, "MODEM: Dialing %s\n", number); 

	strcpy(str, command_prefix);
	strcat(str, dial_command);
	strcat(str, number_prefix);
	strcat(str, number);
	strcat(str, command_suffix);

	lprintf(LOG_VERBOSE, "MODEM: Sending dial string %s to modem\n", str); 

	if (MDM_send(modem, str) != 0)
	{	lprintf(LOG_ERROR, "MODEM: Sending dial string\n");
		exit(-1);
	}

	lprintf(LOG_VERBOSE, "MODEM: Waiting for Connection...\n");


	ring_count = 0;
	while ((res = MDM_response(modem, MDM_connect_timeout)) == MDM_RINGING)
	{
		lprintf(LOG_VERBOSE, "MODEM: Ringing...\n"); 

		ring_count++;
		if (ring_count > MDM_max_rings)
		{	lprintf(LOG_WARNING, "MODEM: Expecting CONNECT within %ld rings\n", MDM_max_rings); 
			break;
		}
	}

	if (res != MDM_CONNECT)
	{
		lprintf(LOG_VERBOSE, "MODEM: Expecting CONNECT response - %s\n", get_response(res)); 
		exit(-1);
	}


	lprintf(LOG_VERBOSE, "MODEM: Connection Established.\n");

	if (MDM_pause_after_connect != 0)
	{
		lprintf(LOG_VERBOSE, "MODEM: Pausing after connection...\n");

		usleep(MDM_pause_after_connect);

		lprintf(LOG_VERBOSE, "MODEM: Continuing.\n");
	}

	return modem;
}

