#define CLIB_SOCKET_INLINES_H /* avoid that include file */
#include <exec/types.h>
#include <exec/memory.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <dos/dos.h>
#include <dos/dosextens.h>
#include <dos/dostags.h>
#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/utility.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <exec/errors.h>
#include <devices/serial.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <inetd.h>

#include <fakesr.h>
/*#include <libraries/multiuser.h>
#include <proto/multiuser.h>
*/
/* IMPORTANT: Do NOT link this module with the startup code */

struct InstData {
	struct Library *DOSB;
	struct Library *SocketB;
	struct Library *UtilityB;
	int Socket;
	signed short RecvBuf[1024];
	int RecvBufPos;
	int RecvBufFlags;
/* Defines for RecvBufFlags: */
#define RBF_SUBNEGOTIATE (1<<0)
};

#undef DEBUG

struct Library *SocketBase; /* Linker wants it. We don't use it */
#ifndef USE_INETD
struct Library *SysBase;
#endif

const char *Signature="FAKEMSG--EXPUNGE";

#define DOSBase Inst->DOSB
#define SocketBase Inst->SocketB
#define UtilityBase Inst->UtilityB
#define Socket Inst->Socket

/* telnet symbols */
#define IAC 255
#define WILL 251
#define WONT 252
#define DO 253
#define DONT 254
#define ECHO 1
#define LINEMODE 34
#define SE 240
#define SB 250
#define MODE 1
#define EDIT 1

struct PasswdInfo {
	char Login[50];
	char Passwd[50];
	char RealName[150];
	char WorkDir[256];
	char Command[512];
};

void SockPuts(struct InstData *Inst,int Sock,char *Buf);
void SockGets(struct InstData *Inst,int Sock,char *Buf,int BufSize);
unsigned long GetInt(char *String,char **NewPtr);
struct PasswdInfo *LookupPasswd(struct InstData *Inst,char *Login);
int __asm SubSubProc(register __a0 char *ArgString,register __d0 long Length);
struct DosPacket *PktFromMsg(struct Message *Msg);
void MakeLongStr(char *StrBuf,unsigned long Num);
void SockWrite(struct InstData *Inst,int Sock,char *Buf,int Len,int RawFlag);
int RecvChar(struct InstData *Inst,int Sock,unsigned char *Loc,int RawMode);
struct Process *MyCreateNewProcTags(struct InstData *,ULONG,...);
void *MyAllocDosObjectTags(struct InstData *,ULONG,ULONG,...);
void PktFPuts(struct InstData *Inst,BPTR File,char *String);
void SockRawWrite(struct InstData *Inst,int Sock,char *Buf,int Len);
void MySystemTags(struct Library *DSBse,char *Cmd,ULONG Tag0,...);
void mystrcat(char *Dest,char *Cat);
void MyFPrintf(struct InstData *Inst,BPTR File,char *Fmt,...);

ULONG FakeBeginIO(void *IORequest);
ULONG FakeAbortIO(void *IORequest);
ULONG FakeOpen(void *IORequest);
ULONG FakeClose(void *IORequest);
void mymovmem(char *From,char *To,int Len);

#define NewList(list) {(list)->lh_Head=(struct Node *)&(list)->lh_Tail;(list)->lh_Tail=NULL;(list)->lh_TailPred=(struct Node *)&(list)->lh_Head;}
#define bzero(base,n) memset(base,0,n)
#define bcopy(from,to,n) memcpy(to,from,n)
#ifdef DEBUG
  BPTR DebugFile;
#endif

