/*
 *  GETTY.C
 *
 *  (C)Copyright 1989, Matthew Dillon, All Rights Reserved
 *
 *  Uses:
 *	GETTY:PASSWD
 *	GETTY:LOGFILE
 *	GETTY:Getty-Header
 *
 *  GETTY   <options>	<modem-commands>
 *
 *	-Sdevicenam Tells GETTY which serial.device to use, default
 *		    serial.device
 *
 *	-Uunitnum   Tells GETTY which unit number to use, default 0
 *
 *	-A	    Always talk to the modem at the first baud
 *		    rate in the -B list and ignore the baud rate
 *		    in the CONNECT message.
 *
 *	-7	    use SERF_7WIRE while online.
 *
 *	-Bn	    Set baud rate.  If specified multiple times the
 *		    first one is the default and remaining ones
 *		    switched to when a BREAK is received after a
 *		    connect.  Also determines how CONNECT messages
 *		    are interpreted.  A CONNECT with no number uses
 *		    the first -B entry while CONNECTs with numbers
 *		    use those numbers regardless of any -B entries.
 *
 *	-Mc	    Set modem type:
 *			c = m	= multimodem
 *			    h	= hays
 *			    d	= dumb (no AT or +++ cmds are ever sent),
 *				  normally used with only one specified
 *				  baud rate.
 *
 *	-m1	    Turn on the modem speaker during dialing/connect
 *		    (default is -m0)
 *
 *	-h0	    Ignore CD (carrier detect), default is to use
 *		    carrier detect.
 *
 *	-c0	    Ignore CONNECT message (i.e. you are not connected
 *		    to a modem, usually combined with -h0).  Default is
 *		    to expect a CONNECT message.
 *
 *	-d0	    do not use DTR to drop connection.	Default is
 *		    to drop DTR to drop a connection.  If this option
 *		    is chosen the +++ and ATH0 sequence will be used
 *		    to drop a connection.
 *
 *	-xn	    Set debug level.  Also causes log output to go
 *		    to stdout instead of GETTY:LOGFILE
 *
 *	-ln	    n = 0:  Do not add -HLINK to command line
 *		    n = 1:  Add -HLINK to commands started by Getty
 *
 *	-bn	    n = 0:  Do not add -BAUD to command line
 *		    n = 1:  Add -BAUD <baud> to commands started
 *	Added by Christian Wolf (chris@wolfhh.hanse.de)
 *
 *	-un	    n = 0:  Do not add -USER to command line
 *		    n = 1:  Add -USER <user> to commands started
 *
 *
 *	-0	    QUIT - Kills any running getty for the specified
 *		    port.
 *
 *	Any fields specified without a '-' are considered modem commands
 *	used to initialize/reinitialize the modem.  Up to 16 fields may
 *	be specified (each is sent to the modem in 1 second intervals)
 */

/*
 * FAX Modifications 1991-1994 by David Varley
 *
 * Add entry in UULIB:passwd along the lines of
 * fax,*,0,0,(Fax_Connection),ram:,AFAX:Faa
 *
 * (Faa is my Fax Auto-Answer program. Uses Getty, Device, Unit flags)
 *
 * It appears that we do not get CD when answering Fax, just +FCON
 * So, have to wait on another event, being serial data received,
 * and look for +FCON.
 * This is a drag, as can wake on noise, etc. DAV
 *
 * 1.15 - 8Nov91 DAV
 * As of TR-29.2/90-21R6 Fax responds +FCO
 *  - Provide +FCO as subset of +FCON
 *
 * 12Dec92 DAV
 * ZyXEL modem has various DCE/DTE rate options. Allow GETTY to use them.
 * (This requires S register manipulation)
 *
 * 31Jan92 DAV
 * Kludge-fix for A2232 serial driver not flushing buffers
 *
 * 12Feb92 DAV
 * Extend Fax bitmap register to Open/Close serial on modem-init
 *
 * -Fn		      n=bit-mapped register
 *   bit
 *    0  =  1 (always set for Fax)
 *    1  =  0 Set rate to 19200 after +FCO
 *	 =  1	Don't set rate to 19200 after +FCO, DCE/DTE locked
 *    2  =  0 Dont Open/Close serial on modem-init
 *	 =  1	Open/Close serial on modem-init
 *    3  =  0 FCLASS=2
 *       =  1 FCLASS=2.0
 *
 * 3Oct93 DAV
 * Added timeout extension on "CARRIER" as suggested by Dave Gilbert
 * to work better with Supra. Version 13F5
 *
 * 14Nov94 DAV
 * With new standard Class-2.0, we cannot depend on receiving fax when
 * in Class-0 with FAA=1 giving us connect details. Instead, keep in
 * Class-2.0 mode, FAA=1 will drop us into data mode on data connect.
 * However callout users expect the modem to be in Class-0, so we must
 * change mode before handing over.
 * Class-2.0 mode set by bit three, thus -F8, or -F14 (see above).
 *
 * Also changed order of shutdown operations in myexit(), which was
 * causing crashes (from first V1.17-beta, as far as I know).
 */

