-- Copyright (C) 1981, 1984, 1985  by Xerox Corporation. All rights reserved. 
-- MiscSoc.mesa, Transport Mechanism Mail Server - Miscellaneous socket communication --

-- HGM: 18-Sep-85  6:42:09
-- Randy Gobbel		29-May-81 14:44:13 --
-- Andrew Birrell	13-Jan-81 11:15:27 --
-- Mark Johnson		May 28, 1981  2:29 PM --
-- Hankins		 2-Oct-84 11:30:32	

DIRECTORY
  BodyDefs USING [maxRNameLength, RName],
  Buffer USING [AccessHandle, GetBuffer, MakePool, ReturnBuffer],
  Heap USING [systemZone],
  Inline USING [LongCOPY],
  LogDefs USING [WriteLogEntry],
  MailboxDefs USING [Poll],
  PolicyDefs USING [ProdServersPause, CheckOperation],
  Process USING [Detach, SecondsToTicks, SetTimeout],
  ProtocolDefs USING [Init, mailServerPollingSocket],
  PupDefs USING [
    AppendHostName, GetPupContentsBytes, PupBuffer, PupRouterSendThis, PupSocket,
    PupSocketMake, SetPupContentsWords, SwapPupSourceAndDest, veryLongWait],
  PupTypes USING [echoSoc, fillInSocketID, PupAddress],
  RestartDefs USING [],
  ServerDefs USING [
    EnumerateAll, NoSuchServer, ServerAddr, ServerHandle, ServerNotUp, ServerUp,
    UpServer],
  SiteCacheDefs USING [SingleFlush],
  String USING [AppendString];