/* SubProcCode MUST be the first function in the file */
#ifdef USE_INETD
void main(void)
#else
int __asm SubProcCode(register __a0 char *ArgStr,register __d0 long Length)
/* ArgString will look like: "[SocketID] [SigNum] [ParentTaskID]" */
#endif
{
	int Cnt;
	unsigned long SockID,SigID,TaskID;
	char Buf[1000],LoginBuf[50];
	BPTR BannerFile=NULL;
	struct InstData *Inst=NULL;
	struct PasswdInfo *Info=NULL;
	struct FileHandle *FH;
	struct Message *Msg,*GotMsg;
	char NoEcho[7];
	char Echo[14];
	char *ArgString;
	int OpenCount=0;
	int AmntRead;
	int HangupFlag=FALSE;
	
	ULONG SigMask;
	long yes=TRUE;
	fd_set RdSet;
	fd_set WrSet;
	struct List ReadWaitList;
	struct DosPacket *Pkt;
	int Err;
	unsigned char Chr,NumBuf[30];
	int RawMode=FALSE;
	int NextChar=-1;
	struct MsgPort *TimePort;
	struct timerequest *TimeReq;
	/*struct Library *muBase=NULL;*/
	struct DaemonMessage *InetdStartup;
	struct Process *Self;
	
	
#ifndef USE_INETD
	SysBase=*((struct Library **)0x00000004);
	ArgString=ArgStr;
#endif
/*
	Echo[0]=NoEcho[0]=IAC;
	Echo[1]=WONT;
	Echo[2]=ECHO;
	NoEcho[1]=WILL;
	NoEcho[2]=ECHO;
	Echo[3]=NoEcho[3]='\0';
*/	
	Echo[0]=NoEcho[0]=IAC;
	Echo[1]=WONT;
	Echo[2]=ECHO;
	Echo[3]=IAC;
	Echo[4]=DO;
	Echo[5]=LINEMODE;
	Echo[6]=IAC;
	Echo[7]=SB;
	Echo[8]=LINEMODE;
	Echo[9]=MODE;
	Echo[10]=EDIT;
	Echo[11]=IAC;
	Echo[12]=SE;
	NoEcho[1]=WILL;
	NoEcho[2]=ECHO;
	NoEcho[3]=IAC;
	NoEcho[4]=DONT;
	NoEcho[5]=LINEMODE;
	Echo[13]=NoEcho[6]='\0';

#ifndef USE_INETD
	for (Cnt=0;Cnt < Length;Cnt++) {
		if (ArgStr[Cnt]!=' ' && (ArgStr[Cnt] < '0' || ArgStr[Cnt] > '9')) 
			return ERROR_FILE_NOT_OBJECT;
	}
#endif	
	Inst=AllocMem(sizeof(struct InstData),MEMF_CLEAR);
	if (!Inst) return 0;
	DOSBase=OpenLibrary("dos.library",36);
	UtilityBase=OpenLibrary("utility.library",36);
	SocketBase=OpenLibrary("bsdsocket.library",2);
/*	muBase=OpenLibrary("multiuser.library",39);*/
	
	
	TimePort=CreateMsgPort();

	if (!DOSBase || !UtilityBase || !SocketBase || !TimePort) return 0;
#ifdef DEBUG
	DebugFile=Open("con:0/0/500/100/TelnetGetty DEBUG/WAIT/CLOSE",MODE_OLDFILE);
	FPuts(DebugFile,"TelnetGetty starting\nArgString=");
	FPuts(DebugFile,ArgString);
	FPuts(DebugFile,"\n");
	
#endif

#ifdef USE_INETD
	Self=FindTask(NULL);
	InetdStartup=(struct DaemonMessage *)Self->pr_ExitData;
	if (!InetdStartup) goto quit;
	Socket=ObtainSocket(InetdStartup->dm_Id,InetdStartup->dm_Family,InetdStartup->dm_Type,0);
	if (Socket < 0) goto quit;
	
#else
	/* Get arguments */
	NewList(&ReadWaitList);
	SockID=GetInt(ArgString,&ArgString);
	ArgString++; /* Skip over space */
	SigID=GetInt(ArgString,&ArgString);
	ArgString++; /* Skip over space */
	TaskID=GetInt(ArgString,&ArgString);
	/* After this call, the parameter string is no longer valid */
	Signal((struct Task *)TaskID,1<<SigID);
#ifdef DEBUG
	FPuts(DebugFile,"Signaled parent\n");
#endif
	
	/* We're on our own now */
	Socket=ObtainSocket(SockID,AF_INET,SOCK_STREAM,0);
	if (Socket < 0) goto quit;

#endif

#ifdef DEBUG
	FPuts(DebugFile,"Got Socket\n");
#endif
	/* First, display banner, ask for password, etc */
	BannerFile=Open("tnserv:banner",MODE_OLDFILE);
	if (!BannerFile) goto quit;
#ifdef DEBUG
	FPuts(DebugFile,"Opened tnserv:banner\n");
#endif
	while (FGets(BannerFile,Buf,sizeof(Buf))) {
		SockWrite(Inst,Socket,Buf,strlen(Buf),FALSE);
	}
#ifdef DEBUG
	FPuts(DebugFile,"Wrote tnserv:banner to socket\n");
#endif
	/* Get login name */
	SockPuts(Inst,Socket,"Login: ");
	SockPuts(Inst,Socket,Echo);
	SockGets(Inst,Socket,LoginBuf,sizeof(LoginBuf));
	while (strlen(LoginBuf) && (LoginBuf[strlen(LoginBuf)-1]=='\n' || LoginBuf[strlen(LoginBuf)-1]=='\r'))
		LoginBuf[strlen(LoginBuf)-1]='\0';
#ifdef DEBUG
	FPuts(DebugFile,"Got Login: ");
	FPuts(DebugFile,LoginBuf);
	FPuts(DebugFile,"\n");
#endif
	
  Info=LookupPasswd(Inst,LoginBuf);
  if (!Info || strcmp(Info->Passwd,"*")) {
  
	  strcpy(Buf,"Password: ");
	  strcpy(Buf+strlen(Buf),NoEcho);
	  SockPuts(Inst,Socket,Buf);
	  SockGets(Inst,Socket,Buf,sizeof(Buf));
	  while (strlen(Buf) && (Buf[strlen(Buf)-1]=='\n' || Buf[strlen(Buf)-1]=='\r'))
	  	Buf[strlen(Buf)-1]='\0';
	  SockPuts(Inst,Socket,Echo);
#ifdef DEBUG
	  FPuts(DebugFile,"Got Password: ");
	  FPuts(DebugFile,Buf);
	  FPuts(DebugFile,"\n");
#endif
	  if (strcmp(Buf,Info->Passwd)) {
		  SockPuts(Inst,Socket,"\nLogin incorrect.\nClosing connection\n");
		  CloseSocket(Socket);
		  goto quit;
	  }
  }
	
  
/*
	if (muBase) {
		struct TagItem tags[]={
			{muT_Graphical,FALSE},
			{muT_Task,NULL},
			{muT_Global,FALSE},
			/*{muT_Quiet,FALSE},*/
			/* {muT_All,FALSE}, */
			{muT_UserID,NULL},
			{muT_Password,NULL},
			{TAG_END,0}
		};
		tags[1].ti_Data=FindTask(NULL);
		tags[5].ti_Data=LoginBuf;
		tags[6].ti_Data=Buf;
		muLoginA(tags);
	}
	*/
	SockPuts(Inst,Socket,"\n");
	
	if (Info->Command[0]=='*') {
		/* Use stdin/stdout */
		/* Make myself a filehandle */
#ifdef DEBUG
		FPuts(DebugFile,"Logged in OK\nMaking Filehandle\n");
#endif
		
		FH=MyAllocDosObjectTags(Inst,DOS_FILEHANDLE,ADO_FH_Mode,MODE_OLDFILE,TAG_END);
		if (!FH) goto quit;
		FH->fh_Pos=FH->fh_End=-1;
		FH->fh_Type=&((struct Process *)FindTask(NULL))->pr_MsgPort;
		FH->fh_Args=(LONG)Info;
		FH->fh_Port=FH->fh_Type; /* Is this right? */
		FH=(struct FileHandle *)MKBADDR(FH);
		OpenCount++;
		
		/* Run the subprocess */
		MakeLongStr(Buf,(ULONG)FH);
		strcpy(Buf+strlen(Buf)," ");
		strcpy(Buf+strlen(Buf),Info->Command+1 /* ignore the leading '*' */);
#ifdef DEBUG
		FPuts(DebugFile,"Starting subprocess\nCmdLine=");
		FPuts(DebugFile,Buf);
		FPuts(DebugFile,"\n");
		/*Close(DebugFile);*/
		Flush(DebugFile);
		
		/*DebugFile=NULL;*/
#endif
	
		/* NO more DOS calls allowed after this one */
		/* We also can't use Buf after we make this call */
    if (*(Info->Command+1)) {
  		MyCreateNewProcTags(Inst,NP_Entry,SubSubProc,
			  NP_Arguments,Buf,
			  NP_Name,"Telnet subprocess",
			  TAG_END);
    }
		FD_ZERO(&RdSet);
		IoctlSocket(Socket,FIONBIO,(caddr_t)&yes);
#ifdef DEBUG
		PktFPuts(Inst,DebugFile,"Starting handler support\n");
#endif
		for (;;) {
			if (ReadWaitList.lh_Head->ln_Succ && !HangupFlag) {
				if (NextChar != -1) {
					Pkt=PktFromMsg((struct Message *)ReadWaitList.lh_Head);
					*(((unsigned char *)Pkt->dp_Arg2)+Pkt->dp_Res1)=NextChar;
					Chr=NextChar;
					NextChar=-1;
					Err=1;
					Pkt->dp_Res1++;
					if (Pkt->dp_Res1==Pkt->dp_Arg3 || Chr=='\r' || Chr=='\n') {
						Remove(ReadWaitList.lh_Head);
						PutMsg(Pkt->dp_Port,Pkt->dp_Link); /* Send it back */
					}
				}
			}	
			if (ReadWaitList.lh_Head->ln_Succ && !HangupFlag) {
				FD_SET(Socket,&RdSet);
			} else {	
				FD_CLR(Socket,&RdSet);
			}
			SigMask=(1<<((struct Process *)FindTask(NULL))->pr_MsgPort.mp_SigBit)|(1<<TimePort->mp_SigBit);
			WaitSelect(Socket+1,&RdSet,NULL,NULL,NULL,&SigMask);
#ifdef DEBUG
			PktFPuts(Inst,DebugFile,"Wait ended \n");
#endif
	
			if (FD_ISSET(Socket,&RdSet)) {
#ifdef DEBUG
				PktFPuts(Inst,DebugFile,"Got read available from socket\n");
#endif
				if (ReadWaitList.lh_Head->ln_Succ) {
					/* Someone is waiting to read */
					Pkt=PktFromMsg((struct Message *)ReadWaitList.lh_Head);
					if (Pkt && Pkt->dp_Type==ACTION_READ) {
#ifdef DEBUG
						PktFPuts(Inst,DebugFile,"Performing delayed read\n");
#endif
						while (Pkt->dp_Res1 < Pkt->dp_Arg3) {
							if (NextChar != -1) {
								*(((unsigned char *)Pkt->dp_Arg2)+Pkt->dp_Res1)=NextChar;
								Chr=NextChar;
								NextChar=-1;
								Err=1;
							} else {
								Err=RecvChar(Inst,Socket,((char *)Pkt->dp_Arg2)+Pkt->dp_Res1,FALSE/*RawMode*/);
								Chr=*(((char *)Pkt->dp_Arg2)+Pkt->dp_Res1);
							}
							if (Err==1) Pkt->dp_Res1++;
							else if (Err==0) {
								/* Hangup */
								HangupFlag=TRUE;
								break;
							} else break;
#ifdef DEBUG
							if (Chr==255) {
								PktFPuts(Inst,DebugFile,"Received 255\n");
							}
#endif
							if (Chr=='\r' || Chr=='\n') {
								break;
							}
						}
						if (Err==1) {
							if (Pkt->dp_Res1==Pkt->dp_Arg3 || Chr=='\r' || Chr=='\n') {
								Remove(ReadWaitList.lh_Head);
								PutMsg(Pkt->dp_Port,Pkt->dp_Link); /* Send it back */
							}
						}
					} else if (Pkt && Pkt->dp_Type==ACTION_WAIT_CHAR) {
#ifdef DEBUG
						PktFPuts(Inst,DebugFile,"Processing WAIT_CHAR");
#endif
						if (NextChar==-1) {
							Err=RecvChar(Inst,Socket,&Chr,FALSE);
							if (Err==1) {
								Pkt->dp_Res1=DOSTRUE;
								Remove(ReadWaitList.lh_Head);
#ifdef DEBUG
								PktFPuts(Inst,DebugFile,"char ready\n");
#endif
								AbortIO((struct IORequest *)Pkt->dp_Res2);
								WaitIO((struct IORequest *)Pkt->dp_Res2);
								CloseDevice((struct IORequest *)Pkt->dp_Res2);
								DeleteIORequest((struct IORequest *)Pkt->dp_Res2);
								Pkt->dp_Res2=0;
								PutMsg(Pkt->dp_Port,Pkt->dp_Link);
								NextChar=Chr;
							} else {
#ifdef DEBUG
								PktFPuts(Inst,DebugFile,"no char available\n");
#endif
								NextChar=-1;
							}
							if (Err==0) HangupFlag=TRUE;
						} else {
							Pkt->dp_Res1=DOSTRUE;
							Remove(ReadWaitList.lh_Head);
							AbortIO((struct IORequest *)Pkt->dp_Res2);
							WaitIO((struct IORequest *)Pkt->dp_Res2);
							CloseDevice((struct IORequest *)Pkt->dp_Res2);
							DeleteIORequest((struct IORequest *)Pkt->dp_Res2);
							Pkt->dp_Res2=0;
							PutMsg(Pkt->dp_Port,Pkt->dp_Link);
#ifdef DEBUG
							PktFPuts(Inst,DebugFile,"char ready\n");
#endif
						}
					}
					else { /* Invalid message */
						Remove(ReadWaitList.lh_Head);
					}
				} else {
#ifdef DEBUG
					PktFPuts(Inst,DebugFile,"Unexpected: got read ready, no request\n");
#endif				
				}
			}
			while (TimeReq=(struct timerequest *)GetMsg(TimePort)) {
				Msg=(struct Message *)TimeReq->tr_node.io_Message.mn_Node.ln_Name;
				Pkt=PktFromMsg(Msg);
				CloseDevice(TimeReq);
				DeleteIORequest(TimeReq);
				Pkt->dp_Res1=DOSFALSE;
				Pkt->dp_Res2=0;
				Remove(Msg); /* Remove it from the read wait list */
				PutMsg(Pkt->dp_Port,Pkt->dp_Link);
				
				
			}
			while (Msg=GetMsg(&((struct Process *)FindTask(NULL))->pr_MsgPort)) {
				Pkt=PktFromMsg(Msg);
#ifdef DEBUG
				PktFPuts(Inst,DebugFile,"Got packet\n");
#endif
				switch(Pkt->dp_Type) {
					case ACTION_FINDINPUT:
					case ACTION_FINDOUTPUT:
					case ACTION_FINDUPDATE:
						FH=(struct FileHandle *)BADDR((BPTR)Pkt->dp_Arg1);
						FH->fh_Pos=FH->fh_End=-1;
						FH->fh_Type=&((struct Process *)FindTask(NULL))->pr_MsgPort;
						FH->fh_Args=(LONG)Info;
						FH->fh_Port=FH->fh_Type; /* Is this right? */
						OpenCount++;
						Pkt->dp_Res1=DOSTRUE;
						PutMsg(Pkt->dp_Port,Pkt->dp_Link);	
					break;
					
					case ACTION_READ:
					Pkt->dp_Res1=0;
#ifdef DEBUG
					PktFPuts(Inst,DebugFile,"Got action_read\n");
#endif
					AddTail(&ReadWaitList,Msg);
					break;
					
					case ACTION_WRITE:
#ifdef DEBUG
					PktFPuts(Inst,DebugFile,"Got action_write\n");
#endif
					SockWrite(Inst,Socket,(char *)Pkt->dp_Arg2,Pkt->dp_Arg3,FALSE/*RawMode*/);
					Pkt->dp_Res1=Pkt->dp_Arg3;
					PutMsg(Pkt->dp_Port,Pkt->dp_Link);	
					break;
					
					case ACTION_WAIT_CHAR:
#ifdef DEBUG
						PktFPuts(Inst,DebugFile,"Got action_waitchar");
#endif
						Pkt->dp_Res1=DOSFALSE;
						
						TimeReq=CreateIORequest(TimePort,sizeof(struct timerequest));
						if (!TimeReq || OpenDevice("timer.device",UNIT_MICROHZ,TimeReq,0)) {
							if (TimeReq) DeleteIORequest(TimeReq);
							Pkt->dp_Res2=ERROR_NO_FREE_STORE;
							PutMsg(Pkt->dp_Port,Pkt->dp_Link);						
						}
						TimeReq->tr_node.io_Command=TR_ADDREQUEST;
						TimeReq->tr_time.tv_micro=Pkt->dp_Arg1;
						TimeReq->tr_time.tv_secs=0;
						TimeReq->tr_node.io_Message.mn_Node.ln_Name=Msg;
						SendIO(TimeReq);
						Pkt->dp_Res2=(ULONG)TimeReq;
						
						AddTail(&ReadWaitList,Msg);
					
					break;
	
	/*
					case ACTION_DISKINFO:
					
					break;
					*/
					case ACTION_SCREEN_MODE:
#ifdef DEBUG
					PktFPuts(Inst,DebugFile,"Got action_screen_mode\n");
#endif
						if (Pkt->dp_Arg1) {
							/* Raw mode */
							SockPuts(Inst,Socket,NoEcho);
							RawMode=TRUE;
						} else {
							SockPuts(Inst,Socket,Echo);
							RawMode=FALSE;
						}
						Pkt->dp_Res1=DOSFALSE;
						PutMsg(Pkt->dp_Port,Pkt->dp_Link);
					break;
					
					case ACTION_END:
#ifdef DEBUG
					PktFPuts(Inst,DebugFile,"Got action_end\n");
#endif
						Pkt->dp_Res1=0;
						PutMsg(Pkt->dp_Port,Pkt->dp_Link);
						OpenCount--;
						if (!OpenCount) {
							for (GotMsg=ReadWaitList.lh_Head;GotMsg->mn_Node.ln_Succ;GotMsg=(struct Message *)GotMsg->mn_Node.ln_Succ) {
								Pkt=PktFromMsg(GotMsg);
								if (Pkt->dp_Type==ACTION_WAIT_CHAR) {
									AbortIO((struct IORequest *)Pkt->dp_Res2);
									WaitIO((struct IORequest *)Pkt->dp_Res2);
									CloseDevice((struct IORequest *)Pkt->dp_Res2);
									DeleteIORequest((struct IORequest *)Pkt->dp_Res2);
									Pkt->dp_Res2=0;
								}
							}
							
							goto quit;
						}
					break;
					
					case ACTION_SEEK:
#ifdef DEBUG
					PktFPuts(Inst,DebugFile,"Got action_seek\n");
#endif
					Pkt->dp_Res1=-1;
					Pkt->dp_Res2=ERROR_OBJECT_WRONG_TYPE;
					PutMsg(Pkt->dp_Port,Pkt->dp_Link);
					
					break;
					
					
					default:
#ifdef DEBUG
					PktFPuts(Inst,DebugFile,"Got action_not_known. Number");
					MakeLongStr(NumBuf,Pkt->dp_Type);
					PktFPuts(Inst,DebugFile,NumBuf);
					PktFPuts(Inst,DebugFile,"\n");
#endif
					Pkt->dp_Res1=0;
					Pkt->dp_Res2=ERROR_ACTION_NOT_KNOWN;
					PutMsg(Pkt->dp_Port,Pkt->dp_Link);
				}
			}
		}
	} else {
		/* Use fake serial */
		struct IOStdReq *IOReq=NULL;
		struct MsgPort *IOPort=NULL;
		struct FSRUnit *SerUnit=NULL;
		/* SerUnit->fsru_UserData[0]=CmdPort
		   SerUnit->fsru_UserData[1]=ReadQueue
			 SerUnit->fsru_UserData[2]=WriteQueue
			 SerUnit->fsru_UserData[3]=Open count 
			 SerUnit->fsru_UserData[4]=RBufLen */
		struct MsgPort *CmdPort=NULL;
		struct List *ReadQueue=NULL;
		struct List *WriteQueue=NULL;
		char *CmdPtr;
		struct IOExtSer *GotReq=NULL,*OtrReq=NULL;
		char *RBuf=NULL;
		int RBufPos=0;
		int SerUnitAdded=FALSE;
		int Amnt;
		int RecvRet;
		int CD=TRUE; /* Carrier Detect */
		long UnitNum=FSR_UNITNONE;
		
		/* We use IOExtSer.Status bit 0 as a flag to say whether this request has
		   been aborted */
		
#ifdef DEBUG
		FPuts(DebugFile,"Creating ports...\n");
#endif
		IOPort=CreateMsgPort();
		CmdPort=CreateMsgPort();
		IOReq=CreateIORequest(IOPort,sizeof(struct IOStdReq));
		if (!IOPort || !IOReq || !CmdPort) {
#ifdef DEBUG
			FPuts(DebugFile,"Unable to create message port/IORequest\nExiting\n");
#endif
			goto quitfakeser;
		}
#ifdef DEBUG
		FPuts(DebugFile,"Opening fakesr.device...\n");
#endif		
		if (OpenDevice("fakesr.device",FSR_CTLUNIT,IOReq,0)) {
			IOReq->io_Device=NULL;
#ifdef DEBUG
			FPuts(DebugFile,"Unable to open fakesr.device\nExiting\n");
#endif
			goto quitfakeser;
		}
#ifdef DEBUG
		FPuts(DebugFile,"opened successfully\n");
#endif
		SerUnit=AllocMem(sizeof(struct FSRUnit),MEMF_CLEAR|MEMF_PUBLIC);
		ReadQueue=AllocMem(sizeof(struct List),MEMF_PUBLIC);
		if (ReadQueue) NewList(ReadQueue);
		WriteQueue=AllocMem(sizeof(struct List),MEMF_PUBLIC);
		if (WriteQueue) NewList(WriteQueue);
		if (!SerUnit || !ReadQueue || !WriteQueue) {
#ifdef DEBUG
			FPuts(DebugFile,"Unable to allocate memory\nExiting\n");
#endif			
			goto quitfakeser;
		}
		
		CmdPtr=Info->Command;
		if (Info->Command[0]<='9' && Info->Command[0] >= '0') {
			UnitNum=GetInt(Info->Command,&CmdPtr);
		}
		
		SerUnit->fsru_Num=UnitNum;
		SerUnit->fsru_BeginIO=FakeBeginIO;
		SerUnit->fsru_AbortIO=FakeAbortIO;
		SerUnit->fsru_Open=FakeOpen;
		SerUnit->fsru_Close=FakeClose;
		SerUnit->fsru_UserData[0]=CmdPort;
		SerUnit->fsru_UserData[1]=ReadQueue;
		SerUnit->fsru_UserData[2]=WriteQueue;
		SerUnit->fsru_UserData[3]=0;
		SerUnit->fsru_UserData[4]=(void *)1024;
		
		IOReq->io_Data=(APTR)SerUnit;
		IOReq->io_Length=sizeof(*SerUnit);
		IOReq->io_Command=FSRCMD_ADDUNIT;
		IOReq->io_Error=0;
#ifdef DEBUG
		FPuts(DebugFile,"Adding FakeSr unit...");
#endif

		DoIO(IOReq);
		if (IOReq->io_Error) {
#ifdef DEBUG
			FPuts(DebugFile,"Error adding fakesr.device unit\nExiting\n");
#endif						
			goto quitfakeser;
		}

		SerUnitAdded=TRUE;

#ifdef DEBUG
		FPuts(DebugFile,"Done\n");
#endif

		RBuf=AllocMem((ULONG)SerUnit->fsru_UserData[4],MEMF_PUBLIC);
		RBufPos=0;
		if (!RBuf) {
#ifdef DEBUG
			FPuts(DebugFile,"Unable to allocate memory\nExiting\n");
#endif						
			goto quitfakeser;
		}
		

		IoctlSocket(Socket,FIONBIO,(caddr_t)&yes);
		{
			BPTR NilInput,NilOutput;
#ifdef DEBUG
			FPuts(DebugFile,"Opening filehandles\n");
#endif
      if (*CmdPtr) {
			  NilInput=Open("NIL:",MODE_OLDFILE);
			  NilOutput=Open("NIL:",MODE_OLDFILE);

			  /* sprintf(Buf,"%s -DEVICE fakesr.device -UNIT %ld",CmdPtr,SerUnit->fsru_Num); */
			  strcpy(Buf,CmdPtr);
			  mystrcat(Buf," -DEVICE fakesr.device -UNIT ");
			  MakeLongStr(LoginBuf,SerUnit->fsru_Num);
#ifdef DEBUG
			  FPuts(DebugFile,"Got FakeSr unit: ");
			  FPuts(DebugFile,LoginBuf);
			  FPuts(DebugFile,"\n");
#endif
			  SockPuts(Inst,Socket,NoEcho);

			  mystrcat(Buf,LoginBuf);
#ifdef DEBUG
			  FPuts(DebugFile,"Running command:");
			  FPuts(DebugFile,Buf);
			  FPuts(DebugFile,"\n");
#endif
			  MySystemTags(DOSBase,Buf,SYS_Input,NilInput,SYS_Output,NilOutput,SYS_Asynch,TRUE,TAG_END);
      }
		}
		
		for (;;) {
			FD_ZERO(&RdSet);
			FD_ZERO(&WrSet);
			if (CD)  {
				if (RBufPos < (((long)SerUnit->fsru_UserData[4])-1)) FD_SET(Socket,&RdSet);
				if (WriteQueue->lh_Head->ln_Succ) FD_SET(Socket,&WrSet);
			}
			SigMask=(1<<CmdPort->mp_SigBit)|SIGBREAKF_CTRL_C;
			WaitSelect(Socket+1,&RdSet,&WrSet,NULL,NULL,&SigMask);
#ifdef DEBUG
			FPuts(DebugFile,"Wait ended...");
#endif
			
			if (FD_ISSET(Socket,&RdSet)) {
				while ((RBufPos < (long)SerUnit->fsru_UserData[4]) && ((RecvRet=RecvChar(Inst,Socket,RBuf+RBufPos,TRUE))==1)) RBufPos++;
				Forbid();
				if ((GotReq=(struct IOExtSer *)ReadQueue->lh_Head)->IOSer.io_Message.mn_Node.ln_Succ) {
					Remove(GotReq);
					Permit();
					Amnt=min(RBufPos,GotReq->IOSer.io_Length);
					memcpy(((char *)GotReq->IOSer.io_Data)+GotReq->IOSer.io_Actual,RBuf,Amnt);
					GotReq->IOSer.io_Actual+=Amnt;
					if (Amnt < RBufPos) mymovmem(RBuf+Amnt,RBuf,RBufPos-Amnt);
					RBufPos-=Amnt;
					
					if (GotReq->IOSer.io_Actual < GotReq->IOSer.io_Length) {
						if (GotReq->io_Status & (1<<0)) {
							/* We got an AbortIO() */
							GotReq->IOSer.io_Error=IOERR_ABORTED;
							ReplyMsg(GotReq);
						} else {
							Forbid();
							AddTail(ReadQueue,GotReq);
							Permit();
						}
					}
					else ReplyMsg(GotReq);
					
				}
				else Permit();
				if (RecvRet==0) {
					/* Connection closed */
					CD=FALSE;
				}
			}
			if (FD_ISSET(Socket,&WrSet)) {
				Forbid();
				if ((GotReq=(struct IOExtSer *)WriteQueue->lh_Head)->IOSer.io_Message.mn_Node.ln_Succ) {
					Remove(GotReq);
					Permit();
					
#ifdef DEBUG
					FPuts(DebugFile,"Writing to socket\n");
					MyFPrintf(Inst,DebugFile,"io_Actual=%ld,io_Length=%ld\n",(long)GotReq->IOSer.io_Actual,(long)GotReq->IOSer.io_Length);
#endif
					SockWrite(Inst,Socket,((char *)GotReq->IOSer.io_Data)+GotReq->IOSer.io_Actual,GotReq->IOSer.io_Length-GotReq->IOSer.io_Actual,TRUE);
#ifdef DEBUG
					FPuts(DebugFile,"Write completed\n");
#endif
					GotReq->IOSer.io_Actual=GotReq->IOSer.io_Length;
					
					
					if (GotReq->IOSer.io_Actual < GotReq->IOSer.io_Length) {
						Forbid();
						if (GotReq->io_Status & (1<<0)) {
							Permit();
							/* We got an AbortIO() */
							GotReq->IOSer.io_Error=IOERR_ABORTED;
							ReplyMsg(GotReq);
						} else {
							AddTail(WriteQueue,GotReq);
							Permit();
						}
					} else {
#ifdef DEBUG
						FPuts(DebugFile,"Replying write message\n");
#endif						
						ReplyMsg(GotReq);
					}
						
					
				} else {
					
					Permit();
				}
			}
			while (GotReq=(struct IOExtSer *)GetMsg(CmdPort)) {
				if (GotReq->IOSer.io_Message.mn_Node.ln_Name==Signature) {
					/* We just use the pointer as a signature */
					/* We just got an expunge request */
#ifdef DEBUG
					FPuts(DebugFile,"Got expunge request\n");
#endif

					FreeMem(GotReq,sizeof(struct Message)); /* We free the expunge request instead of reply */
					
					Forbid();
					if (SerUnit->fsru_UserData[3]==0) {
						/* Open count==0 */
						if (SerUnitAdded) {
							IOReq->io_Command=FSRCMD_REMUNIT;
							IOReq->io_Data=(APTR)SerUnit;
							IOReq->io_Length=sizeof(*SerUnit);
							DoIO(IOReq);
							SerUnitAdded=FALSE;
						}
						if (SerUnit->fsru_UserData[3]==0) {
							/* If opencnt is still 0 */
							Permit();
							goto quitfakeser; /* exit */
						}
					}
					Permit();
				}
				else {
#ifdef DEBUG
					FPuts(DebugFile,"Got cmd...");
#endif
					
					switch(GotReq->IOSer.io_Command) {
						case CMD_CLEAR:
#ifdef DEBUG
							FPuts(DebugFile,"Got CMD_CLEAR\n");
#endif
							RBufPos=0;
							GotReq->IOSer.io_Error=0;
							ReplyMsg(GotReq);						
						break;
						
						case CMD_FLUSH:
						case CMD_RESET:
#ifdef DEBUG
							FPuts(DebugFile,"Got CMD_FLUSH/RESET\n");
#endif

							Forbid();
							while (OtrReq=(struct IOExtSer *)RemHead(ReadQueue)) {
								OtrReq->IOSer.io_Error=IOERR_ABORTED;
								ReplyMsg(OtrReq);
							}
							while (OtrReq=(struct IOExtSer *)RemHead(WriteQueue)) {
								OtrReq->IOSer.io_Error=IOERR_ABORTED;
								ReplyMsg(OtrReq);
							}
							Permit();
							GotReq->IOSer.io_Error=0;
							ReplyMsg(GotReq);
						break;
						
						case CMD_READ:
#ifdef DEBUG
							FPuts(DebugFile,"Got CMD_READ\n");
#endif
							
							GotReq->IOSer.io_Actual=0;
							GotReq->IOSer.io_Error=0;
							Amnt=min(RBufPos,GotReq->IOSer.io_Length);
							memcpy(((char *)GotReq->IOSer.io_Data)+GotReq->IOSer.io_Actual,RBuf,Amnt);
							GotReq->IOSer.io_Actual+=Amnt;
							if (Amnt < RBufPos) mymovmem(RBuf+Amnt,RBuf,Amnt);
							RBufPos-=Amnt;
							
							if (GotReq->IOSer.io_Actual < GotReq->IOSer.io_Length) {
								Forbid();
								AddTail(ReadQueue,GotReq);
								Permit();
							}
							else ReplyMsg(GotReq);
						break;
						
						case CMD_START:
#ifdef DEBUG
							FPuts(DebugFile,"Got CMD_START\n");
#endif

							GotReq->IOSer.io_Error=0;
							ReplyMsg(GotReq);						
						break;
						
						case CMD_STOP:
#ifdef DEBUG
							FPuts(DebugFile,"Got CMD_STOP\n");
#endif

							GotReq->IOSer.io_Error=0;
							ReplyMsg(GotReq);						
						break;
						
						case CMD_WRITE:
#ifdef DEBUG
							FPuts(DebugFile,"Got CMD_WRITE\n");
#endif
							
							GotReq->IOSer.io_Actual=0;
							GotReq->IOSer.io_Error=0;
							Forbid();
							AddTail(WriteQueue,GotReq);
							Permit();
						break;
						
						case SDCMD_BREAK:
#ifdef DEBUG
							FPuts(DebugFile,"Got SDCMD_BREAK\n");
#endif

							GotReq->IOSer.io_Error=0;
							ReplyMsg(GotReq);							
						break;
						
						case SDCMD_QUERY:
#ifdef DEBUG
							FPuts(DebugFile,"Got SDCMD_QUERY\n");
#endif

							GotReq->IOSer.io_Actual=RBufPos;
							GotReq->io_Status=0x0000 | ((CD) ? 0:(1<<5));
							GotReq->IOSer.io_Error=0;
							
							ReplyMsg(GotReq);
						break;
						
						case SDCMD_SETPARAMS:
#ifdef DEBUG
							FPuts(DebugFile,"Got SDCMD_SETPARAMS\n");
#endif

							GotReq->IOSer.io_Error=0;
							ReplyMsg(GotReq);							
						break;
						
						default:
#ifdef DEBUG
							FPuts(DebugFile,"Got CMD_UNKNOWN\n");
#endif
							GotReq->IOSer.io_Error=IOERR_NOCMD;
							ReplyMsg(GotReq);
							
					}
				}
			}
		}
		
		
		quitfakeser:
		
		if (ReadQueue) {
			Forbid();
			while (GotReq=(struct IOExtSer *)RemHead(ReadQueue)) {
				GotReq->IOSer.io_Error=IOERR_ABORTED;
				GotReq->IOSer.io_Actual=0;
				ReplyMsg(GotReq);
			}
			Permit();
			FreeMem(ReadQueue,sizeof(struct List));
		}
		if (WriteQueue) {
			Forbid();
			while (GotReq=(struct IOExtSer *)RemHead(WriteQueue)) {
				GotReq->IOSer.io_Error=IOERR_ABORTED;
				GotReq->IOSer.io_Actual=0;
				ReplyMsg(GotReq);
			}
			Permit();
			FreeMem(WriteQueue,sizeof(struct List));
		}
		if (RBuf && SerUnit) {
			FreeMem(RBuf,(long)SerUnit->fsru_UserData[4]);
		}
		if (SerUnit) {
			if (SerUnitAdded && IOReq) {
				IOReq->io_Data=(APTR)SerUnit;
				IOReq->io_Length=sizeof(*SerUnit);
				IOReq->io_Command=FSRCMD_REMUNIT;
				DoIO(IOReq);
			}
			FreeMem(SerUnit,sizeof(struct FSRUnit));
		}
		if (IOReq) {
			if (IOReq->io_Device) CloseDevice(IOReq);
			DeleteIORequest(IOReq);
		}
		if (IOPort) DeleteMsgPort(IOPort);
		if (CmdPort) DeleteMsgPort(CmdPort);
		goto quit;
	}
  		
	quit:
#ifdef DEBUG
	if (DebugFile) Close(DebugFile);
#endif
	if (TimePort) DeleteMsgPort(TimePort);
	if (Info) FreeMem(Info,sizeof(struct PasswdInfo));
	if (BannerFile) Close(BannerFile);
	if (Inst) {
		if (SocketBase) CloseLibrary(SocketBase);
		if (UtilityBase) CloseLibrary(UtilityBase);
		if (DOSBase) CloseLibrary(DOSBase);
	}
	return 0;
}

void MyFPrintf(struct InstData *Inst,BPTR File,char *Fmt,...)
{
	VFPrintf(File,Fmt,(&Fmt)+1);
}

void mystrcat(char *Dest,char *Cat)
{
	memcpy(Dest+strlen(Dest),Cat,strlen(Cat)+1);
}

void mymovmem(char *From,char *To,int Len)
{
	int Cnt;
	if (From > To) {
		for (Cnt=0;Cnt < Len;Cnt++) {
			To[Cnt]=From[Cnt];
		}
	} else if (From < To) {
		for (Cnt=Len-1;Cnt >= 0;Cnt--) {
			To[Cnt]=From[Cnt];
		}		
	}
}

struct Node *FindNode(struct List *List,struct Node *Node)
/* returns Node if Node is a member of list, NULL otherwise */
{
	struct Node *ChkNode;
	
	for (ChkNode=List->lh_Head;ChkNode->ln_Succ;ChkNode=ChkNode->ln_Succ) {
		if (ChkNode==Node) return Node;
	}
	return NULL;
}