/*
 * Feb 96 -- Modifications by Martin Hohl
 * Extend Fax bitmap register to Open/Close serial on modem-init
 * -Fn		      n=bit-mapped register
 *   Bit
 *    0  =  1 (always set for Fax)
 *    1  =  0 Set rate to 19200 after +FCO
 *	 =  1	Don't set rate to 19200 after +FCO, DCE/DTE locked
 *    2  =  0 Dont Open/Close serial on modem-init
 *	 =  1	Open/Close serial on modem-init
 *    3  =  0 FCLASS=2
 *       =  1 FCLASS=2.0
 *    4  =  0 FCLASS depends on bit 3
 *       =  1 FCLASS=Z (ZyXEL E2864 mode)
 *    5  =  0 if (Bit 4 = 1) AT+FZF=3 (ZyXEL E2864 mode)
 *       =  1 if (Bit 4 = 1) AT+FZF=5 (ZyXEL E2864 mode)
 *    7  =  1 AT+FCLASS=2+FECM=2+FAA=1 (experimental)
 *
 * Issue ATA to ZyXEL E2864 if DRAM buffer contains a fax.
 * As of firmware v1.11 ZyXEL E2864 disregards S0 setting and uses
 * RING msg to signal a fax in DRAM buffer to the host.
*/

/*
 * Modifications to use OwnDevUnit.library for serial port locking
 * done by Christopher A. Wichura (caw@miroc.chi.il.us)
 */

#include "defs.h"

#ifdef __SASC_60
#include "fifolib_pragmas.h"
#endif

#ifdef __STDC__
#include <string.h>
#include <stdlib.h>
#endif

Prototype char	DeviceName[64];
Prototype long	DeviceUnit;
Prototype BPTR	NullFH;
Prototype Iot	  Iot0;
Prototype Iot	  Iot1;
Prototype IOSer   Ios;
Prototype IOSer   Iosr;
Prototype IOSer   Iosw;
Prototype IOSer   Ioss;
Prototype long	  MainTimeout;

Prototype char	  Iot0IP;	/*  Iot0 in progress	  */
Prototype char	  Iot1IP;	/*  Iot1 in progress	  */
Prototype char	  IosrIP;	/*  serial read in prog   */
Prototype char	  IoswIP;	/*  serial write in prog  */

Prototype char	HardLink;
Prototype char	BaudReport;
Prototype long	BaudRate;
Prototype char	UserReport;	/* Added by Christian Wolf (chris@wolfhh.hanse.de) */

Prototype struct MsgPort *IoSink;
Prototype char	LoginBuf[32];
/* Added by Christian Wolf (chris@wolfhh.hanse.de) */
char	UserReport	= 0;
Prototype char	PasswdBuf[32];

IDENT (".17");

static char ver_string[] = "$VER: mhgetty 1.16.17 "__AMIGADATE__;

char	*CopyRight = "(c)Copyright 1989, Matthew Dillon, All Rights Reserved\r\n";
char	*ComPortName = NULL;

char	Fax		= 0;
char	FIgnoreCD	= 0;
char	DeviceName [64] = { "serial.device" };
long	DeviceUnit	= 0;
BPTR	NullFH		= 0;
char	SpeakerLevel	= 0;
char	AnswerRing	= 2;	/*  default, answer on second ring */
char	SpeakerOpt	= 0;
char	IgnoreCD	= 0;
char	IgnoreConnect	= 0;
char	IgnoreDTR	= 0;
char	BaudAdjust	= 0;
char	DropOnExit	= 1;
char	ModemType	= 'h';
char	ZeroOption	= 0;
char	HardLink	= 0;
char	BaudReport	= 0;
char	Wire7		= 0;		/*  use 7 wire while online */
long	Bauds [16]	= { 9600 };	/*  up 16 baud rates	    */
long	BaudRate;
char	*AtFields [16];

struct MsgPort *ComPort = NULL;      /*  Background Getty    */
struct MsgPort *IoSink	= NULL;      /*  Sink for IO reqs.   */
long	IoSinkMask;
long	ComPortMask;

Iot	Iot0;		/*  login timeout	*/
Iot	Iot1;		/*  check other things	*/
IOSer	Ios;		/*  parameters req	*/
IOSer	Iosr;		/*  serial read-req	*/
IOSer	Iosw;		/*  write req		*/
IOSer	Ioss;		/*  synchronous req	*/

long	MainTimeout = 3;

char	Iot0IP; 	/*  Iot0 in progress	*/
char	Iot1IP; 	/*  Iot1 in progress	*/
char	IosrIP; 	/*  serial read in prog */
char	IoswIP; 	/*  serial write in prog*/
char	CarrierDetectEvent;
char	TimeoutEvent;
char	PortRequestedEvent;
char	SerialEvent;
char	OptionEvent;

