

/*
 *  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 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
 *
 * 3Oct93 DAV
 * Added timeout extension on "CARRIER" as suggested by Dave Gilbert
 * to work better with Supra. Version 13F5
 */

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

#define FAX 1

#include "defs.h"

Prototype char	DeviceName[64];
Prototype long	DeviceUnit;
Prototype long	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 MsgPort *IoSink;
Prototype char	LoginBuf[32];
/* Added by Christian Wolf (chris@wolfhh.hanse.de) */
char	UserReport	= 0;
Prototype char	PasswdBuf[32];

#ifdef FAX
IDENT(".13F5");
#else
IDENT(".13");
#endif

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

#ifdef FAX
char	Fax = 0;
char	FIgnoreCD = 0;
#endif
char	DeviceName[64] = { "serial.device" };
long	DeviceUnit  = 0;
long	NullFH;
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];

MsgPort *ComPort;	/*  Background Getty	*/
MsgPort *IoSink;	/*  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;
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 *);

main(ac, av)
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);
    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 == NULL) {
	ulog(-1, "GETTY REQUIRES NULL: HANDLER!");
	puts("Requires NULL: handler!");
	exit(1);
    }
    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))) {
	puts("Unable to open " ODU_NAME);
	exit(20);
    }

    if ((NotifyBit = AllocSignal(-1)) == -1) {
	puts("Couldn't allocate notify signal.");
	exit(20);
    }

    NotifyMask = 1L << NotifyBit;

    /*
     *	Timer Device
     */

    Iot0.tr_node.io_Message.mn_ReplyPort = IoSink;
    if (OpenDevice("timer.device", UNIT_VBLANK, &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(&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;
	}
#ifdef FAX
	if (Fax && SerialEvent) {
	    IgnoreCD = 1;
	    CarrierDetectEvent = 1;
	    ulog(2, "Serial Data Detected");
	    DoLogin();
	    IgnoreCD = FIgnoreCD;
	    ulog(2, "Getty Disconnecting");
	    Disconnect(1);
	    InitModem();
	    OptionEvent = 0;
	}
#endif
	if (PortRequestedEvent) {
	    ulog(2, "External Port Request");
	    CloseSerial();
	    FUnLockSerialPort();

	    PortRequestedEvent = 0;

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

		/* if AttempDevUnit 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;
	}
    }
}

void
DoOptions(ac, av)
int ac;
char *av[];
{
    short i;
    short bi = 0;
    short 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
		puts("AT field overflow");
	    continue;
	}
	if (*++ptr)             /*  skip -      */
	    ++ptr;		/*  and option	*/
	v = atoi(ptr);
	switch(ptr[-1]) {
#ifdef FAX
	case 'F':
	    Fax = v | 1;
	    break;
#endif
	case '0':
	    ZeroOption = 1;
	    break;
	case '7':
	    Wire7 = 1;
/* Added by Christian Wolf (chris@wolfhh.hanse.de) */
	case 'u':
	    UserReport = v;
	    break;
	    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
		puts("-B field overflow");
	    break;
	case 'm':
	    SpeakerOpt = 1;
	    SpeakerLevel = v;
	    break;
	case 'r':
	    AnswerRing = v;
	    break;
	case 'h':
#ifdef FAX
	    FIgnoreCD =
#endif
	    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:
	    printf("Warning, Bad option: -%s\n", ptr);
	    break;
	}
    }
    if (fi && fi != arysize(AtFields))
	AtFields[fi] = NULL;
    if (bi && bi != arysize(Bauds))
	Bauds[bi] = 0;
}


void
myexit()
{
    if (IoSink) {
	DeletePort(IoSink);
	IoSink = NULL;
    }
    if (ComPort) {
	DeletePort(ComPort);
	ComPort = NULL;
    }
    if (Iot0IP) {
	AbortIO(&Iot0);
	WaitIO(&Iot0);
    }
    if (Iot1IP) {
	AbortIO(&Iot1);
	WaitIO(&Iot1);
    }
    if (Iot0.tr_node.io_Device) {
	CloseDevice(&Iot0);
	Iot0.tr_node.io_Device = NULL;
    }
    CloseSerial();
    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;
    }
}

