-- File: PupListeners.mesa,  Last Edit: HGM  January 24, 1981  2:23 PM

DIRECTORY
  Process USING [Detach],
  Runtime USING [GlobalFrame],
  Stream USING [Handle],
  PupRouterDefs USING [GetFirstPupSocket, PupRouterSocket],
  PupPktDefs USING [PupPktStreamMake, PupPktStream],
  PupStream USING [
    PupByteStreamMake, PupListener, PupListenerObject, RejectThisRequest],
  PupDefs USING [
    PupAddress, PupBuffer, PupSocketID, Tocks, PupRouterSendThis, SendErrorPup,
    SwapPupSourceAndDest, ReturnFreePupBuffer, PupSocket, PupSocketMake,
    PupSocketKick, PupSocketDestroy, veryLongWait, SetPupContentsBytes,
    AppendStringBodyToPupBuffer],
  PupTypes USING [fillInPupAddress];

PupListeners: MONITOR
  IMPORTS Runtime, Process, PupRouterDefs, PupPktDefs, PupStream, PupDefs
  EXPORTS PupPktDefs, PupStream =
  BEGIN OPEN PupRouterDefs, PupStream, PupDefs;

  -- Manager data
  free: POINTER TO FRAME[PupListeners] ← LOOPHOLE[Runtime.GlobalFrame[Listen]];

  GetInstance: ENTRY PROCEDURE RETURNS [him: POINTER TO FRAME[PupListeners]] =
    BEGIN
    IF free = NIL THEN
      BEGIN
      him ← NEW PupListeners;
      START him; -- Do initialization code
      RETURN;
      END;
    him ← free;
    free ← free.next;
    END;

  FreeInstance: ENTRY PROCEDURE [him: POINTER TO FRAME[PupListeners]] =
    BEGIN him.next ← free; free ← him; END;


  next: POINTER TO FRAME[PupListeners] ← NIL;

  myPupListener: PupListenerObject ← LOOPHOLE[Listen];

  socket: PupSocket;

  timeout: Tocks;
  stop: BOOLEAN;
  proc: PROCESS;
  who: PROCEDURE [UNSPECIFIED, PupAddress];
  check: PROCEDURE [PupAddress];
  Kind: TYPE = {pkt, byte};
  kind: Kind;

  DontReject: PUBLIC PROCEDURE [PupAddress] = BEGIN END;

  CreatePupByteStreamListener: PUBLIC PROCEDURE [
    local: PupSocketID, proc: PROCEDURE [Stream.Handle, PupAddress], ticks: Tocks,
    filter: PROCEDURE [PupAddress]] RETURNS [PupListener] =
    BEGIN RETURN[CreatePupListener[local, proc, ticks, byte, filter]]; END;

  CreatePupPktStreamListener: PUBLIC PROCEDURE [
    local: PupSocketID, proc: PROCEDURE [PupPktDefs.PupPktStream, PupAddress],
    ticks: Tocks, filter: PROCEDURE [PupAddress]] RETURNS [PupListener] =
    BEGIN RETURN[CreatePupListener[local, proc, ticks, pkt, filter]]; END;

  CreatePupListener: PROCEDURE [
    local: PupSocketID, w: PROCEDURE [UNSPECIFIED, PupAddress], ticks: Tocks,
    k: Kind, f: PROCEDURE [PupAddress]] RETURNS [PupListener] =
    BEGIN
    him: POINTER TO FRAME[PupListeners] ← GetInstance[];
    him.socket ← PupSocketMake[local, PupTypes.fillInPupAddress, veryLongWait];
    him.kind ← k;
    him.who ← w;
    him.check ← f;
    him.timeout ← ticks;
    him.stop ← FALSE;
    him.proc ← FORK him.Listen[local];
    RETURN[@him.myPupListener];
    END;

  DestroyPupListener: PUBLIC PROCEDURE [listener: PupListener] =
    BEGIN
    him: POINTER TO FRAME[PupListeners] ← LOOPHOLE[Runtime.GlobalFrame[
      listener↑]];
    him.stop ← TRUE;
    PupSocketKick[him.socket];
    JOIN him.proc;
    PupSocketDestroy[him.socket];
    FreeInstance[him];
    END;

  Listen: PROCEDURE [local: PupSocketID] =
    BEGIN
    soc: PupRouterSocket;
    arg: UNSPECIFIED;
    b: PupBuffer;
    UNTIL stop DO
      b ← socket.get[];
      IF b # NIL THEN
	BEGIN
	SELECT b.pupType FROM
	  rfc =>
	    BEGIN OPEN PupStream;
	    FOR soc ← GetFirstPupSocket[], soc.next UNTIL soc = NIL DO
	      -- check for duplicate
	      IF soc.remote # b.source THEN LOOP;
	      IF soc.id # b.pupID THEN LOOP;
	      b.address ← soc.local;
	      SwapPupSourceAndDest[b];
	      PupRouterSendThis[b];
	      EXIT;
	      ENDLOOP;
	    IF soc = NIL THEN
	      BEGIN -- not a duplicate, make a new connection
	      him: PupAddress ← b.address;
	      check[
		him !
		RejectThisRequest =>
		  BEGIN
		  b.pupType ← abort;
		  SwapPupSourceAndDest[b];
		  SetPupContentsBytes[b, 2];
		  AppendStringBodyToPupBuffer[b, error];
		  PupRouterSendThis[b];
		  GOTO Reject;
		  END];
	      ReturnFreePupBuffer[b];
	      SELECT kind FROM
		pkt =>
		  arg ← PupPktDefs.PupPktStreamMake[
		    local, him, timeout, alreadyOpened, b.pupID];
		byte =>
		  arg ← PupByteStreamMake[
		    local, him, timeout, alreadyOpened, b.pupID];
		ENDCASE => ERROR;
	      Process.Detach[FORK who[arg, him]];
	      END;
	    EXITS Reject => NULL;
	    END;
	  echoMe =>
	    BEGIN
	    b.pupType ← iAmEcho;
	    SwapPupSourceAndDest[b];
	    PupRouterSendThis[b];
	    END;
	  ENDCASE => SendErrorPup[b, LOOPHOLE[100B], "RFC expected"L];
	END;
      ENDLOOP;
    END;


  -- initialization

  END. -- of PupListeners