struct Library *OwnDevUnitBase = NULL;
char	NotifyBit = -1;
long	NotifyMask;

char	LoginBuf[32];
char	PasswdBuf[32];
char	RxBuf[32];
char	ScrBuf[256];

void myexit (void);
void FLockSerialPort (void);
void FUnLockSerialPort (void);
void InitModem (void);
void Disconnect (int);
void WaitEvents (void);
void Timeout (int);
void OpenSerial (void);
void CloseSerial (void);
void SetParams (int);
void RxStart (void);
void RxStop (void);
long GetStatus (void);
void ClearRecv (void);
void ResetSP (void);
void DoOptions (int, char **);
void DoLogin (void);
void SerPuts (char *);
char *GetLine (int);

Prototype void NiceAbortIO (void *);
Prototype void RxStart (void);
Prototype void RxStop (void);
Prototype long GetStatus (void);
Prototype void Timeout (int);

/* from passwd.c */
int CheckLoginAndPassword(void);

int
main (int ac, char **av)
{
	puts (CopyRight);
	fflush (stdout);
	atexit (myexit);

	LogProgram = "Getty";
	LogWho = LoginBuf;
	LogFile = "Getty:LOGFILE";
	PasswdFile = "Getty:Passwd";

	DoOptions (ac, av);

	IoSink = CreatePort (NULL, 0);
	IoSinkMask = 1 << IoSink->mp_SigBit;

	ComPortName = malloc (strlen (DeviceName) + 20);
	if (!ComPortName) {
		fprintf (stderr, "No memory for portname!\n");
		exit (30);
	}
	sprintf (ComPortName, "Getty.%s.%ld", DeviceName, DeviceUnit);

	Forbid();
	if (ComPort = FindPort (ComPortName)) {
		GMsg
			msg;

		msg.Cmd = 'O';
		msg.Data1 = ac;
		msg.Data2 = (void *) av;
		msg.Msg.mn_ReplyPort = IoSink;
		PutMsg (ComPort, &msg.Msg);
		ComPort = NULL;
		WaitPort (IoSink);
		Remove (&msg.Msg.mn_Node);
		Permit ();
		puts ("Options updated");
		exit (0);
	}
	ComPort = CreatePort (ComPortName, 0L);
	Permit ();

	ComPortMask = 1L << ComPort->mp_SigBit;

	NullFH = Open ("NULL:", 1006);
	if (!NullFH) {
		ulog (-1, "GETTY REQUIRES NULL: HANDLER!");
		puts ("Requires NULL: handler!");
		exit (30);
	}
	if (LogToStdout == 0) {
		freopen ("NULL:", "r", stdin);
		freopen ("NULL:", "w", stdout);
		freopen ("NULL:", "w", stderr);
	}

	/*
	 *  Grab OwnDevUnit.library stuff
	 */

	if (!(OwnDevUnitBase = OpenLibrary (ODU_NAME, 0))) {
		fprintf (stderr, "Unable to open %s\n", ODU_NAME);
		exit (20);
	}

	if ((NotifyBit = AllocSignal (-1)) == -1) {
		fprintf (stderr, "Couldn't allocate notify signal.\n");
		exit (20);
	}

	NotifyMask = 1L << NotifyBit;

	/*
	 *  Timer Device
	 */

	Iot0.tr_node.io_Message.mn_ReplyPort = IoSink;
	if (OpenDevice ("timer.device", UNIT_VBLANK, (struct IORequest *) &Iot0, 0L)) {
		Iot0.tr_node.io_Device = NULL;
		exit (20);
	}
	Iot0.tr_node.io_Command = TR_ADDREQUEST;
	Iot1 = Iot0;

	Iot1.tr_time.tv_secs = MainTimeout;
	Iot1.tr_time.tv_micro= 0;
	SendIO ((struct IORequest *) &Iot1);
	Iot1IP = 1;

	/*
	 *  Main Loop
	 */

	FLockSerialPort ();
	OpenSerial ();
	InitModem ();

	for (;;) {
		WaitEvents ();
		if (OptionEvent) {
			puts ("OE");
			InitModem ();
			OptionEvent = 0;
		}
		if (CarrierDetectEvent) {
			ulog (2, "Carrier Detected");
			DoLogin ();
			ulog (2, "Getty Disconnecting");
			Disconnect (1);
			InitModem ();
			OptionEvent = 0;
		}
		if (Fax && SerialEvent) {
			IgnoreCD = 1;
			CarrierDetectEvent = 1;
			ulog (2, "Serial Data Detected");
			DoLogin ();
			IgnoreCD = FIgnoreCD;
			ulog (2, "Getty Disconnecting");
			Disconnect (1);
			InitModem ();
			OptionEvent = 0;
		}
		if (PortRequestedEvent) {
			ulog (2, "External Port Request");
			CloseSerial ();
			FUnLockSerialPort ();

			PortRequestedEvent = 0;

			for (;;) {
				while (!AvailDevUnit (DeviceName, DeviceUnit))
					WaitEvents ();

				/* if AttemptDevUnit returns NULL then we got the lock back
				 * and can break out of the loop and continue on.
				 * Otherwise, someone swiped the device out from under our
				 * feet in the time between our AvailDevUnit() call and our
				 * AttemptDevUnit() call.
				 */

				if (AttemptDevUnit (DeviceName, DeviceUnit, "Getty", NotifyBit) == NULL)
					break;
			}

			OpenSerial ();
			ulog (2, "Getty Back on port");
			Disconnect (1);
			InitModem ();
			OptionEvent = 0;
		}
	}

	exit (0);
}

