/*************************************************************************
NIXMAIL MODEM Routines
--------------------------------------------------------------------------
Copyright (c) 1992 Anthony Rumble
--------------------------------------------------------------------------
RCS Info

$Header: /home/smilie/emsi/RCS/modem.c,v 1.8 1992/12/06 09:36:30 smilie Exp $

$Log: modem.c,v $
 * Revision 1.8  1992/12/06  09:36:30  smilie
 * run thru with Wall. Fixing warnings
 *
 * Revision 1.7  1992/10/18  11:34:06  smilie
 * fixed up a few problems with dialing
 *
 * Revision 1.6  1992/10/16  08:55:36  smilie
 * removed lockfile stuff, and moved to lockfile.c
 *
 * Revision 1.3  1992/08/09  05:14:21  smilie
 * cleaned up
 *
 * Revision 1.1  1992/06/14  16:17:58  smilie
 * Initial revision
 *

*************************************************************************/

/* Feature test switches */
#define _POSIX_SOURCE 1
#define _MODEM_C

/* System Headers */
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <signal.h>
#include <pwd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <string.h>

/* Local Headers */
#include "nixmail.h"
#include "lockfile.h"

/* Macros */

/* File scope variables */

static char modem_rcsid[] = "$Id: modem.c,v 1.8 1992/12/06 09:36:30 smilie Exp $";
#define RCSID modem_rcsid

static struct termios oldt;	/* TTY Term settings */

char lock[256];			/* Lock file */

/* External variables */

/* External Functions */

/* Structures and unions */

/* Functions */

/************************************************************************
				GET_SPEED
*************************************************************************/
int get_speed(int spd)
{
/**/
switch(spd)
	{
	case   300: return B300;
	case  1200: return B1200;
	case  2400: return B2400;
	case  4800: return B4800;
	case  9600: return B9600;
	case 19200: return B19200;
	case 38400: return B38400;
	}
return 0;
}
/************************************************************************
				UNLOCK_PORT
-------------------------------------------------------------------------
Unlock the port
*************************************************************************/
int unlock_port(void)
{
char tmps[100];
/**/
/* Remove device path */
sscanf(CONFIG.port, "/dev/%s", tmps);
/* Make full path to lock file */
sprintf(lock, "/usr/spool/uucp/LCK..%s", tmps);
if (checklock(lock) == TRUE)
	{
	#ifdef DEBUG
	flog("Port [%s] was locked.. trying later\n", lock);
	#endif
	exit(ERRET_LOCKED);
	}

makelock(lock);	

/* Send a few CR's to wake the Modem server up */
fprintf(MFHO, "\r\r\r\r\r");
return TRUE;
}
/************************************************************************
				PARSE_RETS
-------------------------------------------------------------------------
Convert |'s in a string to Carriage Returns.

Returns a STATIC array
*************************************************************************/
char *parse_rets(char *instr)
{
static char ret_str[256];
char *ss = instr;
char *s = ret_str;
/**/
while (*ss)
	{
	if (*ss == '|')
		*s = 13;	/* CR */
	else
		*s = *ss;
	s++; ss++;
	}
return ret_str;
}
/*************************************************************************
			    INITIALISE_MODEM
*************************************************************************/
int initialise_modem()
{
int stop = FALSE;
char instr[256];
int retry = 0;  
/**/
#ifdef DEBUG
flog("Send to modem '%s'/'%s'\n", CONFIG.modeminit, parse_rets(CONFIG.modeminit));
#endif
fprintf(MFHO, "%s", parse_rets(CONFIG.modeminit));	/* Send INIT String */
fflush(MFHO);					/* Flush TTY */
while (!stop)
	{
	if (fgetsraw(instr, sizeof(instr), MFHI, 5))	/* Get return */
		{
		strupr(instr);
		#ifdef DEBUG
		flog("[%s]\n", instr);
		#endif
		if (strncmp(instr, "OK", 2) == 0)
			stop = TRUE;
		}
	else
		{
		retry++;
		if (retry > 10)
			{
			flog("initialise_modem: timeout waiting for 'OK'\n");
			return FALSE;
			}
		flog("Timout initialising modem, retrying..\n");
		#ifdef DEBUG
		flog("Send to modem '%s'\n", CONFIG.modeminit);
		#endif
		fprintf(MFHO, "%s", parse_rets(CONFIG.modeminit));	/* Send INIT String */
		fflush(MFHO);					/* Flush TTY */
		}
	}
return TRUE;
}
/************************************************************************
				OPEN_MODEM
*************************************************************************/
int open_modem(void)
{
struct termios t;
int fnum;
/**/

/* Unlock the Modem port */
(void)unlock_port();

/* Open the Modem PORT device */
fnum = open(CONFIG.port, O_RDWR);

if (fnum == -1)
	{
	flog("open_modem: Problem opening Modem Port '%s' : \n", CONFIG.port, strerror(errno));
	exit(ERRET_FATAL);
	}
	
if ((MFHO = fdopen(fnum, "w")) == NULL)
	{
	flog("open_modem: Could not open port %s for writing\n", CONFIG.port);
	exit(ERRET_FATAL);
	}
if ((MFHI = fdopen(fnum, "r")) == NULL)
	{
	flog("open_modem: Could not open port %s for reading\n", CONFIG.port);
	exit(ERRET_FATAL);
	}	

/* Flush any rubbish from the port */
tcflush(fnum, TCIOFLUSH);
	
/* Save away TTY Terminal Settings */
tcgetattr(fnum, &oldt);
tcgetattr(fnum, &t);

/* Set the TTY Terminal Settings */
t.c_iflag = (ISTRIP|ICRNL);
t.c_cflag = (CREAD|CS8|HUPCL|get_speed(CONFIG.speed));
t.c_lflag = (ICANON); 
t.c_oflag = (OPOST);
t.c_cc[VEOF] = 0x04;
t.c_cc[VEOL] = 0x0A;
t.c_cc[VERASE] = _POSIX_VDISABLE;
t.c_cc[VINTR] = _POSIX_VDISABLE;
t.c_cc[VKILL] = _POSIX_VDISABLE;
t.c_cc[VQUIT] = _POSIX_VDISABLE;
t.c_cc[VSUSP] = _POSIX_VDISABLE;
t.c_cc[VSTART] = 0x11;
t.c_cc[VSTOP] = 0x13;

/* Set SERIAL Speed */
/*cfsetispeed(&t, B2400);
cfsetospeed(&t, B2400);*/

/* Set terminal settings */
tcsetattr(fileno(MFHI), TCSANOW, &t);
fflush(MFHO);
#ifdef DEBUG
flog("open_modem: Port SETTINGS set\n");
#endif

initialise_modem();

return TRUE;
}