ULONG FakeBeginIO(void *IORequest)
{
	((struct IORequest *)IORequest)->io_Flags &= ~IOF_QUICK; /* Quick IO not supported */
	((struct IOExtSer *)IORequest)->io_Status &= ~(1<<0); /* clear abort bit */
	((struct Node *)IORequest)->ln_Name=NULL; /* clear possible signature */
	PutMsg((struct MsgPort *)((struct FSRUnit *)((struct IORequest *)IORequest)->io_Unit)->fsru_UserData[0],IORequest);
	return 0;
}

ULONG FakeAbortIO(void *IORequest)
{
	struct IOExtSer *Ser;
	
	Ser=IORequest;
	Forbid();
		Ser->io_Status |= (1<<0); /* Flag the request as aborted */
		if (FindNode(((struct FSRUnit *)Ser->IOSer.io_Unit)->fsru_UserData[1],Ser) ||
			FindNode(((struct FSRUnit *)Ser->IOSer.io_Unit)->fsru_UserData[2],Ser)) {
				/* This IORequest is waiting to be processed */
				Remove(Ser);
				Ser->IOSer.io_Error=IOERR_ABORTED;
				Ser->IOSer.io_Actual=0;
				ReplyMsg(Ser);
		}
		else {
			/* This IORequest is currently being processed -- send a break */
			Signal(((struct MsgPort *)((struct FSRUnit *)Ser->IOSer.io_Unit)->fsru_UserData[0])->mp_SigTask,SIGBREAKF_CTRL_C);
		}
	Permit();
	
	return 0;
}