void
DoOptions (int ac, char **av)
{
	int
		i,
		bi = 0,
		fi = 0;
	long
		v;

	LogLevel = 1;

	for (i = 1; i < ac; ++i) {
		char
			*ptr = av [i];

		if (*ptr != '-') {
			if (fi != arysize (AtFields))
				AtFields [fi++] = ptr;
			else
				fprintf (stderr, "AT field overflow\n");
			continue;
		}
		if (*++ptr)		/*  skip -	*/
			++ptr;		/*  and option	*/
		v = atoi (ptr);
		switch (ptr [-1]) {
			case 'F':
				Fax = v | 1;
				break;
			case '0':
				ZeroOption = 1;
				break;
			case '7':
				Wire7 = 1;
				break;
			case 'u':
				/* Added by Christian Wolf (chris@wolfhh.hanse.de) */
				UserReport = v;
				break;
			case 'S':
				strcpy (DeviceName, ptr);
				break;
			case 'U':
				DeviceUnit = v;
				break;
			case 'M':
				ModemType = *ptr;
				break;
			case 'A':
				BaudAdjust = 1;
				break;
			case 'B':
				if (bi != arysize (Bauds))
					Bauds [bi++] = v;
				else
					fprintf (stderr, "-B field overflow\n");
				break;
			case 'm':
				SpeakerOpt = 1;
				SpeakerLevel = v;
				break;
			case 'r':
				AnswerRing = v;
				break;
			case 'h':
				FIgnoreCD = !v;
				IgnoreCD = !v;
				break;
			case 'c':
				IgnoreConnect = !v;
				break;
			case 'd':
				IgnoreDTR = !v;
				break;
			case 'x':
				LogLevel = v;
				LogToStdout = (v >= 9);
				break;
			case 'n':
				/*NoBugs = 1;*/
				break;
			case 'l':
				HardLink = v;
				break;
			case 'b':
				BaudReport = v;
				break;
			default:
				fprintf (stderr, "Warning, Bad option: -%s\n", ptr);
				break;
		}
	}

	if (fi && fi != arysize (AtFields))
		AtFields [fi] = NULL;
	if (bi && bi != arysize (Bauds))
		Bauds [bi] = 0;

	return;
}

void
myexit (void)
{
	CloseSerial ();
	if (ComPort) {
		DeletePort (ComPort);
		ComPort = NULL;
	}
	if (Iot0IP) {
		AbortIO ((struct IORequest *) &Iot0);
		WaitIO ((struct IORequest *) &Iot0);
	}
	if (Iot1IP) {
		AbortIO ((struct IORequest *) &Iot1);
		WaitIO ((struct IORequest *) &Iot1);
	}
	if (Iot0.tr_node.io_Device) {
		CloseDevice ((struct IORequest *) &Iot0);
		Iot0.tr_node.io_Device = NULL;
	}
	if (IoSink) {
		DeletePort (IoSink);
		IoSink = NULL;
	}
	if (NullFH) {
		Close (NullFH);
		NullFH = 0;
	}

	/* clean up stuff related to OwnDevUnit.library */

	if (OwnDevUnitBase) {
		FreeDevUnit (DeviceName, DeviceUnit);	 /* if we don't own it the */
		CloseLibrary (OwnDevUnitBase);		 /* lib will ignore the free */
		OwnDevUnitBase = NULL;
	}

	if (NotifyBit != -1) {
		FreeSignal(NotifyBit);
		NotifyBit = -1;
	}

	return;
}

void
FLockSerialPort (void)
{
	char
		*error;

	if (error = LockDevUnit (DeviceName, DeviceUnit, "Getty", NotifyBit)) {
		fprintf (stderr, "Unable to lock serial port: %s\n", &error [1]);
		exit (20);
	}

	return;
}

void
FUnLockSerialPort (void)
{
	FreeDevUnit (DeviceName, DeviceUnit);

	return;
}