MiscSoc: MONITOR
  IMPORTS
    Buffer, Heap, Inline, LogDefs, MailboxDefs, PolicyDefs, Process, ProtocolDefs, PupDefs,
    ServerDefs, SiteCacheDefs, String
  EXPORTS RestartDefs --PROGRAM-- =

  BEGIN

  bufferPool: Buffer.AccessHandle;

  poll: PupDefs.PupSocket ← NIL;
  prod: PupDefs.PupSocket ← NIL;
  echo: PupDefs.PupSocket ← NIL;

  UnexpectedTimeout: ERROR = CODE;

  AcceptPoll: PROCEDURE =
    BEGIN
    DO
      b: PupDefs.PupBuffer = poll.get[];
      IF b = NIL THEN ERROR UnexpectedTimeout[];
      SELECT b.pup.pupType FROM
        mailCheckLaurel => PutBuffer[b];
        echoMe =>
          BEGIN
          IF ~PolicyDefs.CheckOperation[serverInput, FALSE] THEN
	    -- Don't answer unless we are accepting forwarded mail to avoid
	    -- false hopes (and thrashing) on other servers.
	    BEGIN Buffer.ReturnBuffer[b]; LOOP; END;
          PupDefs.SwapPupSourceAndDest[b];
          b.pup.pupType ← iAmEcho;
          PupDefs.PupRouterSendThis[b];
          END;
        LOOPHOLE[215B] => PutBuffer[b];
        ENDCASE => Buffer.ReturnBuffer[b];
      ENDLOOP;
    END;

  WorkOnPoll: PROCEDURE =
    BEGIN
    DO
      b: PupDefs.PupBuffer = GetBuffer[];
      SELECT b.pup.pupType FROM
        mailCheckLaurel =>
          BEGIN
          pLength: CARDINAL = PupDefs.GetPupContentsBytes[b];
          IF pLength > BodyDefs.maxRNameLength THEN b.pup.pupType ← mailNotNew
          ELSE
            BEGIN
            client: BodyDefs.RName = [BodyDefs.maxRNameLength];
            Inline.LongCOPY[
              from: @(b.pup.pupString), to: @(client.text),
              nwords: (1 + pLength) / 2];
            client.length ← pLength;
            b.pup.pupType ←
              IF MailboxDefs.Poll[client] THEN mailIsNew ELSE mailNotNew;
            END;
          PupDefs.SwapPupSourceAndDest[b];
          PupDefs.SetPupContentsWords[b, 0];
          PupDefs.PupRouterSendThis[b];
          END;
        LOOPHOLE[215B] => CacheFlushRequest[b];
        ENDCASE => ERROR;
      ENDLOOP;
    END;

  flushing: BOOLEAN ← FALSE;
  buffersFlushed, clumpsFlushed: LONG CARDINAL ← 0;
  theBuffer: PupDefs.PupBuffer ← NIL;
  waitingForBuffer: CONDITION;
  
  PutBuffer: ENTRY PROC [b: PupDefs.PupBuffer] =
    BEGIN
    IF theBuffer # NIL AND ~flushing THEN WAIT waitingForBuffer;
    IF theBuffer # NIL THEN
      BEGIN
      Buffer.ReturnBuffer[b];
      buffersFlushed ← buffersFlushed.SUCC;
      IF ~flushing THEN clumpsFlushed ← clumpsFlushed.SUCC;
      flushing ← TRUE;
      RETURN;
      END;
    theBuffer ← b;
    NOTIFY waitingForBuffer;
    flushing ← FALSE;
    END;

  GetBuffer: ENTRY PROC RETURNS [b: PupDefs.PupBuffer] =
    BEGIN
    WHILE theBuffer = NIL DO WAIT waitingForBuffer; ENDLOOP;
    b ← theBuffer;
    theBuffer ← NIL;
    NOTIFY waitingForBuffer;
    END;

  CacheFlushRequest: PROCEDURE [b: PupDefs.PupBuffer] =
    BEGIN
    name: LONG STRING = LOOPHOLE[@(b.pup.pupWords)];
    IF name.length <= BodyDefs.maxRNameLength THEN
      BEGIN
      log: LONG STRING ← Heap.systemZone.NEW[StringBody[200]];
      SiteCacheDefs.SingleFlush[name];
      String.AppendString[log, "Cache-Flush request for "L];
      String.AppendString[log, name];
      String.AppendString[log, " from "];
      PupDefs.AppendHostName[log, b.pup.source];
      LogDefs.WriteLogEntry[log];
      Heap.systemZone.FREE[@log];
      END;
    Buffer.ReturnBuffer[b];
    END;

  PollForUpServer: PROCEDURE [server: ServerDefs.ServerHandle] =
    BEGIN
    ENABLE
      BEGIN
      ServerDefs.NoSuchServer => GOTO NoSuchServer;
      ServerDefs.ServerNotUp => GOTO ServerNotUp;
      END;
    IF NOT ServerDefs.ServerUp[server] THEN
      BEGIN
      dest: PupTypes.PupAddress ← ServerDefs.ServerAddr[server];
      b: PupDefs.PupBuffer ← Buffer.GetBuffer[
        type: pup, aH: bufferPool, function: send];
      PupDefs.SetPupContentsWords[b, 0];
      b.pup.pupType ← echoMe;
      b.pup.pupID.a ← LOOPHOLE[server];
      b.pup.pupID.b ← LOOPHOLE[server];
      dest.socket ← IF server.leaf THEN PupTypes.echoSoc
        ELSE ProtocolDefs.mailServerPollingSocket;
      prod.setRemoteAddress[dest];
      prod.put[b];
      END;
    EXITS
      NoSuchServer => NULL;
      ServerNotUp => NULL;
    END;

  ProdServers: PROC =
    BEGIN
    DO
      PolicyDefs.ProdServersPause[];
      ServerDefs.EnumerateAll[PollForUpServer];
      ENDLOOP;
    END;

  ProdReply: PROCEDURE =
    BEGIN
    DO
      b: PupDefs.PupBuffer = prod.get[];
      IF b = NIL THEN ERROR UnexpectedTimeout[];
      SELECT b.pup.pupType FROM
        iAmEcho =>
          BEGIN
          maybe: ServerDefs.ServerHandle = LOOPHOLE[b.pup.pupID.a];
          Work: PROCEDURE [really: ServerDefs.ServerHandle] =
            BEGIN
            IF really = maybe THEN ServerDefs.UpServer[really];
            END;
          ServerDefs.EnumerateAll[Work];
          Buffer.ReturnBuffer[b];
          END;
        ENDCASE => Buffer.ReturnBuffer[b];
      ENDLOOP;
    END;

  Echo: PROCEDURE =
    BEGIN
    DO
      b: PupDefs.PupBuffer = echo.get[];
      IF b = NIL THEN ERROR UnexpectedTimeout[];
      SELECT b.pup.pupType FROM
        echoMe =>
          BEGIN
          PupDefs.SwapPupSourceAndDest[b];
          b.pup.pupType ← iAmEcho;
          PupDefs.PupRouterSendThis[b];
          END;
        ENDCASE => Buffer.ReturnBuffer[b];
      ENDLOOP;
    END;

  MiscSoc1: PUBLIC PROC =
    BEGIN
    Process.SetTimeout[@waitingForBuffer, Process.SecondsToTicks[5]];
    ProtocolDefs.Init[];
    END;
    
  MiscSoc2: PUBLIC PROC =
    BEGIN
    bufferPool ← Buffer.MakePool[send: 1, receive: 0];
    poll ← PupDefs.PupSocketMake[ProtocolDefs.mailServerPollingSocket, , PupDefs.veryLongWait];
    prod ← PupDefs.PupSocketMake[PupTypes.fillInSocketID, , PupDefs.veryLongWait];
    echo ← PupDefs.PupSocketMake[PupTypes.echoSoc, , PupDefs.veryLongWait];
    Process.Detach[FORK AcceptPoll[]];
    Process.Detach[FORK WorkOnPoll[]];
    Process.Detach[FORK ProdReply[]];
    Process.Detach[FORK Echo[]];
    Process.Detach[FORK ProdServers[]];
    END;
    
  END.