/************************************************************************
			PARSE_MODEM_RETURN
*************************************************************************/
int parse_modem_return(char *str, int *spd)
{
int num;
char connect_str[20];
/**/
if (strncasecmp(str, "AT", 2) == 0)
	return MDM_COMMAND;
else
if (strncasecmp(str, "RINGING", 7) == 0)
 	return MDM_RINGING;
else
if (strncasecmp(str, "NO CARRIER", 10) == 0)
	return MDM_NOCARRIER;
else
if (strncasecmp(str, "NO ANSWER", 9) == 0)
	return MDM_NOANSWER;
else
if (strncasecmp(str, "NO DIALTONE", 11) == 0)
	return MDM_NODIALTONE;
else
if (strncasecmp(str, "BUSY", 4) == 0)
	return MDM_BUSY;
else
	{
	num = sscanf(str, "%s %u\n", connect_str, spd);
	if (strcasecmp(connect_str, "CONNECT") == 0)
		{
		if (num == 1)
			{
			*spd = 300;
			return MDM_CONNECT;
			}
		if (num == 2)
			{
			return MDM_CONNECT;
			}
		}
	}
return MDM_UNKNOWN;
}

/************************************************************************
				DIAL_SYSTEM
*************************************************************************/
int dial_system(void)
{
char ret[50];
int stop = FALSE;
int ret_speed, retnum;
struct termios t;
/**/

tcgetattr(fileno(MFHO), &t);

flog("Dialing %s\n", CONFIG.number);

/* Send Dialing String */
fprintf(MFHO, "%s%s%s\r", CONFIG.dialpre, CONFIG.number, parse_rets(CONFIG.dialsuf));
fflush(MFHO);
sleep(1);
tcflush(fileno(MFHO), TCIOFLUSH);

while (!stop)
	{
	fgetsraw(ret, sizeof(ret), MFHI, 100);	
	#ifdef DEBUG
	flog("[%s]\n", ret);
	#endif
	if (ret[0])
		{
		retnum = parse_modem_return(ret, &ret_speed);
		#ifdef DEBUG
		flog("Returned from parse_modem_return == '%u'\n", retnum);
		#endif

		switch(retnum)
			{
			case MDM_RINGING:
				break;
			case MDM_COMMAND:
				break;
			case MDM_UNKNOWN:
				flog("dial_system: Unknown modem return code [%s]\n", ret);
				return FALSE;
				break;
			case MDM_NOCARRIER:
				flog("dial_system: Connect Failed\n");
				return FALSE;
				break;
			case MDM_NOANSWER:
				flog("dial_system: Remote modem did not answer\n");
				return FALSE;
				break;
			case MDM_BUSY:
				flog("dial_system: System is BUSY\n");
				return FALSE;
				break;
			case MDM_VOICE:
				flog("dial_system: System returned VOICE\n");
				return FALSE;
				break;
			case MDM_NODIALTONE:
				flog("dial_system: There is no dial tone\n");
				return FALSE;
				break;
			case MDM_CONNECT:
				flog("%s\n", ret);		
				if (!CONFIG.locked)
					{
					/* Set the SERIAL speed */
					cfsetispeed(&t, get_speed(ret_speed));
					cfsetospeed(&t, get_speed(ret_speed));
					tcsetattr(fileno(MFHO), TCSANOW, &t);
					}
				stop = TRUE;
				break;
			}
		}
	}

return TRUE;
}
/************************************************************************
			GET_PORTSETTINGS
-------------------------------------------------------------------------
*************************************************************************/
int get_portsettings(void)
{
int m;
/**/
ioctl(fileno(MFHO), TIOCMGET, &m);
return m;
}
/************************************************************************
			SET_PORTSETTINGS
-------------------------------------------------------------------------
*************************************************************************/
void set_portsettings(int in)
{
/**/
ioctl(fileno(MFHO), TIOCMSET, &in);
}
/************************************************************************
				DISCONNECT
-------------------------------------------------------------------------
			   Disconnect the modem
*************************************************************************/
void disconnect()
{
int mps;	/* Modem Port Settings */
/**/
mps = get_portsettings();

/* Check that carrier is present */
while(!(mps & TIOCM_CD))
	{
	mps &= ~TIOCM_DTR;
	/* Drop DTR */
	set_portsettings(mps);
	mps = get_portsettings();
	}

/* Reset TTY settings */
tcsetattr(fileno(MFHI), TCSAFLUSH, &oldt);

/* Remove Lock Files */
rmlock(lock);
}

