/*
 *      $Filename: telnetd.c $
 *      $Revision: 2.0 $
 *      $Date: 1995/04/13 19:53:48 $
 *
 *      Copyright (C) 1993,94 by Steve Holland <sdh4@cornell.edu>
 *      Copyright (C) 1995 by Peter Simons <simons@peti.rhein.de>
 *
 *      This program is free software; you can redistribute it and/or
 *      modify it under the terms of the GNU General Public License as
 *      published by the Free Software Foundation; either version 2 of
 *      the License, or (at your option) any later version.
 *
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *      General Public License for more details.
 *
 *      You should have received a copy of the GNU General Public License
 *      along with this program; if not, write to the Free Software
 *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *      $Id: telnetd.c 2.0  1995/04/13 19:53:48 simons Exp $
 *
 *      *********************************************************
 *	*	WARNING WARNING WARNING WARNING WARNING !!!	*
 * 	*********************************************************
 *
 * 	Telnetd source code has been modified by Andrea Rafreider
 *      (andsk@amiga.dei.unipd.it) in order to make it MultiUser compatible.
 *	
 *	This modify of the original source code from Steve Holland and
 *	Peter Simons was made on date 1995/05/04.
 *	
 *	Original version of telnetd2.0 with source can be found in this
 *	same archive.
 *
 *	Watch the file Multiuser.txt to see what is MultiUser, and what can
 *	be done with it.
 *
 *      With *this* modified telnetd, passwd checking is done via
 *	multiuser.library instead of usergroup.library from AmiTCP.
 *
 *      The main advantage is that giving a shell to a remote user is
 *	no longer *totally* insecure because of multiuser protection 
 *	mechanisms.
 *
 * 	Obviously for better security the system manager should protect
 *	all the "hot" commands with root permissions only.
 *	
 *	It's no longer possible to log in without password if the passwd field
 *	in the multiuser passwd file is "*".
 *	Some annoying syslog messages have also been removed.
 *
 *	The password checking is done via multiuser.library so that multiuser
 * 	passwd file can be protected from indiscreet eyes with the following
 * 	protection bits:  root ----rwed group ---- other ----	
 *
 *	I really don't see why AmiTCP group have complicated things not
 *	using ACrypt() criptographic function from Commodore. To set up
 *	a multiuser environement with AmiTCP one should have two passwd 
 *	files !! One for AmiTCP, and another one for MultiUser!!
 *	This is ridiculous.
 *
 *	In our amiga system at University we don't use AmiTCP passwd file
 *	at all ! Clients don't need it and as daemons we use JoranFTPd and
 * 	now this modified telnetd.
 *
 *	I'm considering the idea of creating a fake usergroup.library from
 *	the free usergroup.library source code that makes passwd checks via
 *	ACrypt() function from Commodore, so we could end up with this silly
 * 	problems.
 *
 * 	That's all folks!!
 *					Andrea Rafreider.
 *
 *	Please see also the GNU General Public Licence included in this archive.
 *	to agree with terms and conditions of the distribution of this package. 
 */

/************************************* includes ***********/
#include <exec/types.h>
#include <exec/memory.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/param.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 <proto/usergroup.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 <syslog.h>
#include <amitcp/socketbasetags.h>

/* Next 3 includes are added by Mr. Andsk */

#include <utility/tagitem.h>
#include <libraries/multiuser.h>
#include <proto/multiuser.h>

#include <fakesr.h>

#include "telnetd_rev.h"

/************************************* structures *********/

struct InstData {
        struct Library *DOSB;
        struct Library *SocketB;
        struct Library *UtilityB;
        struct Library *UserGroupB;
        int Socket;
        signed short RecvBuf[1024];
        int RecvBufPos;
        int RecvBufFlags;
};

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

/************************************* defines ************/
#define DOSBase Inst->DOSB
#define UtilityBase Inst->UtilityB
#define SocketBase Inst->SocketB
#define UserGroupBase Inst->UserGroupB
#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

#define RBF_SUBNEGOTIATE (1<<0)         /* Defines for RecvBufFlags */

/************************************* global variables ***/
static const char __DOSVer[] = VERSTAG " written by Peter Simons <simons@peti.rhein.de>";
#ifndef USE_INETD
struct Library *SysBase;
#endif
char *Signature="FAKEMSG--EXPUNGE";

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

#ifdef DEBUG
  BPTR DebugFile;
#endif