void
FLockSerialPort()
{
    char *error;

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

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

void
InitModem()
{
    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':
	SerPuts("\010\010\r");
	Delay(10);
	SerPuts("ATZ\r");
	Delay(120);
	strcpy(buf, "AT");
	if (SpeakerOpt)
	    sprintf(buf + strlen(buf), "M%d", SpeakerLevel);
	sprintf(buf + strlen(buf), "S0=%d", AnswerRing);
#ifdef FAX
	if (Fax)
	    sprintf(buf+strlen(buf),"+FCLASS=0+FCR=1+FAA=1");
#endif
	strcat(buf, "\r");
	SerPuts(buf);
	break;
    case 'd':
	SerPuts("\010\010\r");
	break;
    }
    for (i = 0; i < arysize(AtFields) && AtFields[i]; ++i) {
	Delay(50);
	SerPuts(AtFields[i]);
	SerPuts("\r");
    }
    Delay(20);
    ClearRecv();
#ifdef FAX
    /* 31JAN92 DAV - kludge around possible A2232 CMD_FLUSH problem */
    if(Fax) {
	for(;;) {
	    RxStart();
	    if(CheckIO(&Iosr))
	    	WaitIO(&Iosr);
	    else
	        break;
    	}
    }
#endif
}

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

#ifdef FAX /* DAV 12FEB92 */
    /* Don't know why, but operation on A2232 is unreliable w/o this */
    if(Fax & 4) {
	CloseSerial();
	Delay(100);
	OpenSerial();
    }
#endif

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

    RxStop();
    if (IgnoreCD == 0) {
	while (dropdtr) {
	    short i;
	    long status = GetStatus();

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

	    SetParams(0);

	    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);
		}
	    } else {
		CloseDevice(&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, &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);
	    }

	    /*
	     *	Loop until carrier lost
	     */

	    if (--retry == 0) {
		if (IgnoreDTR == 0)
		    puts("Getty: unable to disconnect!");
		break;
	    }
	}
    }
}


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

    while (event == 0) {
	{
	    GMsg *msg;
	    while (msg = (GMsg *)GetMsg(ComPort)) {
		switch(msg->Cmd) {
		case 'O':
		    DoOptions(msg->Data1, msg->Data2);
		    break;
		}
		ReplyMsg((Message *)msg);
		if (ZeroOption)
		    exit(0);
		OptionEvent = 1;
		++event;
	    }
	}
	{
	    IORequest *ior;
	    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(&Iot1);
		    Iot1IP = 1;

		    if (Ios.IOSer.io_Device) {
			if ((GetStatus() & CIAF_COMCD) == 0)
			    CarrierDetectEvent = 1;
			else
			    CarrierDetectEvent = 0;
		    }
		    ++event;
		} else 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);
	}
    }
}

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

void
OpenSerial()
{
    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, &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(&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);
}

void
CloseSerial()
{
    if (IosrIP) {
	NiceAbortIO(&Iosr);
	WaitIO(&Iosr);
	IosrIP = 0;
    }
    if (Ios.IOSer.io_Device) {
	CloseDevice(&Ios);
	Ios.IOSer.io_Device = NULL;
    }
}

void
SetParams(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(&Ios))
	    ulog(0, "SetParams Error %d", error);
    }
}

void
RxStart()
{
    if (IosrIP) {
	NiceAbortIO(&Iosr);
	WaitIO(&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(&Iosr);
}

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

void
ClearRecv()
{
    int error;

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

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

    SerialEvent = 0;

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

void
ResetSP()
{
    int error;

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

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

void
SerPuts(str)
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(&Ioss))
	ulog(0, "Write Error %d", error);
}

long
GetStatus()
{
    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(&Ioss))
	ulog(0, "Query Error %d", error);
    status = Ioss.io_Status;
    if (IgnoreCD)
	status &= ~CIAF_COMCD;
    return(status);
}

/*
 *  HANDLE LOGIN
 */

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

    Timeout(30);
    if (IgnoreConnect == 0) {
	while (ptr = GetLine(0)) {
#ifdef FAX
	    /*ulog(-1,"Received <%s>\n",ptr);*/
	    if(Fax) {
		if((strnicmp(ptr, "RING", 4) == 0)
			|| (strnicmp(ptr, "CARRIER", 7) == 0)) {
			Timeout(40);	/* Extend timeout */
		} else if(strnicmp(ptr, "+FCO", 4) == 0) {
			if(!(Fax & 2)) {
				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) {
			continue;
		}
	    }
#endif
	    if (strnicmp(ptr, "CONNECT", 7) == 0)
		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);

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

    {
	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, "LoginFailed user=%s pass=%s", LoginBuf, PasswdBuf);
		    Delay(50*4);
		    continue;
		}
	    }
	    ulog(-1, "login %s", LoginBuf);
	    RxStop();
	    RunPasswdEntry();
	}
	break;
    }
}

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

char *
GetLine(echo)
{
    static char Buf[64];
    char buf[2];
    short i = 0;
    short 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(ior)
void *ior;
{
    Disable();
    if (((struct Message *)ior)->mn_Node.ln_Type != NT_REPLYMSG)
	AbortIO(ior);
    Enable();
}