void
InitModem (void)
{
	char
		buf [64];
	short
		i;

	RxStop ();
	ulog (2, "Init Modem");
	Ios.io_Baud = BaudRate = Bauds [0];    /*  reset baud rate */
	SetParams (0);

	switch (ModemType) {
		case 'm':               /*  Multi-Modem     */
			SerPuts ("\010\010\r");
			Delay (10);
			SerPuts ("AT\r");
			Delay (50);
			sprintf (buf, "ATM%dS0=%dX4$BA%d&E%d\r",
				SpeakerLevel,
				AnswerRing,
				!BaudAdjust,
				(Wire7) ? 4 : 3
			);
			SerPuts (buf);
			break;

		case 'h':               /* Hayes compatible */
			SerPuts ("\010\010\r");
			Delay (10);
			if (Fax & 16) {
			  SerPuts ("AT&F\r");
			  Delay (50);
			}
			SerPuts ("ATZ\r");
			if (Fax & 16) Delay(50);
			else Delay (120);
			strcpy (buf, "AT");
			if (SpeakerOpt)
				sprintf (buf + strlen (buf), "M%d", SpeakerLevel);
			if (Fax & 16) sprintf(buf + strlen (buf), "S0=1S71.6=0\r");
			else sprintf(buf + strlen (buf), "S0=0\r");
                        SerPuts(buf);
                        ulog(8,buf); /* DEBUG */
			Delay (20);
                        strcpy (buf, "AT");
			if (Fax) {
			  if (Fax & (1<<4)) {
                            if (Fax & (1<<5)) strcat(buf,"+FCLASS=Z+FCR=1+FAA=1+FZF=5+FECM=2");
                            else strcat(buf,"+FCLASS=Z+FCR=1+FAA=1+FZF=3+FECM=2");
                          }
			  else if (Fax & (1<<7)) {
			    strcat(buf,"+FCLASS=2+FCR=1+FAA=1+FECM=2");
			  }
                          else sprintf (buf + strlen (buf),
					"+FCLASS=%s+FCR=1+FAA=1",
					(Fax & 8) ? "2.0+FNR=1,1,1,0" : "0");
                        };
			strcat (buf, "\r");
			SerPuts (buf);
                        ulog(8,buf); /* DEBUG */
			break;

		case 'd':               /* Dumb */
			SerPuts ("\010\010\r");
			break;
	}

	if (!(Fax & 16))
	for (i = 0; i < arysize (AtFields) && AtFields [i]; ++i) {
		Delay (50);
		SerPuts (AtFields [i]);
		SerPuts ("\r");
	}

	Delay (20);
	ClearRecv ();

	if (Fax) {
		/* 31JAN92 DAV - kludge around possible A2232 CMD_FLUSH problem */
		for (;;) {
			RxStart ();
			if (CheckIO ((struct IORequest *) &Iosr))
				WaitIO ((struct IORequest *) &Iosr);
			else
				break;
		}
	}

	return;
}