ULONG FakeOpen(void *IORequest)
{
	struct IOExtSer *Ser;
	
	Ser=IORequest;
	Forbid();
	((struct FSRUnit *)Ser->IOSer.io_Unit)->fsru_UserData[3]=(void *) ((long)(((struct FSRUnit *)Ser->IOSer.io_Unit)->fsru_UserData[3])+1); /* Increment opencnt */
	Permit();
	Ser->IOSer.io_Error=0;
	Ser->io_CtlChar=SER_DEFAULT_CTLCHAR;
	Ser->io_RBufLen=(long)((struct FSRUnit *)Ser->IOSer.io_Unit)->fsru_UserData[4];
	Ser->io_ExtFlags=0;
	Ser->io_Baud=19200;
	Ser->io_BrkTime=250000;
	Ser->io_TermArray.TermArray0=0;
	Ser->io_TermArray.TermArray1=0;
	Ser->io_ReadLen=8;
	Ser->io_WriteLen=8;
	Ser->io_StopBits=1;
	Ser->io_Status=0;
	return 0;
}

ULONG FakeClose(void *IORequest)
{
	struct IOExtSer *Ser;
	struct Message *Msg;
	
	Ser=IORequest;
	Forbid();
	
	((struct FSRUnit *)Ser->IOSer.io_Unit)->fsru_UserData[3]=(void *) ((long)(((struct FSRUnit *)Ser->IOSer.io_Unit)->fsru_UserData[3])-1); /* Decrement opencnt */
	Msg=AllocMem(sizeof(struct Message),MEMF_PUBLIC|MEMF_CLEAR);
	Msg->mn_Node.ln_Name=Signature;
	PutMsg((struct MsgPort *)((struct FSRUnit *)((struct IORequest *)IORequest)->io_Unit)->fsru_UserData[0],Msg);
	
	Permit();
	return 0;
}


