-- File: TelnetListenerImpl.mesa - last edit:
-- JAV                  4-May-87 13:34:02

-- Copyright (C) 1985, 1986, 1987 by Xerox Corporation. All rights reserved.

DIRECTORY
  ArpaRouter USING [InternetAddress, Port],
  ArpaTelnetStream USING [Create, Handle],
  Heap USING [Create],
  Process USING [Abort, Detach, Pause, SecondsToTicks],
  TcpStream USING [Failed, Handle, Listen, Suspended],
  TelnetListener;

TelnetListenerImpl: PROGRAM
  IMPORTS Heap, Process, TcpStream, ArpaTelnetStream
  EXPORTS TelnetListener =
  BEGIN
  
  numberOfListeners: CARDINAL ← 0;
  
  ConnectID: PUBLIC TYPE = LONG POINTER TO ListenerRecord;
  
  firstListener: LONG POINTER TO ListenerRecord ← NIL;
  lastListener: LONG POINTER TO ListenerRecord ← NIL;
  ListenerRecord: TYPE = RECORD [
    next: LONG POINTER TO ListenerRecord ← NIL,
    connectProc: TelnetListener.ConnectProc,
    listenerProcess: PROCESS ← NIL];
  
  ListenerZone: UNCOUNTED ZONE ← Heap.Create[1];
    
  Listen: PUBLIC PROCEDURE [connect: TelnetListener.ConnectProc, portNumber: ArpaRouter.Port, suppressLF: BOOLEAN] RETURNS [connectionID: TelnetListener.ConnectID] =
    BEGIN
    listener: LONG POINTER TO ListenerRecord ← ListenerZone.NEW[ListenerRecord ← [NIL, connect, NIL]];
    IF firstListener = NIL THEN firstListener ← listener;
    lastListener ← listener;
    lastListener.listenerProcess ← FORK CreateListener[portNumber, connect, suppressLF];
    numberOfListeners ← numberOfListeners + 1;
    RETURN[lastListener]
    END;
  
  StopListening: PUBLIC PROCEDURE [connectionID: ConnectID] =
    BEGIN
      listener: ConnectID ← firstListener;
      previous: ConnectID ← NIL;
      DO 
        IF connectionID = listener THEN EXIT;
	previous ← listener;
	listener ← listener.next;
      ENDLOOP;
      Process.Abort[listener.listenerProcess];
      JOIN listener.listenerProcess;
      IF listener.next = NIL THEN lastListener ← previous
      ELSE previous.next ← listener.next;
      numberOfListeners ← numberOfListeners - 1;
      ListenerZone.FREE[@listener];
    END;
    
  CreateListener: PROCEDURE [port: ArpaRouter.Port, connect: TelnetListener.ConnectProc, suppressLF: BOOLEAN] =
    BEGIN ENABLE ABORTED => GOTO Exit;
      listenHandle: TcpStream.Handle;
      remoteAddr: ArpaRouter.InternetAddress;
      DO
        listenHandle ← TcpStream.Listen[port ! TcpStream.Failed => LOOP];
	[,remoteAddr , , ] ← listenHandle.findAddresses[];
	Process.Detach[FORK ConnectUser[listenHandle, connect, remoteAddr, suppressLF]];
      ENDLOOP;
    EXITS 
      Exit => RETURN;
    END;
    
  ConnectUser: PROCEDURE [stream: TcpStream.Handle, connect: TelnetListener.ConnectProc, remoteAddr: ArpaRouter.InternetAddress, suppressLF: BOOLEAN] =
    BEGIN
    telnetStream: ArpaTelnetStream.Handle ← NIL;
	telnetStream ← ArpaTelnetStream.Create[stream, [TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, TRUE], suppressLF];
	Process.Pause[Process.SecondsToTicks[2]];
	connect[telnetStream, stream, remoteAddr ! 
	  TcpStream.Suspended => CONTINUE;
	  TcpStream.Failed => CONTINUE;
	  ABORTED => CONTINUE];
	telnetStream.delete[telnetStream];
    END;
    
  END...