#ifdef USE_INETD
int main(void)
#else
int __asm main(register __a0 char *ArgStr,register __d0 long Length)
/* ArgString will look like: "[SocketID] [SigNum] [ParentTaskID]" */
#endif
{
        int Cnt;
	char A_Buf[20];  /* temporary buffer for ACrypt() */
        unsigned long SockID,SigID,TaskID;
        char Buf[1000],LoginBuf[50], MyPrgName[64];
        BPTR BannerFile=NULL;
	struct muBase *muBase = NULL;   	/* added by andsk */
	struct TagItem tags[3];			/* added by andsk */
	struct muUserInfo *muInfo = NULL; 	/* added by andsk */
        struct InstData *Inst=NULL;
        struct PasswdInfo *Info=NULL;
        struct FileHandle *FH;
        struct Message *Msg,*GotMsg;
        unsigned char NoEcho[7];
        unsigned 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 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;
        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;
	
	muBase=OpenLibrary("multiuser.library",39); /* added by andsk */
        DOSBase=OpenLibrary("dos.library",36);
        UtilityBase=OpenLibrary("utility.library",36);
        SocketBase=OpenLibrary("bsdsocket.library",2);
        UserGroupBase=OldOpenLibrary(USERGROUPNAME);

        TimePort=CreateMsgPort();
        NewList(&ReadWaitList);

        if (!DOSBase || !UtilityBase || !SocketBase || !UserGroupBase || !TimePort || !muBase) return 0; /* added check for multiuser.library */

        SocketBaseTags(SBTM_SETVAL(SBTC_LOGTAGPTR), "telnetd",
                 SBTM_SETVAL(SBTC_LOGSTAT), 0,
                 SBTM_SETVAL(SBTC_LOGFACILITY), LOG_AUTH,
                 TAG_END);

#ifdef DEBUG
        DebugFile=Open("con:0/0/500/100/telnetd DEBUG/WAIT/CLOSE",MODE_OLDFILE);
        FPuts(DebugFile,"telnetd starting\n");
#  ifndef USE_INETD
        FPuts(DebugFile,"ArgString=");
        FPuts(DebugFile,ArgString);
        FPuts(DebugFile,"\n");
#  endif

#endif

#ifdef USE_INETD
        Self=(struct Process *) 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 */
        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 */
        if (BannerFile=Open("AmiTCP:db/telnetd_banner",MODE_OLDFILE)) {
                int len = 1;

                while(TRUE) {
                        len = Read(BannerFile, Buf, sizeof(Buf));
                        if (!len || len == -1)
                                break;
                        SockWrite(Inst,Socket,Buf,len,FALSE);
                }
                Close(BannerFile);
        }

#ifdef DEBUG
        FPuts(DebugFile,"Wrote 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);  	      not needed anymore	*/
/*	We get now info for connecting user via multiuser.library	        */

	if (muInfo = muAllocUserInfo()) {
		strcpy(muInfo->UserID,LoginBuf);
		if (!muGetUserInfo(muInfo,muKeyType_UserID)) {
			muFreeUserInfo(muInfo);
			muInfo = NULL;
		}
		else if (Info=AllocMem(sizeof(struct PasswdInfo),MEMF_PUBLIC)) {
			strcpy(Info->Login,muInfo->UserID);
			strcpy(Info->Passwd,"faked_passwd!"); /* not returned by muGetUserInfo, not needed anyway! */
			strcpy(Info->RealName,muInfo->UserName);
			strcpy(Info->WorkDir,muInfo->HomeDir);
			strcpy(Info->Command,muInfo->Shell);
		}
		else
			syslog(LOG_CRIT, "Couldn't allocate my info structure!");
	}
        else
                syslog(LOG_CRIT, "Couldn't allocate memory muInfo structure!");
		
        /*
         * We don't get asked for password if user doesn't exist
         * or the shell is equal "nologin", we are just pulled out.
         * 
	 * 			WARNING !!
	 *
	 * Code is changed a little bit ..
	 * No more you can get logged in if your passwd is "*" for multiuser
	 * compatibility.
         */
        if (Info && strcmp("nologin", Info->Command)) {
                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

	 	SockPuts(Inst,Socket,"\n");

		/* We must be sure that current user is nobody */

		tags[0].ti_Tag = muT_Quiet;
		tags[0].ti_Data = TRUE;
		tags[1].ti_Tag = TAG_DONE;
		(void) muLogoutA(tags);

		/*		MultiUser login		       */

		tags[0].ti_Tag = muT_UserID;
		tags[0].ti_Data = Info->Login;	
		tags[1].ti_Tag = muT_Password;
		tags[1].ti_Data = Buf;
		tags[2].ti_Tag = TAG_DONE;
		if (!muLoginA(tags)) {
			SockPuts(Inst,Socket,"Login failed!\n");
			CloseSocket(Socket);
			goto quit;
		}
	}
	else	{
                SockPuts(Inst,Socket,"\nUser does not exist or has not telnet access to this machine.\nClosing connection\n");
                CloseSocket(Socket);
                goto quit;
        }

	sprintf(MyPrgName, "telnetd <%s>", Info->Login);
	SetProgramName(MyPrgName);

        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,
				    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);
/* 								syslog(LOG_DEBUG, "Sending out \"%s\"",
 * 								       &Pkt->dp_Link+sizeof(struct Message));
 */
                                                                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=(struct Message *)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
        SocketBaseTags(SBTM_SETVAL(SBTC_LOGTAGPTR), NULL, TAG_END);
        if (TimePort) DeleteMsgPort(TimePort);
        if (Info) FreeMem(Info,sizeof(struct PasswdInfo));
	if (muInfo) muFreeUserInfo(muInfo); /* added by andsk */
        if (Inst) {
		if (muBase) CloseLibrary((struct Library *)muBase); /* added by andsk */
                if (UserGroupBase) CloseLibrary(UserGroupBase);
                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=(LONG) 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;
        unsigned 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 = NULL;
        struct passwd *result;

        if (result = getpwnam(Login)) {
                if (Info=AllocMem(sizeof(struct PasswdInfo),MEMF_PUBLIC)) {
                        strcpy(Info->Login,result->pw_name);
                        strcpy(Info->Passwd,result->pw_passwd);
                        strcpy(Info->RealName,result->pw_gecos);
                        strcpy(Info->Command,result->pw_shell);
                }
                else
                        syslog(LOG_CRIT, "Couldn't allocate my info structure!");
        }
        else
		;
/*                syslog(LOG_INFO, "user %s is unknown here", Login); */

        return Info;
}

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,...)
{
#undef DOSBase
#define DOSBase DSBse
        SystemTagList(Cmd,(struct TagItem *)&Tag0);
#undef DOSBase
}

#define DOSBase DSBse
int __asm __saveds 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);
	MySystemTags(DSBse,(StringBuf[0]) ? StringBuf : "execute S:remote-startup",
		     SYS_Input,FH,
		     SYS_Output,FH,
		     NP_ConsoleTask,((struct FileHandle *)BADDR(FH))->fh_Type,
		     NP_Cli,TRUE,
		     TAG_END);

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