struct DosPacket *PktFromMsg(struct Message *Msg)
{
	return (struct DosPacket *)Msg->mn_Node.ln_Name;
}

void PktFPuts(struct InstData *Inst,BPTR File,char *String)
{
	struct MsgPort *ReplyPort;
	struct DosPacket *Pkt;
	
	ReplyPort=CreateMsgPort();
	Pkt=AllocDosObject(DOS_STDPKT,NULL);
	if (!Pkt || !ReplyPort) return;
	
	Pkt->dp_Type=ACTION_WRITE;
	Pkt->dp_Arg1=((struct FileHandle *)BADDR(File))->fh_Arg1;
	Pkt->dp_Arg2=String;
	Pkt->dp_Arg3=strlen(String);
	Pkt->dp_Port=ReplyPort;
	PutMsg(((struct FileHandle *)BADDR(File))->fh_Type,Pkt->dp_Link);
	WaitPort(ReplyPort);
	GetMsg(ReplyPort);
	DeleteMsgPort(ReplyPort);
	FreeDosObject(DOS_STDPKT,Pkt);
}

unsigned long GetInt(char *String,char **NewPtr)
{
	unsigned long Num=0;
	char *Junk;
	long Cnt;
	long BaseNum;
	
	if (!NewPtr) NewPtr=&Junk;
	
	for (*NewPtr=String;;(*NewPtr)++) {
		
		if (**NewPtr < '0' || **NewPtr > '9') return Num;
		
		/* AAARGH!! multiplication causes a lib call to be generated.
		   We can't use the library. Therefore we have to add */
		/* Num=Num*10; */
		for (BaseNum=Num,Cnt=0;Cnt < 9;Cnt++,Num+=BaseNum);
		
		
		Num+=**NewPtr-'0';
	}
	
}