void
Disconnect (int dropdtr)
{
	short
		retry = (IgnoreDTR) ? 2 : 10;

	ulog (2, "Disconnect drop = %ld", dropdtr);

	if (Fax & 4) {
		/* DAV 12 FEB 92 */
		/* Don't know why, but operation on A2232 is unreliable without this */
		CloseSerial ();
		Delay (100);
		OpenSerial ();
	}

	RxStop ();

	if (IgnoreCD) {
		return;
	}

	while (dropdtr) {
		short
			i;
		long
			status = GetStatus ();

		if (status & CIAF_COMCD)	/*  no carrier	*/
			break;

		SetParams (0);

		CloseDevice ((struct IORequest *) &Ios);
		Ios.IOSer.io_Device = NULL;    /*  so exit doesn't reclose */
		for (i = 0; i < 5; ++i) {      /*  5 seconds   */
			Delay (50);
			if (SetSignal (0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C)
				exit (23);
		}

		/*
		 *  Use Iosr to re-open serial device so we don't loose
		 *  our config.
		 */

		if (Wire7)
			Ios.io_SerFlags |= SERF_7WIRE;
		else
			Ios.io_SerFlags &= ~SERF_7WIRE;

		if (OpenDevice (DeviceName, DeviceUnit, (struct IORequest *) &Ios, 0)) {
			Ios.IOSer.io_Device = NULL;
			exit (22);
		}
		Iosr.IOSer.io_Device = Ios.IOSer.io_Device;
		Iosw.IOSer.io_Device = Ios.IOSer.io_Device;
		Ioss.IOSer.io_Device = Ios.IOSer.io_Device;
		Iosr.IOSer.io_Unit = Ios.IOSer.io_Unit;
		Iosw.IOSer.io_Unit = Ios.IOSer.io_Unit;
		Ioss.IOSer.io_Unit = Ios.IOSer.io_Unit;
		SetParams (1);

		status = GetStatus ();

		if (status & CIAF_COMCD)	/*  no carrier	*/
			break;

		if (IgnoreDTR) {
			if (ModemType != 'd') {
				Delay (70);
				SerPuts ("+++");
				Delay (70);
				SerPuts ("\010\010\r");
				Delay (10);
				SerPuts ("ATZ\r");
				Delay (50*3);
				SerPuts ("ATH0\r");
				Delay (120);
			}
		}

		/*
		 *  Loop until carrier lost
		 */

		if (--retry == 0) {
			if (IgnoreDTR == 0)
				fprintf (stderr, "Getty: unable to disconnect!\n");
			break;
		}
	}

	return;
}

void
WaitEvents (void)
{
	short
		event = 0;
	unsigned long
		mask = 0;

	while (event == 0) {
		struct GMsg
			*msg;
		struct IORequest
			*ior;

		while (msg = (GMsg *) GetMsg (ComPort)) {
			if (msg->Cmd == 'O') {
				/* ignore all others */
				DoOptions(msg->Data1, msg->Data2);
			}
			ReplyMsg ((struct Message *) msg);
			if (ZeroOption)
				exit (0);
			OptionEvent = 1;
			++event;
		}

		while (ior = (IORequest *) GetMsg (IoSink)) {
			if (ior == (IORequest *) &Iot0) {
				Iot0IP = 0;
				TimeoutEvent = 1;
				++event;
			}

			if (ior == (IORequest *) &Iot1) {
				Iot1.tr_time.tv_secs = MainTimeout;
				Iot1.tr_time.tv_micro= 0;
				SendIO ((struct IORequest *) &Iot1);
				Iot1IP = 1;

				if (Ios.IOSer.io_Device) {
					if ((GetStatus () & CIAF_COMCD) == 0)
						CarrierDetectEvent = 1;
					else
						CarrierDetectEvent = 0;
				}
				++event;
			}

			if (ior == (IORequest *) &Iosr) {
				IosrIP = 0;
				SerialEvent = 1;
				++event;
			}
		}

		if ((mask & NotifyMask) || SetSignal (0L, NotifyMask) == NotifyMask) {
			PortRequestedEvent = 1;
			++event;
		}

		if (event == 0) {
			mask = Wait (SIGBREAKF_CTRL_C | IoSinkMask | ComPortMask | NotifyMask);
			if (mask & SIGBREAKF_CTRL_C)
				exit (0);
		}
	}

	return;
}

void
Timeout (int n)
{
	TimeoutEvent = 0;
	if (Iot0IP) {
		AbortIO ((struct IORequest *) &Iot0);
		WaitIO ((struct IORequest *) &Iot0);
		Iot0IP = 0;
	}
	if (n) {
		Iot0.tr_time.tv_secs = n;
		Iot0.tr_time.tv_micro= 0;
		SendIO ((struct IORequest *) &Iot0);
		Iot0IP = 1;
	}

	return;
}

void
OpenSerial (void)
{
	Ios.IOSer.io_Message.mn_ReplyPort = IoSink;
	Ios.io_SerFlags = SERF_XDISABLED | SERF_SHARED;
	if (Wire7)
		Ios.io_SerFlags |= SERF_7WIRE;

	if (OpenDevice (DeviceName, DeviceUnit, (struct IORequest *) &Ios, 0L)) {
		Ios.IOSer.io_Device = NULL;
		exit (21);
	}

	Iosr = Ios;
	Iosw = Ios;
	Ioss = Ios;

	Iosr.IOSer.io_Command = CMD_READ;   /*	asynchronous read  */
	Iosw.IOSer.io_Command = CMD_WRITE;  /*	asynchronous write */
	Ios.IOSer.io_Command = SDCMD_QUERY;
	DoIO ((struct IORequest *) &Ios);

	Ios.io_SerFlags = SERF_XDISABLED | SERF_RAD_BOOGIE | SERF_SHARED;
	Ios.io_ExtFlags = 0;
	Ios.io_Baud = Bauds [0];
	Ios.io_ReadLen = 8;
	Ios.io_WriteLen = 8;
	Ios.io_StopBits = 1;

	SetParams (0);

	return;
}

void
CloseSerial (void)
{
	if((Fax & 8) && Ios.IOSer.io_Device) {
		SerPuts("AT+FCLASS=0\r");
		Delay(50L);
	}
	if (IosrIP) {
		NiceAbortIO (&Iosr);
		WaitIO ((struct IORequest *) &Iosr);
		IosrIP = 0;
	}
	if (Ios.IOSer.io_Device) {
		CloseDevice ((struct IORequest *) &Ios);
		Ios.IOSer.io_Device = NULL;
	}
	return;
}

void
SetParams (int wire7)
{
	int
		error;

	wire7 = Wire7;

	RxStop ();

	if (wire7 && Wire7)
	    Ios.io_SerFlags |= SERF_7WIRE;
	else
	    Ios.io_SerFlags &= ~SERF_7WIRE;
	Ios.IOSer.io_Command = SDCMD_SETPARAMS;
	Ios.IOSer.io_Message.mn_Node.ln_Type = NT_MESSAGE;
	if (error = DoIO ((struct IORequest *) &Ios))
	    ulog (0, "SetParams Error %d", error);

	return;
}

void
RxStart (void)
{
	if (IosrIP) {
		NiceAbortIO (&Iosr);
		WaitIO ((struct IORequest *) &Iosr);
	}
	Iosr.IOSer.io_Data = (APTR) RxBuf;
	Iosr.IOSer.io_Length = 1;
	Iosr.IOSer.io_Message.mn_Node.ln_Type = NT_MESSAGE;
	IosrIP = 1;
	SendIO ((struct IORequest *) &Iosr);

	return;
}

void
RxStop (void)
{
	if (IosrIP) {
		NiceAbortIO (&Iosr);
		WaitIO ((struct IORequest *) &Iosr);
		IosrIP = 0;
	}

	return;
}

void
ClearRecv (void)
{
	int
		error;

	Ioss.IOSer.io_Command = CMD_CLEAR;
	Ioss.IOSer.io_Message.mn_Node.ln_Type = NT_MESSAGE;

	if (error = DoIO ((struct IORequest *) &Ioss))
		ulog (0, "Clear Error %d", error);

	SerialEvent = 0;

	if (IosrIP) {
		NiceAbortIO (&Iosr);
		WaitIO ((struct IORequest *) &Iosr);
		IosrIP = 0;
		RxStart ();
	}

	return;
}

void
ResetSP (void)
{
	int
		error;

	Ioss.IOSer.io_Command = CMD_RESET;
	Ioss.IOSer.io_Message.mn_Node.ln_Type = NT_MESSAGE;

	if (error = DoIO ((struct IORequest *) &Ioss))
		ulog (0, "Reset Error %d", error);

	return;
}

void
SerPuts (char *str)
{
	int
		error;

	Ioss.IOSer.io_Command = CMD_WRITE;
	Ioss.IOSer.io_Data = (APTR) str;
	Ioss.IOSer.io_Length = strlen (str);
	Ioss.IOSer.io_Message.mn_Node.ln_Type = NT_MESSAGE;
	if (error = DoIO ((struct IORequest *) &Ioss))
		ulog (0, "Write Error %d", error);

	return;
}

long
GetStatus (void)
{
	int
		error;
	long
		status = CIAF_COMCD;   /*  no carrier detect   */

	Ioss.IOSer.io_Command = SDCMD_QUERY;
	Ioss.IOSer.io_Message.mn_Node.ln_Type = NT_MESSAGE;
	if (error = DoIO ((struct IORequest *) &Ioss))
		ulog (0, "Query Error %d", error);
	status = Ioss.io_Status;
	if (IgnoreCD)
		status &= ~CIAF_COMCD;

	return status;
}

/*
 *  HANDLE LOGIN
 */

void
DoLogin (void)
{
	char
	    *ptr,
	    *str;
        short RingCounter = 0;
	short
	    retry = (IgnoreConnect) ? -1 : 5;

	Timeout (10);
	if (IgnoreConnect == 0) {
		while (ptr = GetLine (0)) {
			/*ulog(-1,"Received <%s>\n",ptr);*/
			if (Fax) {
				if (strnicmp(ptr, "RING", 4)==0) {
				  Timeout (40);
                                  RingCounter++;
				  if ((AnswerRing!=0) && (RingCounter >= AnswerRing)) {
/* Catch stored faxes from E2864 DRAM */
                                    SerPuts("ATA\r");
                                    RingCounter=0;
#if 0
/* Does'nt work, since faa doesn't use owndevunit.library, if invoked with -a option */
				    ulog(1, "Execute %s\n", "afax:c/faa -a");
				    Execute("afax:c/faa -a", NullFH, NullFH);
#endif
                                  }
                                }
#if 0
				else if (strnicmp (ptr, "RING", 4) == 0 || strnicmp (ptr, "CARRIER", 7) == 0) {
					Timeout (40);	 /* Extend timeout */
				}
#endif
				else if (strnicmp (ptr, "+FCO", 4) == 0) {
                                        if (Fax & 16) continue;
					if ((Fax & 2) == 0) {
						BaudRate = 19200;
						Ios.io_Baud = BaudRate;
						SetParams (1);
					}
					ulog (-1,"Fax Connection");

					strncpy (LoginBuf, "fax", sizeof (LoginBuf) - 1);
					PasswdBuf [0] = 0;
					if (CheckLoginAndPassword ()) {
						ulog (-1, "login %s", LoginBuf);
						RunPasswdEntry ();
						ulog (-1, "Fax Returned");
					}
					else {
						ulog (-1, "FaxConnect Disabled");
					}
					return;
				}
				else if (strnicmp (ptr, "CONNECT FAX", 11) == 0) {
                                        strncpy(LoginBuf,"-",sizeof(LoginBuf)-1);
					ulog(1,"Connect-Msg: %s",ptr);
					continue;
				}
				else if (strnicmp (ptr, "+FDM", 4) == 0) {
					Timeout (40);	 /* Extend timeout */
					continue;
				}

			}
			if ((!Fax) && (strnicmp(ptr, "RING", 4)==0)) {
			  Timeout (40);
                          RingCounter++;
			  if ((AnswerRing!=0) && (RingCounter >= AnswerRing)) {
			    SerPuts("ATA\r");
			    RingCounter=0;
                          }
			}
			else if (strnicmp (ptr, "CONNECT", 7) == 0) {
                                strncpy(LoginBuf,"-",sizeof(LoginBuf)-1);
				ulog(1,"Connect-Msg: %s",ptr);
				break;
			}
		}
		if (ptr == NULL)
			return;
	}

	str = ptr + 7;
	while (*str && (*str == 9 || *str == ' '))
		++str;
	if (*str >= '0' && *str <= '9')
		BaudRate = atoi (str);
	else
		BaudRate = Bauds [0];

	if (BaudAdjust) {
		ulog (9, "Connect Auto-Baud %d", BaudRate);
	}
	else {
		Ios.io_Baud = BaudRate;
		ulog (9, "Connect at %d baud", BaudRate);
	}

	SetParams (1);

	sleep (1);	       /* GettyHeader mangled without sleep */
	IgnoreCD = FIgnoreCD; /* Not fax if get here */

	{
		FILE
			*fi;

		if (fi = fopen ("Getty:Getty-Header", "r")) {
			while (fgets (ScrBuf, sizeof (ScrBuf), fi)) {
				SerPuts (ScrBuf);
				SerPuts ("\r");
			};
			fclose (fi);
		}
	}

	if (retry < 0)
		Timeout (0);	 /*  no timeout      */
	else
		Timeout (60);	 /*  60 seconds      */

	while (retry--) {   /*	login retries	*/
		char
			*ptr;

		if (retry < 0)	/*  no limit	    */
			retry = -1;
		if (IgnoreConnect && PortRequestedEvent)
			break;

		ClearRecv ();
		SerPuts ("\r\nLogin: ");
		if (ptr = GetLine (1)) {
			if (ptr [0] == 0)
				continue;
			strncpy (LoginBuf, ptr, sizeof (LoginBuf) - 1);
			PasswdBuf [0] = 0;
			if (CheckLoginAndPassword () == 0) {
				SerPuts ("Password: ");
				ptr = GetLine (0);

				if (ptr [0] == 0)
					continue;
				SerPuts ("\r\n");
				strncpy (PasswdBuf, ptr, sizeof (PasswdBuf) - 1);

				if (CheckLoginAndPassword () == 0) {
					SerPuts ("Login Failed.\r\n\n");
					ulog (0, "ERROR: Login Failed user='%s' pass='%s'", LoginBuf, PasswdBuf);
					Delay (50 * 4);
					continue;
				}
			}
			ulog (-1, "login %s", LoginBuf);
			RxStop ();
			RunPasswdEntry ();
		}
		break;
	}

	return;
}

/*
 *  Get a line, a timeout causes this call to return pronto NULL
 */

char *
GetLine (int echo)
{
	static char
		Buf [64];
	char
		buf [2];
	short
		i = 0,
		nl = 0;

	RxStart ();
	while (CarrierDetectEvent && TimeoutEvent == 0) {
		if (PortRequestedEvent && IgnoreConnect)
			return NULL;
		if (SerialEvent && IosrIP == 0) {
			SerialEvent = 0;
			if (Iosr.IOSer.io_Actual) {
				unsigned char
					c;

				switch (c = (unsigned char ) (RxBuf [0] & 0x7F)) {
					case 0x00:	/*  ignore NULs */
					case 0x7F:
					case 0xFF:
						break;

					case 13:
					case 10:
						if (nl == 0)
							nl = c;
						if (nl != c)
							break;
						if (echo)
							SerPuts ("\r\n");
						Buf [i] = 0;
						return Buf;

					case 8:
						if (i) {
							if (echo)
								SerPuts("\010 \010");
							--i;
						}
						break;

					default:
						if (i == sizeof (Buf) - 1) {
							if (echo)
								SerPuts ("\007");
						}
						else {
							Buf [i++] = c;
							if (echo) {
								buf [0] = c;
								buf [1] = 0;
								SerPuts (buf);
							}
						}
						break;
				}
			}
			RxStart ();
		}
		WaitEvents ();
	}
	return NULL;
}

/*
 *  bug fix for older A2232 serial.device's
 */

void
NiceAbortIO (void *ior)
{
	Disable ();
	if (((struct Message *) ior)->mn_Node.ln_Type != NT_REPLYMSG)
		AbortIO (ior);
	Enable ();

	return;
}