void SockPuts(struct InstData *Inst,int Sock,char *Buf)
{
	int BufPos=0;
	int BytesWritten;
	
	do {
		BytesWritten=send(Sock,Buf+BufPos,strlen(Buf+BufPos),0);
		if (BytesWritten > 0) BufPos+=BytesWritten;
	} while (Buf[BufPos]);
}

void SockRawWrite(struct InstData *Inst,int Sock,char *Buf,int Len)
{
	int BufPos=0;
	int BytesWritten;
	
	if (!Len) return;
	
	do {
		BytesWritten=send(Sock,Buf+BufPos,Len-BufPos,0);
		if (BytesWritten > 0) BufPos+=BytesWritten;
	} while (Len-BufPos);

}

void SockWrite(struct InstData *Inst,int Sock,char *Buf,int Len,int RawFlag)
{
	int Cnt;
	int BufPos=0;
	char iac2[2];
	char eol[2];
	char csi[2];
	iac2[0]=iac2[1]=IAC;
	eol[0]='\r';
	eol[1]='\n';
	csi[0]='\x1b';
	csi[1]='[';
	
	
	while (Len > BufPos) {
		for (Cnt=BufPos;Cnt < Len;Cnt++) {
			if (!RawFlag) 
				if (Buf[Cnt]=='\r' || Buf[Cnt]=='\n') break;
				if (Buf[Cnt]=='\x9b') break;
			if (Buf[Cnt]==IAC) {
				break;
			}
		}
		SockRawWrite(Inst,Sock,Buf+BufPos,Cnt-BufPos);
		BufPos=Cnt;
		if (BufPos < Len && Buf[BufPos]==IAC) { /* If we got single IAC, send double IAC */
			BufPos++;
			SockRawWrite(Inst,Sock,iac2,2);
		}
		if (!RawFlag) {
			if (BufPos < Len && Buf[BufPos]=='\r') {
				BufPos++;
			}
			if (BufPos < Len && Buf[BufPos]=='\n') {
				BufPos++;
				SockRawWrite(Inst,Sock,eol,2);
			}
			if (BufPos < Len && Buf[BufPos]=='\x9b') {
				BufPos++;
				SockRawWrite(Inst,Sock,csi,2);
			}
		}
	}
}

void ParseBuf3(struct InstData *Inst)
/* Parse 3 character remote request */
{
	
}

void ParseBuf2(struct InstData *Inst)
/* Parse 2 character remote-request */
{
	
}

void ParseBufSubNegotiate(struct InstData *Inst)
{
	
}

int RecvChar(struct InstData *Inst,int Sock,unsigned char *Loc,int RawMode)
{
	int GotLen;
	unsigned char Got,TrueGot;
	/*RawMode=TRUE;*/
	static unsigned char PrevChar;
	
	do {
		
		GotLen=recv(Sock,&Got,1,0); /* Wait for a character */
		if (GotLen==-1) {
			if (Errno()==EINTR) continue;
			else return -1;
		} else if (GotLen==0) return 0;
		
		TrueGot=Got;
		if (!Inst->RecvBufPos && GotLen==1 && Got==0) GotLen=-1; /* Read again */
		if (GotLen==1 && !RawMode && !Inst->RecvBufPos) {
			/* These next few lines ignore incoming newlines and convert
			incoming CRs into newlines   */
			if (Got=='\n' && PrevChar=='\r') GotLen=-1; /* Read again. */
			if (Got=='\r') Got='\n';
		}
	} while (GotLen != 1);
/*	*Loc=Got;
	return 1; */
	
	if (Inst->RecvBufPos) {
		Inst->RecvBuf[Inst->RecvBufPos]=Got;
		Inst->RecvBufPos++;
		switch (Inst->RecvBufPos) {
			case 2:
			  switch(Inst->RecvBuf[1]) {
					case IAC:
					  /* Just a single IAC */
						*Loc=IAC;
						return 1;
					break;
					
					case SB:
						Inst->RecvBufFlags|=RBF_SUBNEGOTIATE;
						return RecvChar(Inst,Sock,Loc,RawMode);
					break;
					
					case SE:
					Inst->RecvBufPos=0;
					Inst->RecvBufFlags &= ~RBF_SUBNEGOTIATE;
					return RecvChar(Inst,Sock,Loc,RawMode);
					
					case WILL:
					case WONT:
					case DO:
					case DONT:
					return RecvChar(Inst,Sock,Loc,RawMode);
					break;
					
					default:
					ParseBuf2(Inst);
					Inst->RecvBufPos=0;
					return RecvChar(Inst,Sock,Loc,RawMode);
				}
			break;
			
			default:
				if (Inst->RecvBufFlags & RBF_SUBNEGOTIATE) {
					if (Inst->RecvBufPos >= 2 && Inst->RecvBuf[Inst->RecvBufPos-2]==IAC && Inst->RecvBuf[Inst->RecvBufPos-1]!=IAC) {
						/* End of subnegotiation */
						Inst->RecvBuf[0]=IAC;
						if ((Inst->RecvBuf[1]=Inst->RecvBuf[Inst->RecvBufPos-1])==SE) {
							Inst->RecvBufFlags &= ~RBF_SUBNEGOTIATE;
							Inst->RecvBufPos=0;
							return RecvChar(Inst,Sock,Loc,RawMode);
						}
						Inst->RecvBufPos=2;
						
						ParseBufSubNegotiate(Inst);
					}
					
					if (Inst->RecvBufPos >= 2 && Inst->RecvBuf[Inst->RecvBufPos-2]==IAC && Inst->RecvBuf[Inst->RecvBufPos-1]==IAC) {
						/* We just got a double IAC in subnegotiation */
						Inst->RecvBuf[Inst->RecvBufPos-2]=-1; /* We store IAC as a (int)-1 */
						Inst->RecvBufPos--;
					}
					return RecvChar(Inst,Sock,Loc,RawMode);
				} else {
					if (Inst->RecvBuf[2]==IAC) {
						/* Restart sequence */
						Inst->RecvBuf[0]=IAC;
						Inst->RecvBufPos=1;
						return RecvChar(Inst,Sock,Loc,RawMode);
					} else {
					  ParseBuf3(Inst);
						Inst->RecvBufPos=0;
						return RecvChar(Inst,Sock,Loc,RawMode);
					}
				}
			break;
		}
	}
	else {
		if (Got==IAC) {
			Inst->RecvBuf[0]=Got;
			Inst->RecvBufPos=1;
			return RecvChar(Inst,Sock,Loc,RawMode);
		} else {
			*Loc=Got;
			PrevChar=TrueGot;
			return 1;
		}
	}
	return 1;
	
}


void SockGets(struct InstData *Inst,int Sock,char *Buf,int BufSize)
/* This function gets the next string until a CR
on the specified socket, and places it in buf. It 
discards leading crs/lfs */
{
	int BufPos=0;
	int BytesRead;
	int FirstBytes=TRUE;
	Buf[0]='\0';
	
	do {
		BytesRead=RecvChar(Inst,Sock,Buf+BufPos,FALSE);
		if (BytesRead < 0) {
			if (Errno()!=EINTR) {
				Buf[BufPos]='\0';
				return;
			}
	  }
		else if (BytesRead==0) {
			Buf[BufPos]='\0';
			return;
		}
		else {
			if ((Buf[BufPos]=='\r' || Buf[BufPos]=='\n') && FirstBytes) {
				Buf[BufPos]='\0';
			} else {
				FirstBytes=FALSE;
				BufPos+=BytesRead;
			}
		}
	} while (Buf[BufPos-1] != '\r' && Buf[BufPos-1] != '\n' && BufPos < (BufSize-1));
	Buf[BufPos]='\0';
}

char *PullPasswdString(char **StringPtr)
{
	int Cnt;
	char *RetVal;
	char *CurPos;
	
	if (!StringPtr || !*StringPtr) return NULL;
	
	RetVal=*StringPtr;
	
	for (CurPos=RetVal;*CurPos != ',' && *CurPos != '\r' && *CurPos != '\n' && *CurPos;CurPos++);
	switch (*CurPos) {
		case ',':
		case '\r':
		case '\n':
			*CurPos='\0';
			CurPos++;
			*StringPtr=CurPos;
		return RetVal;
		
		case '\0':
		*StringPtr=CurPos;
		return RetVal;
	}
}

int mystrcmp(char *S1,char *S2) 
{
	int Cnt;
	for (Cnt=0;S1[Cnt] && S2[Cnt];Cnt++) {
		if (S1[Cnt] != S2[Cnt]) return !0;
	}
	if (S1[Cnt] != S2[Cnt]) return !0;
	return 0;
}

struct PasswdInfo *LookupPasswd(struct InstData *Inst,char *Login)
{
	BPTR File;
	char *LineBuf;
	char *BufPtr;
	char *PasswdPtr;
	char *LoginPtr;
	char *RealNamePtr;
	char *HomeDirPtr;
	char *CmdPtr;
	struct PasswdInfo *Info;
	/* int NoPasswd=FALSE; */
  
	LineBuf=AllocMem(1000,MEMF_PUBLIC);
	if (!LineBuf) return NULL;
	
	File=Open("tnserv:Passwd",MODE_OLDFILE);
	if (!File) return NULL;
	
	
	while (FGets(File,LineBuf,1000)) {
		if (LineBuf[0]=='#') continue;
		BufPtr=LineBuf;
		LoginPtr=PullPasswdString(&BufPtr);
#ifdef DEBUG
		FPuts(DebugFile,"Got username: ");
		FPuts(DebugFile,LoginPtr);
		FPuts(DebugFile,"\n");
#endif
		PasswdPtr=PullPasswdString(&BufPtr);
#ifdef DEBUG
		FPuts(DebugFile,"Got password: ");
		FPuts(DebugFile,PasswdPtr);
		FPuts(DebugFile,"\n");
#endif
		PullPasswdString(&BufPtr); /* throw away user id */
		PullPasswdString(&BufPtr); /* Throw away group id */
		RealNamePtr=PullPasswdString(&BufPtr);
		HomeDirPtr=PullPasswdString(&BufPtr);
		CmdPtr=PullPasswdString(&BufPtr);
		
		/* if (!mystrcmp(PasswdPtr,"*")) NoPasswd=TRUE;*//*strcpy(Passwd,PasswdPtr);*/
    /* else NoPasswd=FALSE; */
		if (!mystrcmp(Login,LoginPtr)) {
#ifdef DEBUG
			FPuts(DebugFile,"Password located\n");
#endif
			/* Password located and correct */
			/*if (CmdPtr[0]=='*') */ /* We only support stdin/out logins */
			Close(File);
			Info=AllocMem(sizeof(struct PasswdInfo),MEMF_PUBLIC);
			if (!Info) return NULL;
			strcpy(Info->Login,LoginPtr);
			strcpy(Info->Passwd,PasswdPtr);
			strcpy(Info->RealName,RealNamePtr);
			strcpy(Info->WorkDir,HomeDirPtr);
			strcpy(Info->Command,CmdPtr);
			FreeMem(LineBuf,1000);
			return Info;
		}
	}
	Close(File);
	FreeMem(LineBuf,1000);
	return NULL; /* Login failed */
}

void __asm PutMLSChar(register __d0 char Chr,register __a3 char **BufPtr)
{
	**BufPtr=Chr;
	(*BufPtr)++;
}

void MakeLongStr(char *StrBuf,unsigned long Num)
{
	RawDoFmt("%lu",&Num,PutMLSChar,&StrBuf);
}


struct Process *MyCreateNewProcTags(struct InstData *Inst,ULONG Tag1,...)
{
	return CreateNewProc((struct TagItem *)&Tag1);
}

void *MyAllocDosObjectTags(struct InstData *Inst,ULONG Type,ULONG Tag1,...)
{
	return AllocDosObject(Type,(struct TagItem *)&Tag1);
}
void MySystemTags(struct Library *DSBse,char *Cmd,ULONG Tag0,...)
{
#define DOSBase DSBse
	SystemTagList(Cmd,(struct TagItem *)&Tag0);
#undef DOSBase
}

#define DOSBase DSBse
int __asm SubSubProc(register __a0 char *ArgString,register __d0 long Length)
{
	char StringBuf[1000];
	char *StringPtr;
	BPTR FH;
	struct Library *DSBse;
	
	DSBse=OpenLibrary("dos.library",36);
	if (!DSBse) return 0;
	
	FH=(BPTR)GetInt(ArgString,&StringPtr);

/*	if (IsInteractive(FH)) {
		Write(FH,"Interactive\n",12);
	}*/
	strcpy(StringBuf,StringPtr);
	if (!StringBuf[0]) {
		MySystemTags(DSBse,"execute s:remote-startup",
			SYS_Input,FH,
			SYS_Output,FH,
			NP_ConsoleTask,((struct FileHandle *)BADDR(FH))->fh_Type,
			TAG_END);
	} else {
		MySystemTags(DSBse,StringBuf,
			SYS_Input,FH,
			SYS_Output,FH,
			NP_ConsoleTask,((struct FileHandle *)BADDR(FH))->fh_Type,
			TAG_END);
	}

	Forbid();
	Close(FH);
	return 0;
}
#undef DOSBase
